【栈和队列】

目录

1,栈(Stack)

1.1 概念

1.2 栈的使用

1.3 栈的模拟实现

1.4 栈的应用场景

1.5 概念区分

1.6  使用链表来实现栈

2, 队列(Queue)

2.1 概念

2.2 队列的使用

2.3 队列模拟实现

3,双端队列 (Deque)

4,面试题

4.1 用队列实现栈

4.2 用栈实现队列


1,栈(Stack)

1.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

1.2 栈的使用

push():叫做压栈,往栈里面放元素

查看push源代码,是有返回值的,他的返回值代表入栈的元素

pop():叫做出栈,弹出栈顶元素

此时就弹出了56

我们查看pop的源代码,他的返回值代表出栈的元素

peek():获取栈顶元素,但是不删除

源码:

empty():检测栈是否为空

源码:

stack可以调用empty可以理解,为什么可以调用isEmpty,Stack里面并没有这个方法呀!!

观察到Stack继承了Vector,isEmpty方法在Vector这个类当中,所以也继承了Vector类当中的方法

1.3 栈的模拟实现

创建一个类,叫做MyStack,自己来实现一个栈

那怎么来表示这个栈呢?

可以用一个数组来表示,每次往数组的最后一个存储元素,删也是删最后一个

1,push

怎么来放呢,就可以用到前面讲过的,定义一个usedSize来记录元素个数,并在对应位置存放元素,usedSize为0,在0下标存放一个元素,记录这个元素(usedSize++),在存放usedSize++下标位置的元素,再次记录这个元素(usedSize++),依次进行!!!

还要考虑数组满了的情况,对其进行扩容!

2,pop    弹出一个元素

首先如果是空的,就不能弹出元素,可以抛一个异常

可以发现,当usedSize为4的时候,我们需要弹出3下标的元素,即弹出elem[ usedSize -1 ]即可

3,peek   获取栈顶元素

这就是我们自己实现的一个栈。

可以将这个自己实现的栈变成泛型:

1.4 栈的应用场景

1,改变元素的序列

2,将递归转化为循环(一般情况下会用栈)

比如:逆序打印链表

非递归实现:就可以申请一个栈,这个栈里面就去放节点,然后进行出栈打印,即为逆序打印

3,括号匹配

描述:给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

在括号匹配的时候,一定是最后一个左括号和第一个右括号是否匹配,如何找到最后一个左括号呢?使用栈来完成。

情况一:如果是在括号匹配的时候,申请一个栈,定义一个 i 下标来遍历字符串。遇到一个左括号把这个左括号放到栈里面,i++,又遇到一个左括号放到栈里面,当遇到右括号的时候,就和当前的栈顶元素去匹配。当遇到右括号时,让当前的这个右括号与栈顶元素进行匹配,匹配成功,就让当前栈顶元素进行出栈,i++,再去判断右括号与当前栈顶元素是否匹配,匹配成功,就让当前栈顶元素进行出栈。即括号是匹配的情况下,最后栈为空且字符串以遍历完成。

情况二:如果不匹配,同上只要是左括号都放到栈里面,当遇到右括号时,与栈顶元素不匹配,返回false

情况三:字符串还没有遍历完,栈已经空了的情况。如下面这种,左括号放到栈里面,当遇到右括号时,与栈顶元素匹配,但是 i 没有遍历完,栈已经空了

情况四:左括号放到栈里面,遇到右括号时,与栈顶元素进行匹配,弹出栈顶元素后,i 已经遍历完了,但是栈里面还有元素

4,逆波兰表达式求值

逆波兰表达式又叫做后缀表达式(将运算符写在操作数之后)

例如:

中缀表达式(平常所看到的表达式):2+(3*5)

对应的后缀表达式:235*+

推导:

运算:

题目描述:

示例:

给的是一个字符串数组,放到栈里面的应该是整型,所以要将数字字符转为整型。

所以可以写一个方法,判断拿到这个字符串是否为运算符

5,栈的压入,弹出序列 (上面第一题)

描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

6,最小栈

描述:设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

在常数时间内(时间复杂度为O(1))检索到最小值,一个栈是不能完成的,需要申请两个栈,一个普通栈,一个最小栈。

那如何能拿到一个最小栈呢?

1.5 概念区分

栈、虚拟机栈、栈帧有什么区别呢?

栈是一种数据结构。

