栈的Java实现-分别使用数组和链表

栈是非常重要的数据结构,栈具有后进先出的特点。

在JVM内部,每个线程维护一个栈,对于每个方法调用,入栈一个元素,成为栈帧,当方法执行完成后,对应的栈帧出栈。
栈帧中,也包含一个栈,称为操作数栈。

一、定义栈

public interface Stack<Item> {
    // 添加一个元素
    void push(Item item);
    // 删除最近添加的元素
    Item pop();
    // 栈是否为空
    boolean isEmpty();
    // 栈中的元素数量
    int size();
}

二、数组实现

/**
 * 数组实现
 * @param <Item>
 */
public class ResizingArrayStack<Item> implements Stack<Item>, Iterable<Item> {
    private Item[] a;
    // 表示栈实际大小
    int N;

    /**
     * 初始的数组容量为16
     */
    public ResizingArrayStack(){ this(16); }
    public ResizingArrayStack(int cap){ a = (Item[]) new Object[cap]; }
    @Override
    public void push(Item item) {
        if(N == a.length) resize(2 * N);
        a[N++] = item;
    }

    /**
     * 扩容,每次扩2倍的空间
     * @param size
     */
    private void resize(int size) {
        if(size <= 16){
            return;
        }
        System.out.println("触发扩容,原容量: " + a.length + ", 扩容后:" + size);
        Item[] temp = (Item[]) new Object[size];
        for (int i = 0; i < N; i++) {
            temp[i] = a[i];
        }
        a = temp;
    }
    @Override
    public Item pop() {
        if(N == a.length / 4) resize(a.length / 2);
        return a[--N];
    }
    @Override
    public boolean isEmpty() { return N == 0; }
    @Override
    public int size() { return N; }
    //    *********** 以下代码与算法实现无关,仅为方便测试使用 *************
    @Override
    public Iterator<Item> iterator() {
        return new Iterator<Item>() {
            private int i = N;
            @Override
            public boolean hasNext() { return i>0; }

            @Override
            public Item next() { return a[--i]; }

            @Override
            public void remove() { }

        };
    }

    public void print(){
        System.out.print("当前元素(自栈顶至栈底):\t");
        Iterator<Item> iterator = iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next() + "\t");
        }

        System.out.println();
    }
}

三、链表实现

/**
 * 链表实现
 * @param <Item>
 */
public class LinkStack<Item> implements Stack<Item>, Iterable<Item> {
    private Node<Item> top;
    private int N;

    private class Node<Item>{ Item item;Node next;}

    /**
     * 入栈
     * @param item
     */
    @Override
    public void push(Item item) {
        Node node = new Node();
        node.item = item;
        node.next = top;
        top = node;
        N++;
    }
    /**
     * 出栈
     * @return
     */
    @Override
    public Item pop() {
        Item item = top.item;
        top = top.next;
        N--;
        return item;
    }
    @Override
    public boolean isEmpty() { return N==0; }
    @Override
    public int size() { return N; }
    
    //*********** 以下代码与算法实现无关,仅为方便测试使用 *************
    @Override
    public Iterator<Item> iterator() {
        return new Iterator<Item>() {
            Node<Item> temp = top;
            @Override
            public boolean hasNext() {
                return temp != null;
            }
            @Override
            public Item next() {
                Item item = temp.item;
                temp = temp.next;
                return item;
            }
        };
    }
    public void print(){
        System.out.print("当前元素(自栈顶至栈底):\t");
        Iterator<Item> iterator = iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next() + "\t");
        }
        System.out.println();
    }
}

四、测试结果

public class StackTest {
    @Test
    public void arrayStackTest(){
    //  ResizingArrayStack<String> stack = new ResizingArrayStack<>();
        LinkStack<String> stack = new LinkStack<>();
        System.out.print("初始化后, ");
        stack.print();
        String pop;
        //压入元素to
        System.out.print("入栈:to,");
        stack.push("to");
        stack.print();
        //压入元素be
        System.out.print("入栈:be,");
        stack.push("be");
        stack.print();
        //压入元素or
        System.out.print("入栈:or,");
        stack.push("or");
        stack.print();
        //压入元素not
        System.out.print("入栈:not,");
        stack.push("not");
        stack.print();
        //压入元素to
        System.out.print("入栈:to,");
        stack.push("to");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //压入元素be
        System.out.print("入栈:be,");
        stack.push("be");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //压入元素that
        System.out.print("入栈:that,");
        stack.push("that");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //弹出元素
        pop = stack.pop();
        System.out.print("出栈:" + pop + ",");
        stack.print();
        //压入元素is
        System.out.print("入栈:is,");
        stack.push("is");
        stack.print();
    }

    @Test
    public void test2(){
        ResizingArrayStack<Integer> stack = new ResizingArrayStack<>();

        for (int i = 0; i < 100; i++) {
            stack.push(i);
        }
    }
}

测试结果如下:
test1

test2

五、多说一点

在网上搜索数组和链表的区别时,最常见的说法是:数组访问较快,插入操作较慢;链表访问操作慢,修改便捷。

在实现栈时,由于栈仅涉及在最后插入一个值、在最后删除一个值,所以在这里比较2种实现方式优劣时,不能按照上述方式描述。

个人认为,数组实现,劣势在于需要扩容操作;链表实现,需要额外维护一个指向next的链接,占用更多空间;总体来说,2种实现方式优劣不明显,均比较合适。

posted @ 2020-09-03 20:22  形形色色?  阅读(184)  评论(0)    收藏  举报