考虑下用栈如何实现浏览器的前进和后退功能
如何理解栈
栈的操作受限,只允许在一端进行插入和删除,后进先出;
如何实现一个栈?
栈主要是包含入栈和出栈这两个操作,都是在栈顶进行插入和删除数据,可以基于数组或者链表来实现。
//基于数组实现的顺序栈
public class ArrayStack{
private String[ ] items;//数组
private int count;//栈中元素的个数;
private int n;//栈的大小
}
//初始化数组,申请一个大小为n的空间
public ArrayStach(int n){
this.items = new String[n];
this.n = n;
this.count = 0;
}
//入栈操作
public boolean push(String item){
//数组空间不够,直接返回失败
if(count == n) return false;
items[count] = item;
++count;
return true;
}
//出栈操作
public String pop(){
//栈为空,直接返回null
if(count==0) return null;
String tmp = items[count-1];
--count;
return tmp;
}
不管是顺序栈还是链式栈,存储数据需要n,但是在入栈和出栈的过程中只需要一两个临时变量存储空间,所以空间复杂度是O(1)
支持动态扩容的顺序栈
对于支持动态扩容的顺序栈,其底层实现主要依赖于一个支持动态扩容的数组即可,再将原先的数据搬移到新的数组中。
以入栈的操作为例,当栈内有空间时,时间复杂度是O(1),但是当栈内空间不够,需要重新申请内存和数据搬移,时间复杂度就变成了O(n),可以采用摊还分析法,前K-1个元素,只是简单的入栈操作,时间复杂度为O(1).,当第k个元素入栈时,需要重新申请空间进行数据搬移,将这K个元素的搬移均摊到每个元素上面,那么每个元素只涉及一次入栈和搬移操作,以此类推,入栈操作的均摊时间复杂度是O(1)
栈的应用
函数调用栈
操作系统为每个线程都分配了一块独立的内存空间,用来存储函数调用时的临时变量,每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成返回之后,将这个函数对应的栈帧出栈:
//以此为例
int main(){
int a = 1;
int ret = 0;
int res = 0;
ret = add(3,5);
System.out.println(res);
retrun 0;
}
int add(int x,int y){
int sum = 0;
sum = x+y;
return sum;
}
main函数调用了add()函数,获取了计算结果,最终打印出了res的值,过程如下图所示
栈在表达式中的应用
例如包含加减乘除的四则运算,编译器通过两个栈来实现,其中一个是保存操作数的栈,另一个是保存运算符的栈。从左到右,当遇到数字的时候,直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较;如果比运算符栈顶的元素的优先级高,就将当前运算符入栈;如果比当前的运算符优先级低或者相等,就从运算符栈中取出栈顶运算符,从操作数栈的栈顶取出2个操作数进行计算,将最后的结果压入操作数栈,继续比较。
栈在括号匹配中的应用
圆括号()、方括号[]和花括号{},并且它们可以任意嵌套。比如,{[{}]}或[{()}([])]等都为合法格式,而{[}()]或[({)]为不合法的格式;用栈来保存未匹配的左括号,从左到右依次扫描字符串;当扫描到左括号时,将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号,如果能够匹配,就弹出,继续扫描剩余的字符串。如果扫描过程中遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。
解答开篇
如何利用栈实现浏览器的前进和后退功能?
采用两个栈X和Y,将首次浏览的页面依次压入栈X,当点击后退按钮时,在依次从栈X中取出数据放入栈Y中,当点击前进按钮时,再将栈Y中的元素依次取出放入栈X中,当栈X中没有数据时,就说明没有页面可以继续后退浏览了。当栈Y中没有元素,说明没有页面可以点击前进了。
练习题
判断括号字符串是否有效?
https://leetcode-cn.com/problems/valid-parentheses/description/
题目描述:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合,左括号必须以正确的顺序闭合,
注意空字符串可被认为是有效字符串。
思路:
上面的文章中已经讲了关于字符串匹配的问题,遇到左括号就依次压入栈中,遇到右括号,就与栈顶元素进行匹配,如果匹配,就弹出,反之就匹配失败
代码如下:
public boolean isValid(String s){
Stack<Character> stack = new stack<>();
for(int i =0;i<s.length();i++){
char a = s.charAt(i);
if(a=="("||a=="["||a=="{") stack.push(a);
else if(stack.empty()) retrun false;
else if(a==")"&&stack.pop()!="(") return false;
else if(a=="}"&&stack.pop()!="{") return false;
else if(a=="]"&&stack.pop()!="[") return false;
}
return stack.empty();
}