Java,  算法和数据结构

如何用链表来实现栈

零 说明

我们在前面用数组这种底层数据结构来实现了栈,也用队列来实现了栈。我们学习和掌握了链表之后,就可以考虑用链表来实现栈了。

一实现思路

在我们给链表引入虚拟头结点之后,所有结点元素都拥有了前驱结点,进而,我们可以对链表的第index个位置处的结点进行增删改查操作了。尤其是对于链表头结点的插入和删除操作。

同时,我们也分析出了,对于链表头位置处的增删改查时间复杂度都是O(1)。且,如果我们用链表头充当栈顶位置的话,所有对于栈的操作:入栈push()、出栈()、查看栈顶元素peek(),就可以直接用链表的addFirst()、removeFirst()、getFirst()了,非常的方便。

比较取巧的一个地方在于,对于栈而言,入栈时,元素是一个一个进到栈底的;而链表的在第1个结点前添加元素,跟栈的这种入栈操作完美匹配。非常好玩儿,又有意思。

跟前面通过数组来实现栈一样:先定义栈的接口,然后,用链表来实现它。

二 代码实现

1定义栈的接口
public interface Stack<E>{
  //入栈    
  void push(E e);
  //出栈
  E pop();
  //查看栈顶元素
  E peek();
  //查看栈的大小
  int getSize();
  //栈是否为空
  boolean isEmpty();
}
2 用链表实现栈接口
/**
 * @Author:asher
 * @Date:2021/4/23 13:48
 * @Description:PACKAGE_NAME
 * @Version:1.0
 */
public class LinkedListImplementStack<E> implements Stack<E> {
    //定义成员变量,用我们自己实现的链表,而不是JDK自带的链表工具类
    private LinkedList<E> linkedList;

    //constructor
    public LinkedListImplementStack() {
        linkedList = new LinkedList<>();
    }

    //开始复写Stack接口的方法
    @Override
    public void push(E e) {
        linkedList.addFirst(e);
    }

    @Override
    public E pop() {
        return linkedList.removeFirst();
    }

    @Override
    public E peek() {
        return linkedList.getFirst();
    }

    @Override
    public int getSize() {
        return linkedList.getSize();
    }

    @Override
    public boolean isEmpty() {
        return linkedList.isEmpty();
    }

    //复写Object类的toString()
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Stack top:[");
        stringBuilder.append(linkedList);
        return stringBuilder.toString();
    }

    public static void main(String[] args) {
        LinkedListImplementStack<Integer> linkedListImplementStack = new LinkedListImplementStack<>();
        System.out.println(linkedListImplementStack.getSize());
        System.out.println(linkedListImplementStack.isEmpty());
        for (int i = 0; i < 3; i++) {
            linkedListImplementStack.push(i);
            System.out.println(linkedListImplementStack);
        }
        System.out.println(linkedListImplementStack.peek());
        System.out.println(linkedListImplementStack.pop());
        System.out.println(linkedListImplementStack);
        System.out.println(linkedListImplementStack.getSize());
        System.out.println(linkedListImplementStack.isEmpty());
    }
}
//结果
0
true
Stack top:[LinkedList:size 1
0
Stack top:[LinkedList:size 2
1->0
Stack top:[LinkedList:size 3
2->1->0
2
2
Stack top:[LinkedList:size 2
1->0
2
false

三 对比数组实现的栈和链表实现的栈

/**
 * @Author:asher
 * @Date:2021/4/23 15:11
 * @Description:PACKAGE_NAME
 * @Version:1.0
 */
public class StackTest{
    //传入的参数Stack是1个接口,是数组实现栈和链表实现栈的公共接口;
    public static void test(Stack stack,int count){
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            stack.push(i);
        }
        for (int i = 0; i < count; i++) {
            stack.pop();
        }
        long endTime = System.currentTimeMillis();
        //用反射来获取实现类的名字
        System.out.println(stack.getClass().getName()+" "+count + " elements push() and pop() Time:" + (endTime - beginTime) + " ms.");
    }
​
    public static void main(String[] args) {
        ArrayStack<Integer> arrayStack=new ArrayStack<>();
        LinkedListImplementStack<Integer> linkedListImplementStack = new LinkedListImplementStack<>();
        int count=100000;
        test(arrayStack, count);
        test(linkedListImplementStack, count);
    }
}
​
//结果
ArrayStack:100000 elements push() and pop() Time:23 ms.
LinkedListImplementStack:100000 elements push() and pop() Time:13 ms.
========================================================
ArrayStack:10000000 elements push() and pop() Time:3150 ms.
LinkedListImplementStack:10000000 elements push() and pop() Time:6604 ms.
1 测试结果

数据量100000时,链表实现的栈耗时要小一些。原因可能是数组实现栈的入栈、出栈操作耗时可能在底层数组的动态扩容上;

数据量为10000000时,数组实现栈的耗时小一些。原因可能是链表实现栈每次在链表头添加结点时,都要new Node()的操作,比较耗时。

当然,这个只是一个测试,并没有孰优孰劣的绝对性。

2 对比数组实现栈和链表实现栈

入栈:数组实现栈调用底层数组的addLast();链表实现栈调用底层链表的addFirst();

出栈:数组实现栈调用底层数组的removeLast();链表实现栈调用底层链表的removeFirst();

栈底:数组存放的是第1个元素;链表存放的最后1个元素;

至此,我们用数组实现了栈,也用队列实现了栈,还用链表实现了栈。

留言