后进者先出,先进者后出,这就是典型的“栈”结构。
从栈的操作特性上来看,栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。
当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,我们就应该首选“栈”这种数据结构。
实际上,栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈。
- // 基于数组实现的顺序栈
- public class ArrayStack {
- private String[] items; // 数组
- private int count; // 栈中元素个数
- private int n; //栈的大小
-
- // 初始化数组,申请一个大小为n的数组空间
- public ArrayStack(int n) {
- this.items = new String[n];
- this.n = n;
- this.count = 0;
- }
-
- // 入栈操作
- public boolean push(String item) {
- // 数组空间不够了,直接返回false,入栈失败。
- if (count == n) return false;
- // 将item放到下标为count的位置,并且count加一
- items[count] = item;
- ++count;
- return true;
- }
-
- // 出栈操作
- public String pop() {
- // 栈为空,则直接返回null
- if (count == 0) return null;
- // 返回下标为count-1的数组元素,并且栈中元素个数count减一
- String tmp = items[count-1];
- --count;
- return tmp;
- }
- }
如何实现浏览器的前进、后退功能?其实,用两个栈就可以非常完美地解决这个问题。
我们使用两个栈,X和Y,我们把首次浏览的页面依次压入栈X,当点击后退按钮时,再依次从栈X中出栈,并将出栈的数据依次放入栈Y。当我们点击前进按钮时,我们依次从栈Y中取出数据,放入栈X中。当栈X中没有数据时,那就说明没有页面可以继续后退浏览了。当栈Y中没有数据,那就说明没有页面可以点击前进按钮浏览了。
比如你顺序查看了a,b,c三个页面,我们就依次把a,b,c压入栈,这个时候,两个栈的数据就是这个样子:
当你通过浏览器的后退按钮,从页面c后退到页面a之后,我们就依次把c和b从栈X中弹出,并且依次放入到栈Y。这个时候,两个栈的数据就是这个样子:
这个时候你又想看页面b,于是你又点击前进按钮回到b页面,我们就把b再从栈Y中出栈,放入栈X中。此时两个栈的数据是这个样子:
这个时候,你通过页面b又跳转到新的页面d了,页面c就无法再通过前进、后退按钮重复查看了,所以需要清空栈Y。此时两个栈的数据这个样子:
后进先出是它最大的特点。栈既可以通过数组实现,也可以通过链表来实现。不管基于数组还是链表,入栈、出栈的时间复杂度都为O(1)。