栈和队列(Java实现)
一:栈
1.1概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
1.2:栈的实现
单链表是否可以实现栈:
入栈:
如果采用尾插法入栈:入栈时间复杂度为O(n)如果有last 那么是O(1)但是 出栈操作 一定是O(n)。
如果采用头插法入栈 入栈的时间复杂度是O(1),同时出栈复杂度也是O(1)。
得出结论:如果采用单链表来实现栈,那么可以采用头插法的形式入栈和出栈。
采用双向链表来实现栈 是完全OK的 入栈 不管是头插法 还是尾插法都是可以实现的
时间复杂度都可以达到O(1)。
1.2.1:初始化
创建一个数组,对数组进行初始化
1.2.2:push (压栈入栈操作)
首先判断这个栈是否已满,如果满了就要进行扩容,如果没满,就将要插入的元素放到elem[userSize]位置,然后userSize在增加一个位置。
1.2.3: pop(出栈)
判断数组是否为空,然后用val接收末尾的数组元素,数组长度在减一。
1.2.4 :peek(栈顶)
获取栈顶元素,与pop不同的是,pop需要弹出这个元素,而peek只需要获取不需要弹出,执行这个方法之后数组长度不变。
二:队列:
1:概念:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
2:队列的实现
单链表实现: 单恋单链表加上一个标记尾结点的引用入队可以采用尾插法出队可以采用删除头节点注意:就算有尾节点的标记,也不能从头节点入队,因为除还是需要找最后一
个节点的前一个节点。
双链表实现: 如果是双向链表 链表的头 和 尾 都是可以进行入队的
2.1:初始化
实现一个双向链表,用userSize表示长度,first是头节点,last是尾节点。
2.2:offer(入队)
首先实例化val对象。
如果队列(数组)为空,那么入队的就是第一个头也是尾,然后如果不是,就是用尾插法,最后一个节点的next节点本来是null,插入了之后last.next指向node,node的前一个节点指向last,然后node成为尾节点(队尾),last指向node。
2.2:poll(出队)
出队操作,头进头出,poll队列中的头节点,使first被poll出去了,first指向下一个节点,并把下一个节点的prev的节点置为null,因为原本的first已经出去了。
2.4:peek(获取头节点)
直接返回first的val值。
三:实现
1:队列实现栈
1.1:初始化
用队列实现栈,需要创建两个队列来实现栈的入栈,出栈,获取栈顶方法。
1.2:push(入栈操作)
如果队列1没有元素就在队列1中入队,如果队列2中没元素就在队列2中入队,如果都没元素就在队列1中入队。
1.3:poll(出队操作)
模拟的出栈操作是:把不为空的队列中的size-1个元素放到另一个队列当中。最后剩下的这一个就是我们 模拟“出栈”的元素。
1.4:top(peek,获取栈顶元素)
假设qu1中存在元素,qu2中为空然后将,qu1中的元素依次弹出,用val依次保存,当弹出最后一个元素时此时val保存的就是最后一个元素的值34,此时stack的栈顶就是34,这样实现了获取栈顶元素。
1.5:empty(栈是否为空)
当qu1和qu2两个队列都为空时,就实现了模拟的栈为空。因为在执行模拟poll操作时,每次poll一个其中一个队列就弹出size-1个元素到另一个队列中,然后剩下的一个元素在弹出就是模拟栈出战的操作,此时弹出的元素就弹出了,没有到另一个队列中。
2:栈实现队列
用栈实现队列同样需要两个栈来实现队列,实现入队,出队,获取队顶元素。
2.1:初始化
创建两个两个栈实现初始化。
ArrayDeque 可以作为栈的实现:
Deque
接口支持栈操作
Deque
接口定义了栈的标准方法,包括:push(E e)
: 将元素压入栈顶(等效于addFirst(e)
)。pop()
: 弹出栈顶元素(等效于removeFirst()
)。peek()
: 查看栈顶元素但不移除(等效于peekFirst()
)。由于
ArrayDeque
实现了Deque
接口,它天然支持这些栈操作。因此,你可以像使用栈一样使用
2.2:push(入队操作)
都放到第一个栈中。
2.3:pop(出队)
stack1中弹出的依次压入stack2中,此时stack1中为空,然后stack2依次出栈就模拟实现出队操作。(注意:此时stack1中为null,所以多次调用pop方法只会重复执行return stack.pop操作)
2.4:peek(模式获取队顶元素)
与pop模拟出队类似,最后直接stack2.peek即可实现模拟获取队顶元素。
2.5:empty(判断是否为null)
两个栈都为空模拟队列就为空。
四:习题
1:括号匹配
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号
无效匹配情况:当放入的是右括号时,直接返回false,左括号的情况是最后i遍历完,栈中不为空返回false,右括号多的情况是栈为空时,i没有遍历完。
2:栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
3:逆波兰表达式求值
中缀表达式a+ b*c+(d*e+f)*g,其转换成后缀表达式则为abc*+de*f+g*+。
4:最小栈
1:要求:
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
2:MinStack()
初始化堆栈对象
首先初始化一个普通的栈和一个最小栈。
3:void push(int val)
将元素val推入堆栈。
逻辑实现:
当想要插入元素是,所有元素都会被插入主栈中(stack),只有满足 val <= minStack.peek()
的元素才会被推入 minStack,然后依次循环,比minStack.peek大的元素就留在主栈中。
入栈:
普通栈一定要放、最小栈放的原则是:
*如果最小栈是空的 ,那么放
*如果最小栈的栈顶元素没有当前的元素小,则放,如果要放的的元素 小于等于 最小栈栈顶元素 可以放吗?放
4: void pop()
删除堆栈顶部的元素。
比较普通站的栈顶是否等于最小栈的栈顶,当等于时,才能pop。
5:int top()
获取堆栈顶部的元素。
直接返回普通栈的栈顶即可
3:int getMin()
获取堆栈中的最小元素。
直接获取最小栈的栈顶即可