05 数据结构-栈

先进后出,后进先出,只允许在一端插入和删除数据。

第一次接触栈,会很疑惑,因为相对与数组和和链表,感觉栈给我们的只有限制,没任何优势。

其实,特定的数据结构是对特定场景的抽象,操作上越灵活,使用就越不可控,也就更容易出错。

当某种浏览器的前进后退,就可以使用栈这种数据结构来处理。

一、如何实现一个栈

栈主要包含2个操作,在栈顶插入和删除元素。栈既可以用数组实现,也可以用链表实现,数组实现的叫做顺序栈,链表实现的叫做链式栈

以下使用数组实现一个顺序栈:

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;
	}
}

时间复杂度:入栈和出栈都是O(1)

二、支持动态扩容的顺序栈

按之前的代码,原理就是,当栈满了之后,我们就申请一个更大的数组,把之前的数组元素复制到新的数组中。

时间复杂度:

对于出栈,复杂度是O(1)。对于入栈,如果没有满,那么也是O(1)。如果满了,那么就涉及到数据的迁移,就是O(n)。

按照之前复杂度分析中将的摊还分析法来分析:

先做一些假设:

  • 每次扩容为原来的2倍
  • 假设只有入栈没有出栈
  • 不涉及扩容的入栈操作时间复杂度为O(1)

当前栈大小为k,k次O(1)操作之后,需要经过O(k)次操作扩容为原来的2倍,变为2k,还可以有k次O(1)操作,然后经过o(2k)次操作变为4K,接下来可以O(1)时间入栈2k次。

第一次费时O(K)操作后,可以使用O(1)时间k次,第二次费时O(2k)操作后,可以使用O(1)时间操作2k次。

那么我们将O(K)摊分到接下来的k次操作上,O(2K)摊分到2k操作上,以此类推。。。

总的时间复杂度就是就是O(1)

三、栈在函数调用中的应用

在软件工程中,函数的调用使用的就是栈。

操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构, 用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈,比如:

int main() {
   int a = 1; 
   int ret = 0;
   int res = 0;
   ret = add(3, 5);
   res = a + ret;
   printf("%d", res);
   reuturn 0;
}

int add(int x, int y) {
   int sum = 0;
   sum = x + y;
   return sum;
}

 四、栈在表达式求值中的应用

我们再来看看栈的另一个常见的场景,编译器是如何利用栈来实现表达式求值的。这里我们只考虑加减乘除运算。

对于编译器来说,是利用2个栈来实现的,其中一个栈用来保存数字,另一个栈用来保存运算符。具体做法是:

我们从左往右遍历,遇到数字,我们直接压入保存数字的栈,遇到运算符,与保存运算符栈顶的符号比较。

如果当前运算符比栈顶的运算符优先级高,则将当前运算符压入栈中,如果比栈顶的运算符优先级低或者相同,则从栈顶取出操作符,并从数字栈中取出2个数字进行运算,计算后将数字压入数字栈,然后继续比较。知道遍历结束:

3+5*8-6如图:

 五、栈在括号匹配中的应用

我们还能用栈来检测表达式中的括号是否匹配。也就是(),{}必须成对出现。(){},({})是合法的,({)}则是不合法的。

具体做法:我们用一个栈来存储左括号(   {   [

从左到右遍历字符串,如果遇到左括号,入栈,遇到右括号,则与栈顶元素比较,如果不相等或者栈为空,则括号不匹配。如果相等,那么弹出栈顶元素,继续比较。知道遍历完全,并且次吃栈为空,则表示括号匹配。

六、总结

栈只支持入栈和出栈2个操作。后进先出是他的特点。栈可以用数组实现,也可以用链表实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七号公园的忧伤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值