文章目录

  • 一、概念
  • 二、实现
  • (一)栈的模拟
  • (二)队列的模拟
  • (1)用数组模拟队列
  • (2)用链表模拟队列
  • 三、实际应用场景
  • 四、参考


一、概念

栈和队列是经过限制(或者说特殊的)的线性结构,我们经常将两个放在一起来比较学习。两者分别有以下特点

  • 栈:先进后出
  • 队列:先进先出

栈就好比你从一摞书的下面找一本书,先得把上面得取下来;队列像排队,前面得人先得到服务。

二、实现

(一)栈的模拟

栈是一种后进先出的数据结构,对于Stack 我们希望至少要对外提供以下几个方法:

Stack<T>()

创建一个空的栈

void push(T s)

往栈中添加一个新的元素

T Pop()

移除并返回最近添加的元素

boolean IsEmpty()

栈是否为空

int Size()

栈中元素的个数

/**
 * 此类用于模拟栈
 * 三种方法:
 * 1,入栈 push()
 * 2,出栈 pop()
 * 3,取栈顶元素 peek()
 */
public class Mystack {
    // 这里以 int 型元素为例,不考虑扩容
    private int[] array = new int[100];
    private int size = 0;// 有效元素个数

    // 入栈
    public void push(int val) {
        array[size++] = val;
    }

    // 出栈
    public int pop() {
        int result = array[size - 1];
        size--;
        return result;
    }

    // 取栈顶元素
    public int peek() {
        return array[size - 1];
    }
    public static void main(String[] args) {
        Mystack mystack = new Mystack();
        mystack.push(1);
        mystack.push(2);
        mystack.push(3);
        mystack.push(4);
        System.out.println(mystack.pop());
        System.out.println(mystack.pop());
        System.out.println(mystack.peek());
    }
}

(二)队列的模拟

和Stack一样,他也有链表和数组两种实现,理解了Stack的实现后,Queue的实现就比较简单了。

Queue<T>()

创建一个空的队列

void push(T t)

往队列中添加一个新的元素

T poll()

将队列中的第一个元素删除且返回

T peek()

将队列中的第一个元素返回

boolean IsEmpty()

队列是否为空

int Size()

队列中元素个数

(1)用数组模拟队列

/**
 * 使用数组来模拟队列
 * 队列的三种方法:
 * (1)入队列push()
 * (2)出队列poll()
 * (3)取队首元素peek()
 */
public class MyQueueByArray {
    private int[] array = new int[100];
    // [head,tail)之间的为有效元素
    private int head = 0;
    private int tail = 0;
    // 由于head等于tail时有两种情况,
    // 空或者满,因此可以使用size来判断
    private int size = 0;

    // 入队操作,我们让入队在队尾
    public void offer(int val) {
        // 先排除特殊情况,需要保证操作不能越界
        if (size == array.length) {
            // 满队列,无法插入
            return;
        }
        array[tail] = val;
        tail++;
        // 如果tail++之后超出了数组有效范围,就从头开始
        if (tail >= array.length) {
            tail = 0;
        }
        size++;
    }
    // 出队操作,我们让对手元素出队
    public Integer poll(){
        // 先排除特殊情况,不能是空队列
        if (size == 0){
            return null;
        }
        Integer ret = array[head];
        head++;
        if (head >= array.length){
            head = 0;
        }
        size--;
        return ret;
    }

    public Integer peek(){
        if (size == 0){
            return null;
        }
        return array[head];
    }

    public static void main(String[] args) {
        MyQueueByArray myQueueByArray = new MyQueueByArray();
        myQueueByArray.offer(1);
        myQueueByArray.offer(2);
        myQueueByArray.offer(3);
        myQueueByArray.offer(4);
        System.out.println(myQueueByArray.poll());
        System.out.println(myQueueByArray.peek());
        System.out.println(myQueueByArray.poll());
    }
}

(2)用链表模拟队列

public class MyQueueByLinkedList {
    static class Node{
        private int val;
        private Node next;

        public Node(int val) {
            this.val = val;
        }
    }

    // 创建一个链表就得有头节点。此处 head 节点不是傀儡节点
    private Node head = null;
    private Node tail = null;

    // 入队列(尾部插入)
    public void offer(int val){
        Node newNode = new Node(val);
        // 若队列为空
        if (head == null){
            head = newNode;
            tail = newNode;
            return;
        }
        // 队列非空
        tail.next = newNode;
        tail = tail.next;
    }

    // 出队列(队首出)
    public Integer poll(){
        // 若当前为空队列
        if (head == null){
            return null;
        }
        // 不是空队列
        int ret = head.val;
        head = head.next;
        if (head == null){
            // 删除当前元素后,队列为空
            tail = null;
        }
        return ret;
    }
    public Integer peek(){
        if (head == null){
            return null;
        }
        return head.val;
    }
}

三、实际应用场景

(一)、栈

  1. 用于符号匹配

在编译器的语法检查中,一个过程就是检查各种括号是否匹配,比如 ([]) ,这就是匹配的,而 {[}] 就不匹配了。可以用堆栈来实现括号匹配。

  1. 用于计算按代数式(也可以用二叉树来解决)
  2. 构造表达式(也可以用二叉树来解决)
  3. 用于函数调用
    (二)、队列
    当多个任务分配给打印机时,为了防止冲突,创建一个队列,把任务入队,按先入先出的原则处理任务。
    当多个用户要访问远程服务端的文件时,也用到队列,满足先来先服务的原则。
    ……
    有一个数学的分支,叫队列理论(queuing theory )。用来计算 预测用户在队中的等待时间,队的长度等等问题。
    答案取决于用户到达队列的频率,用户的任务的处理时间。如果比较简单的情况,可以单单用分析解决。一个简单的例子就是,一部电话,一个接线员。当一个电话打进来,如果接线员很忙,就电话放到等待线路后。接线员从队头开始应答。

如果有k个接线员,问题就变的复杂了。通常难以分析解决的问题就用模拟来解决。 可以用一个队列来模拟这个问题。如果k的值很大,
我们也可能需要其他数据结构来模拟问题。通过模拟,可以找到最小的 k 值,使队列满足一个合理的等待时间。

四、参考

  • 堆栈与队列的实际应用