【Java】想进大厂?你应该知道的算法经典习题(栈)

✅作者简介:大家好我是烫嘴的辛拉面,大家可以叫我拉面。
个人主页: weixin_49405762的博客
系列专栏: 经典算法习题集
为大推荐一款刷题神器哦 点击跳转进入网站

☀️前言:从今天开始一个新的专栏 经典算法习题集,整理牛客网经典算法的习题练习,我将用java语言来解题。牛客网除了算法题单之外还有其他热门的各种提单,应有尽有,大家快刷起来吧点击跳转进入牛客网

目录

  • ✏️数据结构
    • ✒️AB1[模板]栈
      • 题目描述
      • 解题思路
      • 代码实现(Java)
    • ✒️AB2 栈的压入、弹出序列
      • 题目描述
      • 解题思路
      • 具体做法
      • 图示
      • 代码实现(Java):
    • ✒️AB3 有效括号序列
      • 题目描述
      • 题目主要信息
      • 解题思路:
      • 具体做法
      • 图示
      • 代码实现(java)
    • ✒️AB4 逆波兰表达式求值
      • 题目描述
      • 解题思路
      • 图示
      • 代码实现(java)
    • ✒️AB5 点击消除
      • 题目描述
      • 解题思路
      • 代码实现(java)
    • ✒️AB6 表达式求值
      • 题目描述
      • 题目的主要信息
      • 解题思路:
      • 具体做法
      • 图示
      • 代码实现(java)

✏️数据结构

✒️AB1[模板]栈

题目描述

请你实现一个栈。
操作:
push x:将 加x x\ x 入栈,保证 x x\ x 为 int 型整数。
pop:输出栈顶,并让栈顶出栈
top:输出栈顶,栈顶不出栈
输入描述:
第一行为一个正整数 n n\ n ,代表操作次数。(1≤n≤100000)(1 \leq n \leq 100000)(1≤n≤100000)
接下来的 n n\ n ,每行为一个字符串,代表一个操作。保证操作是题目描述中三种中的一种。

输出描述:
如果操作为push,则不输出任何东西。
如果为另外两种,若栈为空,则输出 "error“
否则按对应操作输出。
示例1

输入: 6
push 1
pop
top
push 2
push 3
pop

输出:1
error
3

解题思路

首先看清题目要求,并且根据栈先进后出的特性解决题目 1.用到头文件include这样可以省去建栈等操作,除此之外包含有多种函数 2.将题目分为三种情况,可用if,else语句进行分类,继而进行更进一步划分 3.注意L.pop()用于栈顶的清除,并非用于输出,输出有L.top()函数。

代码实现(Java)

import java.util.Scanner;
//自己做了个栈,不过没做太多校验
public class Main{
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int size = Integer.valueOf(sc.nextLine());
        Stack stack = new Stack(size);
        String tempStr;
        String[] tempArr;
        for(int i =0;i<size;i++){
            tempStr = sc.nextLine();
            tempArr = tempStr.split(" ");
            if(tempArr[0].equals("push")){
                stack.push(Integer.valueOf(tempArr[1]));
            }else if(tempArr[0].equals("top")){
                 stack.top();
            }else{
                stack.pop();
            }
        }
        
    }
        
}

class Stack{
    int[] stackSpace;
    static int index = -1;
    Stack(int size){
        stackSpace = new int[size];
    }
    
    
    public void push(int number){
        stackSpace[++index] = number;
    }
    
    public void pop(){
        if(index == -1){
            System.out.println("error");
        }else{
            System.out.println(stackSpace[index--]);
        }
    }
    
     public void top(){
        if(index == -1){
            System.out.println("error");
        }else{
            System.out.println(stackSpace[index]);
        }
    }
}

✒️AB2 栈的压入、弹出序列

题目描述

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

  1. 0<=pushV.length == popV.length <=1000
  2. -1000<=pushV[i]<=1000
  3. pushV 的所有数字均不相同

示例1

输入:[1,2,3,4,5],[4,5,3,2,1]
返回值:true
说明:可以通过 push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true

示例2

