一、引言

栈是一种后进先出(Last in First out LIFO)的线性数据结构,在此简单用动态数组实现一个栈结构。栈结构虽简单,但却非常有用,应用诸如编辑器的撤销操作、程序的方法调用栈等。

二、实现

1、基于动态数组实现栈

  • 动态数组类

/**
 * 动态数组,数组二次封装
 */
public class Array<E> {

    /**
     * 基于Java原生数组,保存数据的容器
     */
    private E[] data;

    /**
     * 当前元素个数
     */
    private int size;

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

    /**
     * 默认数组容量capacity=10
     */
    public Array() {
        this(10);
    }

    /**
     * 获取数组中元素个数
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 获取数组的容量
     * @return
     */
    public int getCapacity() {
        return data.length;
    }

    /**
     * 判断数组是否为空
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 在所有元素后面添加新元素
     * @param e 元素
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 在所有元素前面添加新元素
     * @param e 元素
     */
    public void addFirst(E e) {
        add(0, e);
    }

    /**
     * 向index索引位置插入一个新元素e
     * @param index 数组索引位置
     * @param e 元素
     */
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("addList failed. index < 0 || index > size");
        }

        //空间不足,扩容
        if (size == data.length) {
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }

    /**
     * 根据元素索引获取数组元素
     * @param index 索引
     * @return
     */
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get failed. index is illegal");
        }
        return data[index];
    }

    /**
     * 获取数组最后一个元素
     * @return
     */
    public E getLast() {
        return get(size - 1);
    }

    /**
     * 获取数组第一个元素
     * @return
     */
    public E getFirst() {
        return get(0);
    }

    /**
     * 根据元素索引修改数组元素
     * @param index 索引
     * @param e 元素
     * @return
     */
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get failed. index is illegal");
        }
        data[index] = e;
    }

    /**
     * 判断包含元素
     * @param e 元素
     * @return
     */
    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找元素索引
     * @param e 元素
     * @return 返回元素索引,如果不存在则返回-1
     */
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 移除指定索引的元素
     * @param index 索引
     * @return 返回被移除的元素
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get failed. index is illegal");
        }
        E ret = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;
        data[size] = null;

        //空间利用率低,数组缩容,防止复杂度震荡
        if (size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }

        return ret;
    }

    /**
     * 移除第一个元素
     * @return 返回被移除元素
     */
    public E removeFirst() {
        return remove(0);
    }

    /**
     * 移除最后一个元素
     * @return 返回被移除元素
     */
    public E removeLast() {
        return remove(size - 1);
    }

    /**
     * 移除数组中一个元素
     * @param e 元素
     */
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    /**
     * 数组容器扩容、缩容
     * @param newCapacity 新的容量
     */
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("Array: size = %d, capacity = %d\n", size, data.length));
        res.append("[");
        for (int i = 0; i < size; i++) {
            res.append(data[i]);
            if (i != size - 1) {
                res.append(", ");
            }
        }
        res.append("]");
        return res.toString();
    }

}
  • 栈类接口
public interface Stack<E> {

    /**
     * 获取栈中元素个数
     * @return
     */
    int getSize();

    /**
     * 判断栈中是否没有元素
     * @return
     */
    boolean isEmpty();

    /**
     * 入栈
     * @param e 元素
     */
    void push(E e);

    /**
     * 出栈
     * @return 弹出元素
     */
    E pop();

    /**
     * 查看栈顶元素
     * @return
     */
    E peek();

}
  • 栈类实现
/**
 * 基于动态数组实现栈
 * @param <E>
 */
public class ArrayStack<E> implements Stack<E> {

    private Array<E> array;

    public ArrayStack(int capacity) {
        array = new Array<>(capacity);
    }

    public ArrayStack() {
        array = new Array<>();
    }

    /**
     * 获取栈中元素个数
     * @return
     */
    @Override
    public int getSize() {
        return array.getSize();
    }

