目录
一:栈的理论知识
因为栈的理论很简单,主要是考察熟练度的问题。
栈的应用:最近相似性(从内向外或者从外向内)
公平性(先来先到)
编程:目前Java中一般使用双向队列构造栈和队列
//原始方法 :下面操作由这个定义来讲的
Stack<Integer> stack = new Stack<Integer>();
//目前常用方法 :它的常见操作见双端队列即可
Deque<String> stack = new LinkedList<String>();
stack有哪些具体操作:
- push() :进栈
- pop(): 出栈
- peek():看看栈顶元素(不取走)
- empty():判断栈是否为空。
【注意】:判断栈stack(stack实现)的时候用的是empty(),
判断队列(Linkedlist实现)是否为空,使用的是isEmpty().
二:队列的理论知识
队列分为单向队列和双向队列
讲清楚双向队列的操作,单项队列就完全OK了
双端队列的定义:
Deque<Integer> deque = new LinkedList<Integer>();
// 单纯的单项队列也可以定义为
Queue<Integer> queue = new LinkedList<Integer>();
双端队列的操作:
- addFirst()
- addLast()
- removeFirst()
- removeLast()
- getFirst()
- getLast()
- size()
同样的定义,对于队列,即Queue,也可以用上述双端队列的操作,或者使用更简洁的操作
- add()
- remove()
- peek()
题目一:有效的括号
(https://leetcode-cn.com/problems/valid-parentheses/)
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
官方解题思路简述:使用栈和哈希表
其中对于左的括号压进栈中。哈希表的key存储右侧的括号,哈希表的value存储左侧的括号。
所以字符不在哈希表中,表明字符是左侧的,需要进栈,否则进行后续比较
Java源代码:
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public class stack2 {
public static void main(String[] args) {
String s = "(";
Deque<Character> stack = new LinkedList<Character>();
Map<Character,Character> hashtable = new HashMap<Character,Character>();
hashtable.put(')','(');
hashtable.put(']','[');
hashtable.put('}','{');
char ch;
for(int i =0;i<s.length();i++){
ch = s.charAt(i);
if(hashtable.containsKey(ch)){
if(stack.isEmpty() || stack.peek() != hashtable.get(ch)){
System.out.println("no");
}else {
stack.pop();
}
}
else{
stack.push(ch);
}
}
if(stack.isEmpty()){
System.out.println("yes");
}
else{
System.out.println("no");
}
}
}
【注意1】:最后比较的是栈顶元素和hashtable.get(ch)进行比较,而不是栈顶元素和ch比较
【注意2】:最后还得判断栈是否为空,因为存在输入样例“[”这样的例子,如果栈不为空,则返回false;
第二次写的代码:
class Solution {
public boolean isValid(String s) {
Map<Character,Character> map = new HashMap<Character,Character>();
Deque<Character> stack = new LinkedList<Character>();
map.put(')','(');
map.put(']','[');
map.put('}','{');
for(int i =0;i<s.length();i++){
char currentchar = s.charAt(i);
if(map.containsKey(currentchar)){
if(map.get(currentchar) == stack.peek()){
stack.pop();
}else{
return false;
}
}else{
stack.push(currentchar);
}
}
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
}
解释:使用栈和hashmap这两种数据结构进行操作。
题目二:柱状图中最大的矩形
(链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
自己的方法:暴力法:枚举左边界,右边界,找到左右边界中高度最小的高度
结果超时。
public class maxarea {
public static void main(String[] args) {
int[] heights = new int[]{0,9};
int maxarea = 0;
for (int i = 0; i < heights.length; i++) {
for (int j = i; j < heights.length; j++) {
int minheight = Integer.MAX_VALUE;
for(int k = i; k<=j;k++){
if(minheight > heights[k]){
minheight = heights[k];
System.out.println("minheight: "+i);
}
}
int temparea = minheight * (j-i+1);
if(maxarea < temparea){
maxarea = temparea;
}
}
}
System.out.println("maxarea: "+maxarea);
}
}
官方思路:单调栈:费脑子,还是要反复思考
class Solution {
public int largestRectangleArea(int[] heights) {
int res = 0;
// 单调栈:栈底到栈顶由小到大
Deque<Integer> stack = new LinkedList<>();
// 矩形的左右边界
int l, r;
for (r = 0; r < heights.length; r++) {
// 它的起始位置等于最后一个弹出的 index
l = r;
while (!stack.isEmpty() && heights[stack.peek()] > heights[r]) {
// 弹出的比当前大的数,它不可能再变长了,计算长度
l = stack.pop();
res = Math.max((r - l) * heights[l], res);
}
heights[l] = heights[r];
stack.push(l);
}
// 处理栈中剩余的元素
while (!stack.isEmpty()) {
int index = stack.pop();
res = Math.max((r - index) * heights[index], res);
}
return res;
}
}
题目三:滑动窗口最大值
(链接:https://leetcode-cn.com/problems/sliding-window-maximum)
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
自己的思路:枚举每个滑动窗口,并且找到其中的最大值:结果超时
Java源代码
import java.util.ArrayList;
import java.util.List;
public class slidewindow {
public static void main(String[] args) {
int[] nums = new int[]{1,3,-1,-3,5,3,6,7};
int k =3;
int[] result = new int[nums.length-k+1];
for (int i = 0; i < nums.length-k+1; i++) {
int every_maxvalue=Integer.MIN_VALUE;
for (int j = i; j < i+k; j++) {
if(every_maxvalue < nums[j]){
every_maxvalue = nums[j];
}
}
result[i] = every_maxvalue;
}
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
}
}
官方思路:还是有点难理解,后续再思考
题目四:检查替换后的词是否有序
(2021/1/9)链接:
给定有效字符串 "abc"。
对于任何有效的字符串 V,我们可以将 V 分成两个部分 X 和 Y,使得 X + Y(X 与 Y 连接)等于 V。(X 或 Y 可以为空。)那么,X + "abc" + Y 也同样是有效的。
例如,如果 S = "abc",则有效字符串的示例是:"abc","aabcbc","abcabc","abcabcababcc"。无效字符串的示例是:"abccba","ab","cababc","bac"。
如果给定字符串 S 有效,则返回 true;否则,返回 false。
示例 1:
输入:"aabcbc"
输出:true
解释:
从有效字符串 "abc" 开始。
然后我们可以在 "a" 和 "bc" 之间插入另一个 "abc",产生 "a" + "abc" + "bc",即 "aabcbc"。
示例 2:
输入:"abcabcababcc"
输出:true
解释:
"abcabcabc" 是有效的,它可以视作在原串后连续插入 "abc"。
然后我们可以在最后一个字母之前插入 "abc",产生 "abcabcab" + "abc" + "c",即 "abcabcababcc"。
示例 3:
输入:"abccba"
输出:false
示例 4:
输入:"cababc"
输出:false
官方思路:使用栈,进行栈的值与当前字符串进行比较。一旦遇到当前字符为:“c”就去看栈里面的数据,否则字符入栈。
Java源代码
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.Deque;
import java.util.LinkedList;
public class abc {
public static void main(String[] args) {
String S ="aabcbc";
Deque<Character> stack = new LinkedList<Character>();
for(int i = 0;i<S.length();i++){
char ch = S.charAt(i);
if(ch == 'c'){
System.out.println(stack);
System.out.println("=====");
if(stack.isEmpty()||stack.removeLast() != 'b' ){
System.out.println("="+stack);
System.out.println("no1");;
}
System.out.println("== "+stack);
if(stack.isEmpty()||stack.removeLast()!= 'a' ){
System.out.println("no2");;
}
}else{
stack.add(ch);
}
if(stack.isEmpty()){
System.out.println("yes");
}
}
}
}
第二次写的代码
class Solution {
public boolean isValid(String s) {
Deque<Character> stack = new LinkedList<Character>();
for(int i =0;i<s.length();i++){
char currentchar = s.charAt(i);
if(currentchar == 'c'){
int flag =0;
if(!stack.isEmpty()){
if(stack.peek() == 'b'){
flag +=1;
stack.pop();
}
else{
return false;
}
}else{
return false;
}
if(!stack.isEmpty()){
if(stack.peek() == 'a'){
flag +=1;
stack.pop();
}
else{
return false;
}
}else{
return false;
}
}else{
stack.push(currentchar);
}
}
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
}
题目四:棒球比赛
(链接:https://leetcode-cn.com/problems/baseball-game/)
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x - 表示本回合新获得分数 x
"+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
"D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
"C" - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
示例 1:
输入:ops = ["5","2","C","D","+"]
输出:30
解释:
"5" - 记录加 5 ,记录现在是 [5]
"2" - 记录加 2 ,记录现在是 [5, 2]
"C" - 使前一次得分的记录无效并将其移除,记录现在是 [5].
"D" - 记录加 2 * 5 = 10 ,记录现在是 [5, 10].
"+" - 记录加 5 + 10 = 15 ,记录现在是 [5, 10, 15].
所有得分的总和 5 + 10 + 15 = 30
示例 2:
输入:ops = ["5","-2","4","C","D","9","+","+"]
输出:27
解释:
"5" - 记录加 5 ,记录现在是 [5]
"-2" - 记录加 -2 ,记录现在是 [5, -2]
"4" - 记录加 4 ,记录现在是 [5, -2, 4]
"C" - 使前一次得分的记录无效并将其移除,记录现在是 [5, -2]
"D" - 记录加 2 * -2 = -4 ,记录现在是 [5, -2, -4]
"9" - 记录加 9 ,记录现在是 [5, -2, -4, 9]
"+" - 记录加 -4 + 9 = 5 ,记录现在是 [5, -2, -4, 9, 5]
"+" - 记录加 9 + 5 = 14 ,记录现在是 [5, -2, -4, 9, 5, 14]
所有得分的总和 5 + -2 + -4 + 9 + 5 + 14 = 27
示例 3:
输入:ops = ["1"]
输出:1
提示:
1 <= ops.length <= 1000
ops[i] 为 "C"、"D"、"+",或者一个表示整数的字符串。整数范围是 [-3 * 104, 3 * 104]
对于 "+" 操作,题目数据保证记录此操作时前面总是存在两个有效的分数
对于 "C" 和 "D" 操作,题目数据保证记录此操作时前面总是存在一个有效的分数
自己的思路:很简单,使用栈即可
在编译器中写的代码:
import java.util.Stack;
public class ballgame {
public static void main(String[] args) {
//String[] ops = {"5","2","C","D","+"};
String[] ops ={"5","-2","4","C","D","9","+","+","C"};
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < ops.length; i++) {
String curr = ops[i];
if(curr == "C"){
System.out.println("yes");
stack.pop();
}
else if(curr == "D"){
stack.push(2*stack.peek());
}else if(curr == "+"){
int first = stack.pop();
int second = stack.peek();
stack.push(first);
stack.push(first+second);
}else{
stack.push(Integer.parseInt(curr));
}
}
System.out.println(stack);
int sum = 0;
while(!stack.isEmpty()){
sum += stack.pop();
}
System.out.println(sum);
}
}
上述字符串比较是否相等我使用的是“==”,结果提交代码通不过,后来改成“equals”,通过了
class Solution {
public int calPoints(String[] ops) {
Stack<Integer> stack = new Stack<Integer>();
stack.push(0);
for (int i = 0; i < ops.length; i++) {
String curr = ops[i];
//if(curr == "C"){
if(curr.equals("C")){
stack.pop();
}
else if(curr.equals("D")){
stack.push(2*stack.peek());
}else if(curr.equals("+")){
int first = stack.pop();
int second = stack.peek();
stack.push(first);
stack.push(first+second);
}else{
stack.push(Integer.valueOf(curr));
}
}
int sum = 0;
while(!stack.isEmpty()){
sum += stack.pop();
}
return sum;
}
}
【注意】:Java中两个字符串用“==”比较仅仅是比较地址是否相等,要比较值是否相等还是要使用equals.
题目五:括号的分数
(链接:https://leetcode-cn.com/problems/score-of-parentheses/)
给定一个平衡括号字符串 S,按下述规则计算该字符串的分数:
() 得 1 分。
AB 得 A + B 分,其中 A 和 B 是平衡括号字符串。
(A) 得 2 * A 分,其中 A 是平衡括号字符串。
示例 1:
输入: "()"
输出: 1
示例 2:
输入: "(())"
输出: 2
示例 3:
输入: "()()"
输出: 2
示例 4:
输入: "(()(()))"
输出: 6
官方思路:用栈存储。每次遇见‘(’执行进栈操作。如果遇到‘)’有两种情况,要么是最内层的‘)’,
栈顶元素出来,我们得到结果1,并且加到此时栈顶的结果中,如果不是最内层的
我们把刚刚出栈结果*2加到此时栈顶结果中,把最新的结果放回栈里面去。
Java源代码:
class Solution {
public int scoreOfParentheses(String S) {
Stack<Integer> stack = new Stack<Integer>();
stack.push(0);
int sum = 0;
for(char ch: S.toCharArray()){
if(ch =='('){
stack.push(0);
}else{
int first = stack.pop();
int second = stack.pop();
sum=second+Math.max(1,2*first);
stack.push(sum);
}
}
return stack.pop();
}
}
【注意】:计算出sum之后,需要进栈。
官方思路:
我们用一个栈来维护当前所在的深度,以及每一层深度的得分。
当我们遇到一个左括号 ( 时,我们将深度加一,并且新的深度的得分置为 0。
当我们遇到一个右括号 ) 时,我们将当前深度的得分乘二并加到上一层的深度。
这里有一种例外情况,如果遇到的是 (),那么得分为1.
class Solution {
public int scoreOfParentheses(String S) {
Deque<Integer> stack = new LinkedList<Integer>();
stack.push(0);
for(int i=0;i<S.length();i++){
if(S.charAt(i) == '('){
stack.push(0);
}
else{
int top1 = stack.pop();
int top2 = stack.pop();
int tempsum = top2+ Math.max(top1*2, 1);
stack.push((tempsum));
}
}
return stack.peek();
}
}
题目六:每日温度
(链接:https://leetcode-cn.com/problems/daily-temperatures/)
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数
方法一:自己的方法:暴力遍历,两重for循环即可解决
Java源代码:
public class dailytemporature {
public static void main(String[] args) {
int[] T = new int[]{73, 74, 75, 71, 69, 72, 76, 73};
int[] res = new int[T.length];
for (int i = 0; i < T.length; i++) {
int flag = 0;
for (int j = i+1; j < T.length; j++) {
if(T[i]<T[j]){
flag = 1;
res[i] = j-i;
break;
}
}
if(flag == 0){
res[i] = 0;
}
}
for (int i = 0; i < res.length; i++) {
System.out.println(res[i]);
}
}
}
方法二:官方思路:单调栈(递减栈):栈里面存储的是数组的index,而不是数组里面的值,栈不为空的时候,
比较栈顶index对应元素的当前index对应元素的大小。当前index对应元素大的话,表明找到,出栈操作,
返回间隔,找到栈顶index的间隔大小,也就是说不是按照数组顺序依次查找的。
Java源代码:
import java.util.Stack;
public class dailytemporature2 {
public static void main(String[] args) {
int[] T = new int[]{55,38,53,81,61,93,97,32,43,78};
Stack<Integer> stack = new Stack<Integer>();
int[] res = new int[T.length];
for (int i = 0; i < T.length-1; i++) {
int curr = T[i];
while(!stack.isEmpty() && T[i]>T[stack.peek()]){
int index = stack.pop();
res[index] = i-index;
}
stack.push(i);
}
for (int i = 0; i < res.length; i++) {
System.out.println(res[i]);
}
}
}
第二次使用单调栈写的代码
class Solution {
public int[] dailyTemperatures(int[] T) {
Deque<Integer> stack = new LinkedList<Integer>();
int[] res = new int[T.length];
for(int i =0;i<T.length;i++){
while(!stack.isEmpty() && T[stack.peek()]<T[i]){
int index = stack.pop();
res[index] = i - index;
}
stack.push(i);
}
return res;
}
}
题目七:反转没对括号间的字串
(链接:https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/)
给出一个字符串 s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
示例 1:
输入:s = "(abcd)"
输出:"dcba"
示例 2:
输入:s = "(u(love)i)"
输出:"iloveu"
示例 3:
输入:s = "(ed(et(oc))el)"
输出:"leetcode"
示例 4:
输入:s = "a(bcdefghijkl(mno)p)q"
输出:"apmnolkjihgfedcbq"
思路:多次使用栈结构,遇见‘)’就进行操作。
Java源代码
class Solution {
public String reverseParentheses(String S) {
String res="";
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < S.length(); i++) {
Queue<Character> tempqueue = new LinkedList<Character>();
char ch = S.charAt(i);
//System.out.println("此时走到为: "+ch);
if(ch == ')'){
//System.out.println("yes");
while(stack.peek() != '('){
char currch = stack.pop();
tempqueue.add(currch);
}
stack.pop();
//System.out.println("tempqueue: "+tempqueue);
while(!tempqueue.isEmpty()){
char curr2 = tempqueue.poll();
stack.push(curr2);
}
}else{
//System.out.println("ch: "+ch);
stack.push(ch);
}
}
Stack<Character> resq = new Stack<>();
while(!stack.isEmpty()){
resq.push(stack.pop());
}
while (!resq.isEmpty()){
res+=resq.pop();
}
return res;
//return stack;
//System.out.println("stack: "+stack);
}
}
方法二:
采用栈存储字符串中左括号(的位置,每次遇到左括号(,依次进栈,当遇到右括号),把
栈顶元素对应的位置一直到右括号前的位置进行翻转。遍历整个数组结束即可。
class Solution {
public String reverseParentheses(String s) {
StringBuffer res = new StringBuffer();
Deque<Integer> stack = new LinkedList<Integer>();
Deque<Character> stackres = new LinkedList<Character>();
char[] chs = s.toCharArray();
for(int i =0;i<s.length();i++){
if(s.charAt(i) == '('){
stack.push(i);
}
if(s.charAt(i) == ')'){
reversestr(chs,stack.pop()+1,i-1);
}
}
for(int k =0;k<chs.length;k++){
if((chs[k] !='(') && ( chs[k]!= ')')){
res.append(chs[k]);
}
}
return res.toString();
}
public void reversestr(char[] chs,int left,int right){
while(left<right){
char temp = chs[left];
chs[left] = chs[right];
chs[right] = temp;
left++;
right--;
}
}
}