输入:[1,2,3,4,5],[4,3,5,1,2]
返回值:false
说明:由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false

解题思路

题目要我们判断两个序列是否符合入栈出栈的次序,我们就可以用一个栈来模拟。对于入栈序列,只要栈为空,序列肯定要依次入栈。那什么时候出来呢?自然是遇到一个元素等于当前的出栈序列的元素,那我们就放弃入栈,让它先出来。

具体做法

step 1:准备一个辅助栈,两个下标分别访问两个序列。
step 2:辅助栈为空或者栈顶不等于出栈数组当前元素,就持续将入栈数组加入栈中。
step 3:栈顶等于出栈数组当前元素就出栈。
step 4:当入栈数组访问完,出栈数组无法依次弹出,就是不匹配的,否则两个序列都访问完就是匹配的。

图示

【Java】想进大厂?你应该知道的算法经典习题(栈)_第1张图片

代码实现(Java):

import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        int n = pushA.length;
        //辅助栈
        Stack<Integer> s = new Stack<>();
        //遍历入栈的下标
        int j = 0;
        //遍历出栈的数组
        for(int i = 0; i < n; i++){
            //入栈:栈为空或者栈顶不等于出栈数组
            while(j < n && (s.isEmpty() || s.peek() != popA[i])){
                s.push(pushA[j]);
                j++;
            }
            //栈顶等于出栈数组
            if(s.peek() == popA[i])
                s.pop();
            //不匹配序列
            else
                return false;
        }
        return true;
    }
}

【Java】想进大厂?你应该知道的算法经典习题(栈)_第2张图片

✒️AB3 有效括号序列

题目描述

给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’]',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。

数据范围:字符串长度 0≤n≤100000\le n \le 100000≤n≤10000
要求:空间复杂度 O(n)O(n)O(n),时间复杂度 O(n)O(n)O(n)
示例1

输入:“()[]{}”
返回值:true

示例2

输入:“[]”
返回值:true

示例3

输入:“([)]”
返回值:false

题目主要信息

给定一个只包含大中小左右括号的字符串,判断其中括号是否合法
中小括号的数学顺序与合法无关,只需要每种左括号在右边有相应匹配的右括号即可,不可交叉匹配,应该是括号嵌套

解题思路:

括号的匹配规则应该符合先进后出原理:最外层的括号即最早出现的左括号,也对应最晚出现的右括号,即先进后出,因此可以使用同样先进后出的栈:遇到左括号就将相应匹配的右括号加入栈中,后续如果是合法的,右括号来的顺序就是栈中弹出的顺序。

具体做法

step 1:创建辅助栈,遍历字符串。
step 2:每次遇到小括号的左括号、中括号的左括号、大括号的左括号,就将其对应的呦括号加入栈中,期待在后续遇到。
step 3:如果没有遇到左括号但是栈为空,说明直接遇到了右括号,不合法。
step 4:其他情况下,如果遇到右括号,刚好会与栈顶元素相同,弹出栈顶元素继续遍历。
step 5:理论上,只要括号是匹配的,栈中元素最后是为空的,因此检查栈是否为空即可最后判断是否合法。

图示

【Java】想进大厂?你应该知道的算法经典习题(栈)_第3张图片

代码实现(java)

import java.util.*;
public class Solution {
    public boolean isValid (String s) {
        //辅助栈
        Stack<Character> st = new Stack<Character>(); 
        //遍历字符串
        for(int i = 0; i < s.length(); i++){ 
            //遇到左小括号
            if(s.charAt(i) == '(') 
                //期待遇到右小括号
                st.push(')'); 
            //遇到左中括号
            else if(s.charAt(i) == '[') 
                //期待遇到右中括号
                st.push(']'); 
            //遇到左打括号
            else if(s.charAt(i) == '{') 
                //期待遇到右打括号
                st.push('}'); 
            //必须有左括号的情况下才能遇到右括号
            else if(st.isEmpty() || st.pop() != s.charAt(i)) 
                return false;
        }
        //栈中是否还有元素
        return st.isEmpty(); 
    }
}

