✅作者简介:大家好我是烫嘴的辛拉面,大家可以叫我拉面。🐭🐭
📜个人主页: 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就不可能是该压栈序列的弹出序列。
- 0<=pushV.length == popV.length <=1000
- -1000<=pushV[i]<=1000
- 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):
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;
}
}
✒️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)
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();
}
}
✒️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)
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));
}
}
✒️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)
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);
}
}