1.
思路:
这种明显需要先分类讨论,列举出不符合正确顺序闭合的情况
1.如示例三:(] ——括号不匹配
2.{ [ ( ) ] ——左括号多了
3. [ ( ) ] } ——右括号多了
如果是左括号,那么无法判断是否正确,如果出现右括号,则可以开始判断这对括号是否正确,并且这个右括号一定是和最后放进去的左括号是一对,左括号是后进先出,那么就可以用栈这个数据结构
所以用栈来判断上面三种不正确的情况
1.是左括号时,就入栈,然后字符串继续遍历;当出现右括号时,将栈顶元素出栈,如果匹配,则字符串继续遍历,如果不匹配就直接返回false
2.当字符串已经遍历完了,发现栈里面还有左括号,说明左括号多了,左括号的个数和右括号的个数不一样,则返回false
3.当字符串还没有遍历完,并且当前的元素是右括号,此时要将栈顶元素出栈,但是栈已经空了,说明右括号多了,则返回false
那么如果全部匹配,且字符串遍历完并且栈为空,则返回true
class Solution {
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(ch=='('||ch=='{'||ch=='['){//如果是左括号
stack.push(ch);//入栈
}else{//如果是右括号
if(stack.empty()){//如果栈已经空了,没有能和右括号匹配
return false;//右括号多了
}else{//栈没空
char ch1=stack.pop();//栈顶元素出栈
if((ch==')'&&ch1=='(')||(ch=='}'&&ch1=='{')||(ch==']'&&ch1=='[')){//如果匹配
continue;//判断下一个
}else{//不匹配
return false;
}
}
}
}
//字符串遍历完了
if(stack.empty()){//如果栈里面左括号没有多
return true;
}else{//左括号多了
return false;
}
}
}
2.
思路:
跟判断出栈顺序的思路一样,只不过转换成代码形式,遍历入栈顺序,当一开始栈为空时,一个入栈一个元素,下一次遍历时,就该判断栈顶元素是否等于出栈顺序的最左边的元素,用j表示出栈顺序走到哪里了,并且有可能连续出栈,所以这里应该用循环;最后将遍历入栈顺序的当前元素再入栈
因为当遍历入栈循环结束后,我们将最后一个入栈元素入栈了,所以此时栈里面至少有一个元素,此时再循环判断栈顶元素是否等于出栈顺序 j 位置的元素,并且出栈顺序没遍历完,如果相等则继续,不相等则跳出循环。最后判断栈是否为空,为空则入栈顺序和出栈顺序全部匹配,不为空则入栈顺序和出栈顺序不完全匹配
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pushV int整型一维数组
* @param popV int整型一维数组
* @return bool布尔型
*/
public boolean IsPopOrder (int[] pushV, int[] popV) {
// write code here
Stack<Integer> stack=new Stack<>();
int j=0;//遍历出栈顺序popV的下标
for(int i=0;i<pushV.length;i++){
if(stack.empty()){//如果此时栈为空
stack.push(pushV[i]);//入栈
continue;//直接找下一个
}
while(!stack.empty()&&stack.peek()==popV[j]){//如果栈不空,并且栈顶元素等于出栈顺序j位置
stack.pop();//出栈
j++;//出栈顺序往后移动一位
}
stack.push(pushV[i]);//将当前元素入栈(说明循环结束后,栈里面一定存在1个及以上个元素)
}
while(j<popV.length&&stack.peek()==popV[j]){//没遍历完且栈顶元素等于出栈顺序j位置
stack.pop();//出栈
j++;//遍历下一个
}
if(stack.empty()){//栈为空,说明全部出栈顺序对应上了
return true;
}else{//栈不为空,说明有的没匹配上,出栈顺序不对
return false;
}
}
}
3.
前提知识:
我们日常生活中用的表达式是中缀表达式,例如(1+2)*5这种,当然也存在前缀表达式和后缀表达式,其中后缀表达式也叫逆波兰表达式,是计算机计算数字采用的表达式,其中就是通过栈来判断计算顺序的优先级。中缀表达式转换为后缀表达式的一种简单方法就是不管怎么样,按照运算符优先级,每两个操作数就用括号包起来,整个式子被括号包完后,将每对括号里的运算符往后提在这对括号之后,最后将括号全部删去,就是后缀表达式
例如:
a+b*c+(d*e-f)/g
第一步按照优先级加括号
a+(b*c)+((d*e)-f)/g
a+(b*c)+(((d*e)-f))/g
a+(b*c)+((((d*e)-f))/g)
(a+(b*c))+((((d*e)-f))/g)
((a+(b*c))+((((d*e)-f))/g))
第二步外到内将运算符提出来,放在当前括号的后面
((a(bc)*)+((((de)*f)-)g)/)+
第三步删除所有括号,得出的就是后缀表达式
abc*+de*f-g/+
同理,在第二步中,如果将运算符提出了放在当前括号前面,则删除所有括号后,得出的就是前缀表达式
当计算机看到后缀表达式后,就会用栈来判断运算顺序,如果是操作数,则入栈,如果是运算符,则将栈顶元素取两个出来,其中第一个出栈的为右操作数,第二个出栈的为左操作数(+*左右操作数交换是一样的,但是-/左右操作数可不能互换),然后将这两个操作数进行运算符的操作,得出来的值又继续入栈,然后如此类推,最后算出来的值就是全部运算后的结果
例如上面的式子,代入几个数字
中缀表达式:1+2*3+(4*5-6)/7,算出来的结果是9
变为后缀表达式为:123*+45*6-7/+
1 2 3是操作数,入栈,栈里为1 2 3
遇见*是运算符,将栈顶元素出栈两个,3先出,作为右操作数,2后出,作为左操作数,2*3=6
将6入栈,此时栈里为1 6
然后遇见+,出栈两个,6先出,1后出,所以1+6=7,将7入栈,此时栈里为7
4 5是操作数,入栈,栈里为7 4 5
然后*,出栈两个,5先出,4后出,所以4*5=20,将20入栈,此时栈里为7 20
6入栈,栈里为7 20 6
然后-,出栈两个,6先出,20后出,所以20-6=14,将14入栈,此时栈里为7 14
7入栈,栈里为7 14 7
然后/,出栈两个,7先出,14后出,所以14/7=2,将2入栈,此时栈里为7 2
然后+,出栈两个,2先出,7后出,所以2+7=9,将9入栈,此时栈里为9
所以结果为9,跟中缀表达式的结果一样
思路:
现在就是将上面的操作用代码实现即可
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack=new Stack<>();
for(String str:tokens){
if(!(str.equals("+")||str.equals("-")||
str.equals("*")||str.equals("/"))){//如果不是运算符
int x=Integer.parseInt(str);//字符串转为整型并入栈
stack.push(x);
}else{//是运算符
int val2=stack.pop();//先出栈的为右操作数
int val1=stack.pop();//后出栈的为左操作数
switch(str){
case "+":
stack.push(val1+val2);
break;
case "-":
stack.push(val1-val2);
break;
case "*":
stack.push(val1*val2);
break;
case "/":
stack.push(val1/val2);
break;
}
}
}
return stack.pop();//返回运算结果
}
}
4.
思路:
栈这种数据类型只能先进后出,不能想让哪个元素出就让,所以靠一个栈是不能做到题目的要求的,因此需要两个栈,一个栈为普通栈,另一个栈的栈顶元素用来存放当前普通栈的最小值,即最小栈。
当一开始栈都为空时,第一个元素放进去肯定是当前的最小值,最小栈放进该元素,然后当后面入栈的元素小于等于最小栈的栈顶元素,则放进最小栈里面,其中当等于时,也应该入栈,因为如果不入栈的话,那么该元素出栈后,会把最小栈原来的栈顶元素也出栈,但是这个栈顶元素保留的是之前的最小元素
例子:
普通栈:3 1 1'
最小栈:3 1 ?
如果当1'==1时不入栈,则最小栈只有:3 1,那么当普通栈1'出栈时,最小栈也出栈1,此时普通栈为3 1,但是最小栈的栈顶元素为3,明显不是最小值
而当1'==1入栈时,则最小栈有:3 1 1',那么当普通栈1'出栈时,最小栈也出栈1',此时普通栈为3 1,最小栈的栈顶元素为1,是普通栈的最小值,是正确的
class MinStack {
Stack<Integer> stack;//普通栈
Stack<Integer> MinStack;//最小栈
public MinStack() {
stack=new Stack<>();
MinStack=new Stack<>();
}
public void push(int val) {
stack.push(val);//都会入普通栈
if(MinStack.empty()){//如果此时最小栈是空
MinStack.push(val);//它是当前的最小值
}else{//最小栈不空
int peekVal=MinStack.peek();//自动拆箱,Integer变为int
if(val<=peekVal){//小于等于就入栈
MinStack.push(val);
}
}
}
public void pop() {
if(stack.empty()){
return;
}
int popval=stack.pop();//自动拆箱,Integer变为int
if(popval==MinStack.peek()){//如果把当前的最小值从普通栈出栈
MinStack.pop();//最小栈的栈顶元素更换
}
}
public int top() {
if(stack.empty()){
return -1;
}
return stack.peek();
}
public int getMin() {
if(MinStack.empty()){
return -1;
}
return MinStack.peek();
}
}