【Java】想进大厂?你应该知道的算法经典习题(栈)_第4张图片

✒️AB4 逆波兰表达式求值

题目描述

给定一个逆波兰表达式,求表达式的值。
数据范围:表达式长度满足 1≤n≤104 1 \le n \le 10^4 \ 1≤n≤104 ,表达式中仅包含数字和 + ,- , * , / ,其中数字的大小满足 ∣val∣≤200 |val| \le 200 \ ∣val∣≤200 。
示例1

输入:[“2”,“1”,“+”,“4”,“*”]
返回值:12

示例2

输入:[“2”,“0”,“+”]
返回值:2

解题思路

逆波兰表达式求值的过程可以借助栈来求解,在遍历数组的时候,判断当前是否是数字,如果是就压入栈中,不是说明遇到了运算符,从栈中弹出两个数字进行运算即可。

图示

【Java】想进大厂?你应该知道的算法经典习题(栈)_第5张图片

代码实现(java)

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param tokens string字符串一维数组 
     * @return int整型
     */
    public int evalRPN (String[] tokens) {
        // 栈
        Stack<Integer> stack = new Stack<Integer>();
        // 长度
        int n = tokens.length;
        // 遍历
        for (int i = 0; i < n; i++) {
            String token = tokens[i];
            // 说明是数字,则押入栈
            if (isNumber(token)) {
                stack.push(Integer.parseInt(token));
            }
            // 说明遇到运算符
            else {
                // 弹出两个元素
                int num2 = stack.pop();
                int num1 = stack.pop();
                //判断+ - * /
                switch (token) {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                    default:
                }
            }
        }
        return stack.pop();
    }
    // 用于判断token是数字还是运算符
    public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}

【Java】想进大厂?你应该知道的算法经典习题(栈)_第6张图片

✒️AB5 点击消除

题目描述

牛牛拿到了一个字符串。
他每次“点击”,可以把字符串中相邻两个相同字母消除,例如,字符串"abbc"点击后可以生成"ac"。
但相同而不相邻、不相同的相邻字母都是不可以被消除的。
牛牛想把字符串变得尽可能短。他想知道,当他点击了足够多次之后,字符串的最终形态是什么?
输入描述:
一个字符串,仅由小写字母组成。(字符串长度不大于300000)
输出描述:
一个字符串,为“点击消除”后的最终形态。若最终的字符串为空串,则输出0。
示例1

输入:abbc
输出:ac

示例2

输入:abba
输出:0

示例3

输入:bbbbb
输出:b

解题思路

分为两步来解决:

1.比较消除相同的字符

2.顺序输出不同的字符

步骤1:

遍历字符,放入栈中。当栈为空栈时(也就是遍历第一次字符的时候,此时栈为空)和栈顶元素和当前遍历的字符不相等时,将该字符放入栈中。
反之说明当前遍历的字符和栈顶字符是相同的,那么消除(移除)

步骤2:

判断栈是否为空,为空说明全部消除,不为空则顺序输出,因为栈是先进后出,这里要保证顺序的话,可以把当前栈的字符放入另外一个栈,然后再输出即保证顺序输出。

代码实现(java)

import java.util.Stack;


public class Solution2 {
    public static void main(String[] args) {
        eliminate("tsirhvtujuzdnwprhoihkzvckc");
        System.out.println();
        System.out.println("-----------------");
        eliminate("abba");
    }

    /**
     *
     * @param s string字符串
     * @return bool布尔型
     */
    public static void eliminate (String s) {
        if (s == null || s == ""){
            return;
        }
        char[] chars = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (int i = 0;i<chars.length;i++){
            if (stack.isEmpty() || stack.peek() != chars[i]){
                stack.push(chars[i]);
            }else {
                stack.pop();
            }
        }

        if (stack.isEmpty()){
            System.out.println("ok");
        }else {
            // 目的是为了顺序输出
            Stack<Character> stack2 = new Stack<>();
            while (!stack.isEmpty()){
                stack2.push(stack.pop());
            }
            while (!stack2.isEmpty()){
                System.out.print(stack2.pop());
            }
        }
    }
}


