数据结构之栈与队列

一、栈的定义

栈是这样一种数据结构,它只允许在有序的线性数据集合的一端进行加入数据和删除数据。也就是说,对于这种数据结构的操作只允许在同一个地方进行,这样栈就具有了它特有的特点,在其中的数据总是保持先进后出的特性。下面以图示展示:
数据结构之栈与队列_第1张图片
好了,我们已经知道对于栈这种数据结构,无论是添加元素还是删除元素,都是在栈顶进行的,下面我们定义出其上的API

public interface Stack {

    void push(E e); //入栈
    E pop();        //出栈
    E peek();       //查询栈顶元素
    int getSize();     //栈中元素个数
    boolean isEmpty();  //判空
    int search(Object e); //查询元素e离栈顶的位置
}

对于栈的存储结构,既可以选择数组这种线性结构,也可以选择链表那种链式结构,这里我们选择之前提到的顺序表作为其存储结构。所以,我们还可以发现,复杂的数据结构其实都是通过对之前简单数据结构的包装以实现不同的特性。
完整的栈代码实现如下:

public class StackImpl implements Stack {

    private E[] data;
    private int size;

    public StackImpl() {
        this(64);  //默认栈容量为64
    }

    public StackImpl(int capacity) {
        data = (E[])new Object[capacity];
        size = 0;
    }

    @Override
    public void push(E e) {
        if(size == data.length)
            throw new IllegalStateException("Stack is full.");
        data[size++] = e;
    }

    @Override
    public E pop() {
        if(size == 0){
            throw new IllegalStateException("Stack is empty.");
        }
        E temp = data[size-1];
        data[size-1] = null;
        size--;
        return temp;
    }

    @Override
    public E peek() {
        return data[size-1];
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public int search(Object e) {
        for(int i=size-1; i>=0; i--){
            if(e.equals(data[i])) { //此处需注意传入的元素需重写equals方法
                return size-1-i;
            }
        }
        return -1;  //未找到
    }

    public static void main(String[] args) {
        StackImpl stack = new StackImpl<>();
        for(int i=0; i<10; i++) {
            stack.push(i);
        }
        for(int i=stack.getSize(); i>0; i--){
            System.out.print(stack.pop() + " ");
        }
    }
}

二、栈的应用

栈这种数据结构不可能是无缘无故被设想出来的,它的出现肯定是解决了什么问题对吧。我们想想递归这个概念,函数自身通过直接或间接的方式调用自己就是递归的过程,因此,当程序运行到递归基时它需要返回,而栈这种数据结构刚好可以记录函数运行时的轨迹,然后倒退回去。所以,现在的程序基本都是把函数包装成一个栈帧,然后函数调用和结束就是分别对应了入栈和出栈的过程。
除此之外,栈还可以转换表达式,它可以把我们熟悉的中序表达式转为后序表达式。举个例子:
中序表达式:1+2*3 转为后序表达式就是:123*+,这就是我们的编辑器解决计算的方式。具体实现小伙伴们可以参考网上其他资源。

三、队列的定义

队列也是一种线性数据结构,它只允许在一端添加数据,在另一端删除数据。这样子就维持了其先进先出的特性。图示如下:
数据结构之栈与队列_第2张图片
举个例子:元素以:5 8 7 1 2入队,那么其出队顺序是5 8 7 1 2。看到没,这就是先进先出,很好理解了。
API如下:

public interface List {

    boolean isEmpty();
    int size();
    void enQueue(E e); //入队
    E deQueue();  //出队
    E peek(); //获得队首元素

}

由于我们的栈选用了数组作为其存储结构,队列这里就选择链表作为其存储结构了。其代码实现如下:

public class LinkedList implements List {

    private class Node {
        E data;
        Node next;

        public Node() {
            this(null,null);
        }

        public Node(E data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

    private Node head; //链表头结点
    private Node rail; //链表尾结点
    private int size;

    public LinkedList() {
        head = new Node();
        rail = head;
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public int size() {
        return size;
    }

    //入队时元素加到队尾
    @Override
    public void enQueue(E e) {
        rail.next = new Node(e,null);
        rail = rail.next;
        size++;
    }

    //出队时删除队首元素
    @Override
    public E deQueue() {
        if(size == 0){
            throw new IllegalStateException("List is empty.");
        }
        E temp = head.next.data;

        head.next = head.next.next;
        size --;
        return temp;
    }

    //获得队首元素
    @Override
    public E peek() {
        if(size == 0){
            throw new IllegalStateException("List is empty.");
        }
        return head.next.data;
    }

    /**
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node cur = head.next;
        sb.append("head ");
        while(cur != null){
            sb.append(cur.data+" ");
            cur = cur.next;
        }
        sb.append("rail");
        return sb.toString();
    }


    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList<>();
        for(int i=0; i<5; i++){
            linkedList.enQueue(i);
        }
        System.out.println(linkedList);
        linkedList.deQueue();
        System.out.println(linkedList);
    }
}

四、队列的应用

队列在计算机领域应用也是挺广的,如著名的生产者消费者模型、消息队列等,都是其具体的应用场景,这里就不赘述了,感兴趣的小伙伴可以Google。

你可能感兴趣的:(数据结构)