本文我们就来了解一下栈的一些经典的算法题目!
直接看题吧!
有效的括号
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
输入:s = "()" 输出:true输入:s = "()[]{}" 输出:true
条件上面都说了,那就来聊聊思路吧;
思路
ideas 1
使用哈希表将所有的符号先存储起来,左半边为key,右半边为value。然后遍历字符串,遇到左半边的符号就入栈,遇到右半边的符号就与栈顶的符号比较,不匹配就返回false。
ideas 2
简单来说就是把与字符串中左括号对应的右括号压入栈中,直到遍历到右括号了开始和栈中弹出的元素进行比较,具体的情况就下面几种。
分析满足条件情况进行区分,总体上来说,不匹配的分类一共有 3 种,分别如下:
字符串左边括号多余
( { [ ] } ( )
括号不多余,但类型不匹配
字符串右边括号多余
{ ( ) } [ ] )
具体解法
先来看第一种解法的代码:
这种解法思路简单但是代码不太容易写!
class Solution {
public boolean isValid(String s) {
if(s.length() <= 1){
return false;
}
Map<Character,Character> map = new HashMap<>();
map.put('(',')');
map.put('{','}');
map.put('[',']');
Stack<Character> stack = new Stack<>();
for(int i=0;i < s.length();i++){
char tmp = s.charAt(i);
if(map.containsKey(tmp)){
stack.push(tmp);
}else{
if(!stack.isEmpty()){
Character left = stack.pop();
char rightchar = map.get(left);
if(rightchar != tmp){
return false;
}
}else{
return false;
}
}
}
return stack.isEmpty();
}
}
解法2
上面也说了几种匹配的情况,这里就不再多说了,艹,还是讲讲吧,不然自己容易忘!
对于第一种情况:左括号多余
因为我们的思路是将左括号对应的有括号放入栈中,然后遍历到了右括号后,就开始弹出栈中的元素进行比较,此时左括号多了,栈中就还有元素。所有return false
对于第二种情况:括号不匹配
通俗易懂即括号对不上
对于第三种情况:右括号多余
遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
唯一正确的就是字符串遍历完之后,栈是空的,就说明全都匹配了。
代码如下:
public boolean isValid (String s) {
// write code here
Deque<Character> deque = new LinkedList<>();
char ch;
for(int i = 0;i < s.length();i++){
ch = s.charAt(i);
if(ch == '('){
deque.push(')');
}else if(ch == '{'){
deque.push('}');
}else if(ch == '['){
deque.push(']');
}else if(deque.isEmpty() || deque.peek() != ch){
return false;
}else{
deque.pop();
}
}
return deque.isEmpty();
}
最小栈
设计一个支持
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。实现
MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。 输入: ["MinStack","push","push","push","getMin","pop","top","getMin"] [[],[-2],[0],[-3],[],[],[],[]] 输出: [null,null,null,null,-3,null,0,-2] 解释: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
有一个非常巧妙的思路,在元素入栈时,我们可以统计当前的最值(利用一个辅助栈),而不是在使用到了getmin方法时才去计算。比如说 1,2,3 依次入栈且没弹出,此时 4 也入栈了,因为4 在栈中,在4被弹出之前,1,2,3都是出不来的,所以提前计算是可以得到节省资源的。
具体来索索什么搞。
将每个元素入栈时把当前栈的最值存储起来。在这之后无论何时,如果栈顶元素是a,下面就是我们的最值。
我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最值。
- 当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最值插入辅助栈中;
- 当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出;
在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。
代码如下:
class MinStack {
Deque<Integer> xStack;
Deque<Integer> minStack;
public MinStack() {
xStack = new LinkedList<>();
minStack = new LinkedList<>();
minStack.push(Integer.MAX_VALUE);
}
public void push(int val) {
xStack.push(val);
minStack.push(Math.min(minStack.peek(),val));
}
public void pop() {
xStack.pop();
minStack.pop();
}
public int top() {
return xStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
最大栈
题目:设计一个最大栈数据结构,既支持栈操作,又支持查找栈中最大的元素。
具体的就不写了,直接介绍思路:
本题和上面的最小栈是很相似的,区别与普通的栈,我们还要考虑 peekMax() 和 popMax() 这两个操作。
对于 peekMax(),我们可以使用一个辅助栈来存储每个位置到栈底的所有元素的最大值。
呃直接看代码。。。。
class MaxStack{
Stack<Integer> stack;
Stack<Integer> maxStack;
public MaxStack(){
stack = new Stack();
maxStack = new Stack();
}
public void push(int val){
int max = maxStack.isEmpty() ? val : maxStack.peek();
maxStack.push(max > val ? max : val);
stack.push(val);
}
public int pop(){
maxStack.pop();
return stack.pop();
}
public int top(){
return stack.peek();
}
public int peekMax(){
return maxStack.peek();
}
public int popMax(){
int max = peekMax();
Stack<Integer> tmp = new Stack();
while(top() != max) tmp.push(pop());
pop();
while(!tmp.isEmpty()) push(tmp.pop());
return max;
}
}
相信各位xdm自己一看就能懂~~~~