✒️AB6 表达式求值

题目描述

请写一个整数计算器,支持加减乘三种运算和括号。
数据范围:0≤∣s∣≤1000\le |s| \le 1000≤∣s∣≤100,保证计算结果始终在整型范围内
要求:空间复杂度: O(n)O(n)O(n),时间复杂度 O(n)O(n)O(n)
示例1

输入:“1+2”
返回值:3

示例2

输入:“(2*(3-4))*5”
返回值:-10

示例3

输入:“3+234-1”
返回值:26

题目的主要信息

写一个支持+ - *三种符号的运算器,其中优先级+ - 是一级,*更高一级
支持括号运算

解题思路:

对于上述两个要求,我们要考虑的是两点,一是处理运算优先级的问题,二是处理括号的问题。
处理优先级问题,那必定是乘号有着优先运算的权利,加号减号先一边看,我们甚至可以把减号看成加一个数的相反数,则这里只有乘法和加法,那我们优先处理乘法,遇到乘法,把前一个数和后一个数乘起来,遇到加法就把这些数字都暂时存起来,最后乘法处理完了,就剩余加法,把之前存起来的数字都相加就好了。
处理括号的问题,我们可以将括号中的部分看成一个新的表达式,即一个子问题,因此可以将新的表达式递归地求解,得到一个数字,再运算:

终止条件: 每次遇到左括号意味着进入括号子问题进行计算,那么遇到右括号代表这个递归结束。
返回值: 将括号内部的计算结果值返回。
本级任务: 遍历括号里面的字符,进行计算。

具体做法

step 1:使用栈辅助处理优先级,默认符号为加号。
step 2:遍历字符串,遇到数字,则将连续的数字字符部分转化为int型数字。
step 3:遇到左括号,则将括号后的部分送入递归,处理子问题;遇到右括号代表已经到了这个子问题的结尾,结束继续遍历字符串,将子问题的加法部分相加为一个数字,返回。
step 4:当遇到符号的时候如果是+,得到的数字正常入栈,如果是-,则将其相反数入栈,如果是*,则将栈中内容弹出与后一个元素相乘再入栈。
step 5:最后将栈中剩余的所有元素,进行一次全部相加。

图示

【Java】想进大厂?你应该知道的算法经典习题(栈)_第7张图片

代码实现(java)

import java.util.*;
public class Solution {
    public ArrayList<Integer> function(String s, int index){
        Stack<Integer> stack = new Stack<Integer>(); 
        int num = 0;
        char op = '+';
        int i;
        for(i = index; i < s.length(); i++){
            //数字转换成int数字
            //判断是否为数字
            if(s.charAt(i) >= '0' && s.charAt(i) <= '9'){ 
                num = num * 10 + s.charAt(i) - '0';
                if(i != s.length() - 1)
                    continue;
            }
            //碰到'('时,把整个括号内的当成一个数字处理
            if(s.charAt(i) == '('){
                //递归处理括号
                ArrayList<Integer> res = function(s, i + 1);
                num = res.get(0);
                i = res.get(1);
                if(i != s.length() - 1)
                    continue;
            }            
            switch(op){
            //加减号先入栈
            case '+': 
                stack.push(num);
                break;
            case '-':
                //相反数
                stack.push(-num);
                break;
            //优先计算乘号
            case '*':  
                int temp = stack.pop();
                stack.push(temp * num);
                break;
            }
            num = 0;
            //右括号结束递归
            if(s.charAt(i) == ')') 
                break; 
            else 
                op = s.charAt(i);
        }
        int sum = 0;
        //栈中元素相加
        while(!stack.isEmpty())  
            sum += stack.pop();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        temp.add(sum);
        temp.add(i);
        return temp; 
    }
    public int solve (String s) {
        ArrayList<Integer> res = function(s, 0);
        return res.get(0);
    }
}

【Java】想进大厂?你应该知道的算法经典习题(栈)_第8张图片

你可能感兴趣的:(经典算法习题集,java,算法,数据结构)