栈练习习题集

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值