算法与数据结构之美-栈


考虑下用栈如何实现浏览器的前进和后退功能

如何理解栈

栈的操作受限,只允许在一端进行插入和删除,后进先出;

如何实现一个栈?

栈主要是包含入栈和出栈这两个操作,都是在栈顶进行插入和删除数据,可以基于数组或者链表来实现。

//基于数组实现的顺序栈
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();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值