    /**
     * 判断栈中是否没有元素
     * @return
     */
    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }

    /**
     * 获取容量
     * @return
     */
    public int getCapacity() {
        return array.getCapacity();
    }

    /**
     * 入栈
     * @param e 元素
     */
    @Override
    public void push(E e) {
        array.addLast(e);
    }

    /**
     * 出栈
     * @return 弹出元素
     */
    @Override
    public E pop() {
        return array.removeLast();
    }

    /**
     * 查看栈顶元素
     * @return
     */
    @Override
    public E peek() {
        return array.getLast();
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Stack: ");
        res.append("[");
        for (int i = 0; i < array.getSize(); i++) {
            res.append(array.get(i));
            if (i != array.getSize() - 1) {
                res.append(", ");
            }
        }
        res.append("] top");
        return res.toString();
    }
}

2、基于链表实现栈

  • 链表实现
/**
 * 链表实现
 * @param <E>
 */
public class LinkedList<E> {
    /**
     * 虚拟头节点
     */
    private Node dummyHead;

    /**
     * 链表中元素个数
     */
    private int size;

    public LinkedList() {
        dummyHead = new Node(null,null);
        size = 0;
    }

    /**
     * 获取链表中元素个数
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 判断链表是否为空
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 在链表的index位置添加新的元素
     * 在链表中不是一个常用的操作,练习用
     * @param index 索引位置
     * @param e 元素
     */
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add failed. illegal index.");
        }

        //找到插入位置的前一个节点
        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        //插入元素
        prev.next = new Node(e, prev.next);
        size++;
    }

    /**
     * 在链表头添加元素
     * @param e 元素
     */
    public void addFirst(E e) {
        add(0, e);
    }

    /**
     * 在链表末尾添加新的元素
     * @param e 元素
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 获取链表指定位置的元素
     * 非常用操作,练习用
     * @param index 指定位置
     * @return 元素
     */
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get failed, illegal index.");
        }

        Node cur = dummyHead.next;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.e;
    }

    /**
     * 获取链表第一个元素
     * @return
     */
    public E getFirst() {
        return get(0);
    }

    /**
     * 获取链表最后一个元素
     * @return
     */
    public E getLast() {
        return get(size - 1);
    }

    /**
     * 更新链表指定位置的元素
     * 非常用操作,练习用
     * @param index 指定位置
     * @return 元素
     */
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("set failed, illegal index.");
        }

        Node cur = dummyHead.next;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }

        cur.e = e;
    }

    /**
     * 查找链表中是否存在元素e
     * @param e 元素
     * @return
     */
    public boolean contains(E e) {
        Node cur = dummyHead.next;
        while (cur != null) {
            if (cur.e.equals(e)) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    /**
     * 删除链表指定位置的元素
     * 非常用操作,练习用
     * @param index 指定位置
     * @return 元素
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove failed, illegal index.");
        }
        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        Node removeNode = prev.next;
        prev.next = removeNode.next;
        removeNode.next = null;
        size--;
        return removeNode.e;
    }

    /**
     * 移除头部元素
     * @return
     */
    public E removeFirst() {
        return remove(0);
    }

    public E removeLast() {
        return remove(size - 1);
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        Node cur = dummyHead.next;
        while (cur != null) {
            res.append(cur + "->");
            cur = cur.next;
        }
        res.append("NULL");
        return res.toString();
    }

    /**
     * 节点
     */
    private class Node {
        public E e;
        public Node next;

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

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

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

        @Override
        public String toString() {
            return e.toString();
        }
    }


}
  • 链表栈实现
/**
 * 基于链表实现栈
 * @param <E>
 */
public class LinkedListStack<E> implements Stack<E> {

    /**
     * 链表容器
     */
    private LinkedList<E> list;

    public LinkedListStack() {
        list = new LinkedList<>();
    }

    /**
     * 获取栈中元素个数
     * @return
     */
    @Override
    public int getSize() {
        return list.getSize();
    }

    /**
     * 判断栈中是否有元素
     * @return
     */
    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    /**
     * 入栈操作
     * @param e 元素
     */
    @Override
    public void push(E e) {
        list.addFirst(e);
    }

    /**
     * 出栈操作
     * @return
     */
    @Override
    public E pop() {
        return list.removeFirst();
    }

    /**
     * 查看栈顶元素
     * @return
     */
    @Override
    public E peek() {
        return list.getFirst();
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("stack: top ");
        res.append(list);
        return res.toString();
    }
}

三、性能测试

  • 简单本地测试
import java.util.Random;

public class Main {

    public static void main(String[] args) {
        int opCount = 10000000;

        LinkedListStack<Integer> linkedListStack = new LinkedListStack<>();
        double linkedListStackTime = testStack(linkedListStack, opCount);
        System.out.println("LinkedListStack, time:" + linkedListStackTime + "s");//LinkedListStack, time:8.1041194s

        ArrayStack<Integer> arrayStack = new ArrayStack<>();
        double arrayStackTime = testStack(arrayStack, opCount);
        System.out.println("ArrayStack, time:" + arrayStackTime + "s");//ArrayStack, time:0.5274266s
    }

    /**
     * 测试使用栈运行入栈、出栈操作所需的时间
     * @param stack 待测试栈
     * @param opCount 操作次数
     * @return
     */
    private static double testStack(Stack<Integer> stack, int opCount) {
        long statTime = System.nanoTime();

        Random random = new Random();
        for (int i = 0; i < opCount; i++) {
            stack.push(random.nextInt(Integer.MAX_VALUE));
        }

        for (int i = 0; i < opCount; i++) {
            stack.pop();
        }

        long endTime = System.nanoTime();

        return (endTime - statTime) / 1000000000.0;
    }
}
  • 说明:无论基于动态数组还是链表实现的栈,入栈、出栈的时间复杂度在同一个级别O(1)。二者的不同的在于,基于数组实现的栈会出现数组的扩缩容,这是它的缺点;而基于链表实现的栈,入栈、出栈操作会在创建更多的对象。以上不太严谨的测试可以看出,二者各执行10000000万次操作时,基于数组的实现性能要好与基于链表的实现。

四、栈的应用

1、括号匹配

public class Solution {

    /**
     * 括号匹配检验
     * @param s 包含括号的字符串,例如:{(){}[]}
     * @return
     */
    public boolean isValid(String s) {
        ArrayStack<Character> stack = new ArrayStack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            //左括号入栈
            if (c == '(' || c == '[' || c == '{') {
                stack.push(c);
            } else if (c == ')' || c == ']' || c == '}'){
                if (stack.isEmpty()) {
                    return false;
                }
                //取栈顶左括号比较
                char topChar = stack.pop();
                if (topChar == '(' && c != ')') {
                    return false;
                }
                if (topChar == '[' && c != ']') {
                    return false;
                }
                if (topChar == '{' && c != '}') {
                    return false;
                }
            }
        }
        //最终栈必须为空,否则括号匹配失败
        return stack.isEmpty();
    }
}

五、复杂度分析

1、基于数组实现的复杂度分析

  • push(e) => O(1) : 虽然push操作有可能触发动态数组的resize扩容操作,但是均摊下来,很接近O(1)。
  • pop() => O(1) : 同样pop操作有可能触发动态数组的resize缩容操作,但是均摊下来,很接近O(1)。
  • peeK() => O(1) : 查看栈顶元素与栈中元素个数无关
  • getSize() => O(1) : 获取栈中元素个数实际上获取的是动态数组的游标,时间复杂度同样与栈中元素个数无关。
  • isEmpty() => O(1) : 同getSize

六、其它数据结构


neojayway
52 声望10 粉丝

学无止境,每天进步一点点