虚拟机栈:如定义的局部变量就存于虚拟机栈当中。

栈帧:调用方法的时候为他开辟的一块内存。

至此:刚刚实现的栈或者说Java集合类的Stack底层是一个数组。这种栈叫做顺序栈!!!

1.6  使用链表来实现栈

首先栈遵循后进先出的原则,并且时间复杂度为O(1)

如果是单链表,那么入栈和出栈最好从链表头进行入栈和出栈。

相反如果链表的尾部进行出栈入栈时间复杂度为O(N)。

如果是双向链表来实现栈,那么复杂度均是O(1),如果以后要用到链式栈,使用双向链表会更好!

那么就可以直接当栈来使用。

为什么会报错?因为List这个接口没有实现push这个方法,要用LinkedList来引用这个对象。

这样就可以直接使用了,我们查看push,pop的源码。源码里面采用的是头插和头删!

所以可以拿数组实现栈,也可以拿链表实现栈。

如果要用数组实现的栈就用Stack这个类。

要用链式栈,使用LinkedList引用,或者Deque(叫做双端队列,下面会讲到)引用就可以当中栈来使用。

2, 队列(Queue)

2.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)

例如:食堂打饭排队,先排队的先打饭

2.2 队列的使用

在Java中,Queue是个接口,底层是通过链表实现的。

三个红色框框为一组,三个绿色框框为一组,两组从结果上来说没有什么区别。

要看他们的区别,如add和offer都是入队操作。

add和offer都是向队列中添加一个元素,一些队列有大小限制,因此如果想在一个满的队列中加入一个新项:

  • add:此时调用 add() 方法就会抛出一个 unchecked 异常
  • offer:此时调用 offer() 方法会返回 false,可以在程序中进行有效的判断。

2.3 队列模拟实现

1,用链表来实现(源码里面用的链式实现)

首先队列遵循先进先出的原则,那么入队就可以从尾巴入,出队就可以从头部出

也可以入队从头部入,出队从尾巴出

我们可查看源码的offer:调用了add方法               进入add方法:源码实现的是尾插法

模拟实现Queue

2,用数组来实现队列

首先对头叫做first,队尾叫做last。

此时last遍历完了,数组满了

假设现在对头可以出,出掉12 23,那么我的对头为34。但是此时0,1下标还可以放元素,数组并没有满!!!

我们在数组满的时候让last指向first。可能first和last相遇代表数组为空,不管空还是满(后面解决),先让last走回来,此时就可以再次存放元素了。

那么这个数组就不是简单的数组了,这个数组循环利用了,把这个队列叫做循环队列。

相当于把这个数组卷起来!!! 现在这个数组是这个样子。

开始存放元素:

满或者空也不一定是在0位置,有可能last在7的时候first就进行出,一直出就可能是在7位置相遇。也可能last在7位置first就开始出,出到2位置又开始存,就也可能在2位置相遇。 

到目前位置:相遇的时候是空的,相遇的时候也可能是满的!!!没有解决问题。

如何判断空和满!

方案一:

usedSize!!!放一个数据usedSize++,us == 0 为空,us == 数组长度为满。

方案二:

定义一个Boolean类型的flg = false 第一次相遇为false,第二次相遇为true。以标记的形式来判断。

方案三:(此代码用方案三实现)

浪费一个空间。也就是假如你的数组长度是8,但是最多只放7个元素。

即last和first相遇时为空,last的下一个为first就为满

如何让last或者first从7走到0,不能是last++或者first++,这样会导致数组下标越界,我们可给出一个公式:last = (last + 1)% 数组长度

代码实现:

首先此时代码有点错误

3,双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

普通的队列只能从队尾进对头出,如果是双端队列,这个队列两边都可以进出。

有头插,尾插,还有头删尾删

LinkedList是双端队列的链表形式。

ArrayDeque是双端队列的线性实现(操作的是数组)。 

对于这两个类(LinkedList,ArrayDeque),可以当作栈使用,也可以当作队列使用

4,面试题

4.1 用队列实现栈

首先一个队列是不能实现栈的,这两个是相互矛盾的,一个是先进先出,一个是先进后出。

既然一个队列达不到要求,就用两个队列来实现栈。

top:

代码实现:

4.2 用栈实现队列

还是一样的,一个栈是不能实现队列的。要申请两个栈。

代码实现:

至此,栈和队列都讲完了!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值