Leetcode_栈、队列

每日温度

1 题目描述

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

提示

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

2 解题(Java)

单调栈

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int len = temperatures.length;
        int[] res = new int[len];
        Deque<Integer> stack = new LinkedList<>();

        for (int i=0; i<len; i++) {
            while (!stack.isEmpty() && temperatures[i]>temperatures[stack.peek()]) {
                int top = stack.pop();
                res[top] = i - top;
            }
            stack.push(i);
        }
        
        return res;
    }
}

3 复杂性分析

  • 时间复杂度:O(n),其中 n 是温度列表的长度。正向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作;
  • 空间复杂度:O(n),其中 n 是温度列表的长度。需要维护一个单调栈存储温度列表中的下标;

字符串解码

1 题目描述

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1

输入:s = “3[a]2[bc]”
输出:“aaabcbc”

示例 2

输入:s = “3[a2[c]]”
输出:“accaccacc”

示例 3

输入:s = “2[abc]3[cd]ef”
输出:“abcabccdcdcdef”

示例 4

输入:s = “abc3[cd]xyz”
输出:“abccdcdcdxyz”

2 解题(Java)

2.1 解法一:栈

本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。

解题思路

  1. 构建辅助栈 stack, 遍历字符串 s 中每个字符 c;

    • 当 c 为数字时,将数字字符转化为数字 multi,用于后续倍数计算;
    • 当 c 为字母时,在 res 尾部添加 c;
    • 当 c 为 [ 时,将当前 multi 和 res 入栈,并分别置空:
      • 记录此 [ 前的倍数 multi 至栈,用于发现对应 ] 后,获取 multi × […] 字符串。
      • 记录此 [ 前的临时结果 res 至栈,用于发现对应 ] 后的拼接操作;
      • 进入到新 [ 后,res 和 multi 重新记录。
    • 当 c 为 ] 时,stack 出栈,拼接字符串 res = last_res + cur_multi * res,其中:
      • cur_multi是当前 [ 到 ] 内字符串的重复倍数,例如 “3[a2[c]]” 中的 2。
      • last_res是上个 [ 到当前 [ 的字符串,例如 “3[a2[c]]” 中的 a;
  2. 返回字符串 res。

class Solution {
    public String decodeString(String s) {
        StringBuilder res = new StringBuilder();
        int multi = 0;
        Deque<Integer> multiStack = new LinkedList<>();
        Deque<String> resStack = new LinkedList<>();
        for(char c : s.toCharArray()) {
            if(c == '[') {
                multiStack.push(multi);
                resStack.push(res.toString());
                multi = 0;
                res = new StringBuilder();
            }
            else if(c == ']') {
                StringBuilder tmp = new StringBuilder();
                int curMulti = multiStack.pop();
                for(int i = 0; i < curMulti; i++) tmp.append(res);
                res = new StringBuilder(resStack.pop() + tmp.toString());
            }
            else if(Character.isDigit(c)) multi = multi * 10 + Integer.parseInt(c + "");
            else res.append(c);
        }
        return res.toString();
    }
}

复杂性分析

  • 时间复杂度:O(N),遍历一次 s;
  • 空间复杂度:O(N),辅助栈在极端情况下需要线性空间,例如 2[2[2[a]]];

2.2 解法二:分治

解题思路

  • 当s[i]是数字时,计算multi;
  • 当s[i] == ‘[’ 时,开启新一层递归,得到 […]的结果,并拼接到res后面;
  • 当s[i]为字符时,直接拼接到res后面;
  • 遍历完毕后返回 res;
class Solution {
    public String decodeString(String s) {
        StringBuilder res = new StringBuilder();
        int multi = 0;
        for (int i=0; i<s.length(); i++) {
            if (Character.isDigit(s.charAt(i))) {
                multi = multi * 10 + Integer.parseInt(s.charAt(i) + "");
            }
            else if (s.charAt(i) == '[') {
                int j = i + 1;
                int counterPartition = 1;
                while (counterPartition > 0) {
                    if (s.charAt(j) == '[') counterPartition++;
                    else if (s.charAt(j) == ']') counterPartition--;
                    j++;
                }
                String temp = decodeString(s.substring(i+1, j-1));
                while (multi > 0) {
                    res.append(temp);
                    multi--;
                }
                i = j - 1;
            }
            else {
                while (i < s.length() && !Character.isDigit(s.charAt(i))) {
                    res.append(s.charAt(i) + "");
                    i++;
                }
                i--;
            }
        }
        return res.toString();
    }
}

复杂性分析

  • 时间复杂度:O(N),遍历一次 s;
  • 空间复杂度:O(N),极端情况下递归深度将会达到线性级别;

最大矩形

1 题目描述

给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例 1

在这里插入图片描述

输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“0”]]
输出:6
解释:最大矩形如上图所示。

示例 2

输入:matrix = []
输出:0

示例 3

输入:matrix = [[“0”]]
输出:0

示例 4

输入:matrix = [[“1”]]
输出:1

示例 5

输入:matrix = [[“0”,“0”]]
输出:0

提示

  • rows == matrix.length
  • cols == matrix[0].length
  • 0 <= row, cols <= 200
  • matrix[i][j] 为 ‘0’ 或 ‘1’

2 解题(Java)

class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0) return 0;
        int row = matrix.length; 
        int col = matrix[0].length;

        int res = 0; 
        Deque<Integer> mono_stack = new LinkedList<>(); 
        int[] heights = new int[col];  
        int[] left = new int[col]; 
        int[] right = new int[col]; 

        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if (matrix[i][j] == '1') heights[j]++;
                else heights[j] = 0;
            }

            for(int j = 0; j < col; j++) {
                while(!mono_stack.isEmpty() && heights[j] <= heights[mono_stack.peek()]) {
                    mono_stack.pop();
                }
                left[j] = mono_stack.isEmpty() ? -1 : mono_stack.peek();
                mono_stack.push(j);
            }
            
            mono_stack.clear();

            for (int j = col - 1; j >= 0; j--) {
                while(!mono_stack.isEmpty() && heights[j] <= heights[mono_stack.peek()]) {
                    mono_stack.pop();
                }
                right[j] = mono_stack.isEmpty() ? col : mono_stack.peek();
                mono_stack.push(j);
            }

            mono_stack.clear();

            for (int j = 0; j < col; j++) {
                res = Math.max(res, (right[j] - left[j] - 1) * heights[j]);
            }
        }
        return res;
    }
}

3 复杂性分析

  • 时间复杂度O(mn):for循环使用O(mn)时间;输入数组里的每一个元素入栈一次,出栈一次,使用O(mn)时间;
  • 空间复杂度O(n):新建数组以及单调栈各自占用O(N)空间;

滑动窗口的最大值

1 题目描述

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:
在这里插入图片描述

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

2 解题(Java)

2.1 解题思路

在这里插入图片描述
本题难点: 如何在每次窗口滑动后,将 “获取窗口内最大值” 的时间复杂度从 O(k) 降低至 O(1);

回忆 包含min函数的栈,其使用 单调栈 实现了随意入栈、出栈情况下的 O(1) 时间获取 “栈内最小值” ,本题同理。

窗口对应的数据结构为 双端队列 ,本题使用 单调队列 即可解决以上问题。遍历数组时,每轮保证单调非严格递减队列 deque:

  1. deque 内仅包含窗口内的元素 ⇒ 每轮窗口滑动移除了元素 nums[left−1] ,如果上一个窗口[left-1,right-1]最大值deque.peekFirst()等于nums[left-1],还需删除deque的队头元素;
  2. deque 内的元素 非严格递减 ⇒ 每轮窗口滑动添加了元素 nums[right + 1],需将 deque 内所有 < nums[right + 1] 的元素删除;

2.2 算法流程

  1. 初始化: 双端队列deque ,结果列表res,数组长度 n;
  2. 滑动窗口: 左边界范围 left∈[1−k,n−k] ,右边界范围 right∈[0,n−1] ;
    1. 若 left > 0 且 队首元素 deque[0] == 被删除元素 nums[left - 1]:则队首元素出队;
    2. 删除 deque 内所有<nums[right] 的元素,以保持 deque 非严格递减;
    3. 将 nums[right] 添加至 deque 尾部;
    4. 若已形成窗口(left≥0 ):将窗口最大值(即队首元素 deque[0])添加至数组 res;
  3. 返回值: 返回结果列表 res ;

2.3 代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for (int right=0, left=1-k; right<nums.length; left++, right++) {
            // 保持deque非严格递减
            while (!deque.isEmpty() && deque.peekLast() < nums[right]) {
                deque.pollLast();
            }
            deque.offer(nums[right]);
            if (left >= 0) {
                res[left] = deque.peek();// 记录窗口最大值
            }
            // 如果窗口[left,right]最大值恰好就是nums[left],右移之后最大值作废,所以删除队列头
            if (left >= 0 && deque.peek() == nums[left]) {
                deque.poll(); 
            }
        }
        return res;
    }
}

3 复杂性分析

  • 时间复杂度 O(N) : 其中 N 为数组 nums 长度;线性遍历 nums 占用 O(N);每个元素最多仅入队和出队一次,因此单调队列 deque 占用 O(2N) ;
  • 空间复杂度 O(k) : 双端队列 deque 中最多同时存储 k 个元素(即窗口大小);

字符串变形

1 题目描述

对于一个给定的字符串,我们需要在线性(也就是O(n))的时间里对它做一些变形。首先这个字符串中包含着一些空格,就像"Hello World"一样,然后我们要做的是把着个字符串中由空格隔开的单词反序,同时反转每个字符的大小写。比如"Hello World"变形后就变成了"wORLD hELLO"。

输入描述:

给定一个字符串s以及它的长度n(1≤n≤500)

返回值描述:

请返回变形后的字符串。题目保证给定的字符串均由大小写字母和空格构成。

示例1

输入

“This is a sample”,16

返回值

“SAMPLE A IS tHIS”

2 解题(Java)

public class Solution {
    public String trans(String s, int n) {
        String res = "";
        String tempStr = "";
        for(int i = 0; i < n; i++){
            char c = s.charAt(i);
            if(Character.isLowerCase(c))
                tempStr += Character.toUpperCase(c);
            else if(Character.isUpperCase(c))
                tempStr += Character.toLowerCase(c);
            else{
                tempStr = c + tempStr;
                res = tempStr + res;
                tempStr = "";
            }
        }
        res = tempStr + res;
        return res;
    }
}

3 复杂性分析

  • 时间复杂度O(N):字符串的长度为N,线性遍历字符串的时间复杂度为O(N);
  • 空间复杂度O(N):临时字符串tempstr最多占用O(N)空间;

表达式求值

1 题目描述

请写一个整数计算器,支持加减乘三种运算和括号。

示例1

输入

“1 + 2”

返回值

3

示例2

输入

“( 2 * ( 3 - 4 ) ) * 5”

返回值

-10

示例3

输入

“3 + 2 * 3 * 4 - 1”

返回值

26

2 解题(Java)

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回表达式的值
     * @param s string字符串 待计算的表达式
     * @return int整型
     */
    public int solve(String s) {
    	s = s.trim();
        Deque<Integer> stack = new LinkedList<>();
        int number = 0;
        char sign = '+';
        char[] charArray = s.toCharArray();
        for (int i = 0; i < charArray.length; i++) {
            char c = charArray[i];
            if (c == ' ') {
                continue;
            }
            if (Character.isDigit(c)) {
                number = number * 10 + c - '0';
            }
            if (c == '(') {
                int j = i + 1;
                int counterPartition = 1;
                while (counterPartition > 0) {
                    if (charArray[j] == '(') {
                        counterPartition++;
                    }
                    if (charArray[j] == ')') {
                        counterPartition--;
                    }
                    j++;
                }
                number = solve(s.substring(i + 1, j - 1));
                i = j - 1;
            }
            if (!Character.isDigit(c) || i == charArray.length - 1) {
                if (sign == '+') {
                    stack.push(number);
                } else if (sign == '-') {
                    stack.push(-1 * number);
                } else if (sign == '*') {
                    stack.push(stack.pop() * number);
                } else if (sign == '/') {
                    stack.push(stack.pop() / number);
                }
                number = 0;
                sign = c;
            }
        }
        int ans = 0;
        while (!stack.isEmpty()) {
            ans += stack.pop();
        }
        return ans;
    }
}

3 复杂性分析

  • 时间复杂度O(N):N为字符串长度,需线性遍历一遍字符串;
  • 空间复杂度O(N):N为字符串长度,栈中存储的元素数量不超过N;

基本计算器

1 题目描述

实现一个基本的计算器来计算一个简单的字符串表达式 s 的值。

示例 1

输入:s = “1 + 1”
输出:2

示例 2

输入:s = " 2-1 + 2 "
输出:3

示例 3

输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23

提示

  • 1 <= s.length <= 3 * 105
  • s 由数字、‘+’、‘-’、‘(’、‘)’、和 ’ ’ 组成
  • s 表示一个有效的表达式

2 解题(Java)

class Solution {
    public int calculate(String s) {
        s = s.trim();
        int sign = 1, res = 0;
        Deque<Integer> stack = new LinkedList<>();
        stack.push(sign);
        for (int i=0; i<s.length(); i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                continue;
            }
            else if (Character.isDigit(c)) {
                int num = 0;
                while (i < s.length() && Character.isDigit(s.charAt(i))) {
                    num = num * 10 + s.charAt(i) - '0';
                    i++;
                }
                res += sign * num;
                i--;
            }
            else if (c == '+') {
                sign = stack.peek();
            }
            else if (c == '-') {
                sign = -stack.peek();
            }
            else if (c == '(') {
                stack.push(sign);
            }
            else if (c == ')') {
                stack.pop();
            }
        }
        return res;
    }
}

3 复杂性分析

  • 时间复杂度O(N):N为字符串s的长度,线性遍历s一次的时间复杂度为O(N);
  • 空间复杂度O(N):N为字符串s的长度,空间复杂度取决于栈的空间,栈中元素数量不超过N;

从尾到头打印链表

1 题目描述

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例

输入:head = [1,3,2]
输出:[2,3,1]

2 解题(Java)

2.1 解题思路

  1. 链表是从头到尾访问每个结点;
  2. 题目要求倒序输出结点值;
  3. 这种先入后出的需求可以借助栈来实现;

2.2 算法流程

  1. 入栈:遍历链表,将各结点值push入栈;
  2. 出栈:将各结点值pop出栈,存储于数组中;
  3. 返回数组;

2.3 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Deque<Integer> stack = new LinkedList<>();
        while (head != null) {
            stack.push(head.val);
            head = head.next;
        }
        int[] res = new int[stack.size()];
        int i = 0;
        while (!stack.isEmpty()) res[i++] = stack.pop();
        return res;
    }
}

3 复杂性分析

  • 时间复杂度O(N):入栈和出栈共使用O(N)时间;
  • 空间复杂度O(N):辅助栈stack使用O(N)的额外空间;

简化路径

1 题目描述

以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。

请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。

示例 1

输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。

示例 2

输入:"/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。

示例 3

输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。

示例 4

输入:"/a/./b/../../c/"
输出:"/c"

示例 5:

输入:"/a/../../b/../c//.//"
输出:"/c"

示例 6:

输入:"/a//bc/d//././/.."
输出:"/a/b/c"

2 解题(Java)

  1. 用栈的思想来解决。
  2. 对于Deque接口,栈的应用是队头插队头删,队列的应用是队尾插队头删。
class Solution {
    public String simplifyPath(String path) {
        Deque<String> stack = new LinkedList<>();
        for (String item : path.split("/")) {
            if (item.equals("..")) {
                if (!stack.isEmpty()) {
                    stack.pop();
                }
            } else if (!item.equals("") && !item.equals(".")) {
                stack.push(item);
            }
        }
        String res = "";
        while (!stack.isEmpty()) res = "/" + stack.pop() + res;
        return res.length() == 0 ? "/" : res;
    }
}

3 复杂性分析

  • 时间复杂度O(N):遍历字符串数组和栈使用线性时间;
  • 空间复杂度O(N):字符串数组和栈占用线性空间;

有效的括号

1 题目描述

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 注意空字符串可被认为是有效字符串。

示例 1:

输入: “()”
输出: true

示例 2:

输入: “()[]{}”
输出: true

示例 3:

输入: “(]”
输出: false

示例 4:

输入: “([)]”
输出: false

2 解题(Java)

栈和哈希表:

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        Map<Character, Character> pairs = new HashMap<>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque<Character> stack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

3 复杂性分析

  • 时间复杂度 O(N):需要遍历一遍 s;
  • 空间复杂度 O(N):栈使用线性的空间大小;

柱状图中最大的矩形

1 题目描述

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

在这里插入图片描述

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

在这里插入图片描述
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

输入: [2,1,5,6,2,3]
输出: 10

2 解题(Java)

单调栈(严格递增)+哨兵:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];
        int[] right = new int[n];
        
        Deque<Integer> monoStack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            while (!monoStack.isEmpty() && heights[i] <= heights[monoStack.peek()]) {
                monoStack.pop();
            }
            left[i] = monoStack.isEmpty() ? -1 : monoStack.peek();
            monoStack.push(i);
        }

        monoStack.clear();
        for (int i = n - 1; i >= 0; i--) {
            while (!monoStack.isEmpty() && heights[i] <= heights[monoStack.peek()]) {
                monoStack.pop();
            }
            right[i] = monoStack.isEmpty() ? n : monoStack.peek();
            monoStack.push(i);
        }
        
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
}

3 复杂性分析

  • 时间复杂度O(N):for循环使用O(N)时间;输入数组里的每一个元素入栈一次,出栈一次,使用O(N)时间;
  • 空间复杂度O(N):新建数组以及单调栈各自占用O(N)空间;

包含min函数的栈

1 题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack(); minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.

提示

各函数的调用总次数不超过 20000 次

2 解题(Java)

class MinStack {
    private Deque<Integer> contentStack;
    private Deque<Integer> minStack;
    
    public MinStack() {
        contentStack = new LinkedList<>();
        minStack = new LinkedList<>();
    }
    
    public void push(int x) {
        contentStack.push(x);
        if(minStack.isEmpty()) minStack.push(x);
        else minStack.push(Math.min(x, minStack.peek()));
    }
    
    public void pop() {
        contentStack.pop();
        minStack.pop();
    }
    
    public int top() {
        return contentStack.peek();
    }
    
    public int min() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof

3 复杂性分析

  • 时间复杂度 O(1): push(), pop(), top(), min() 四个函数的时间复杂度均为常数级别;
  • 空间复杂度 O(N): 辅助栈使用 O(N) 额外空间;

栈的压入、弹出序列

1 题目描述

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

示例 1

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4, push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

示例 2

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

提示

  1. 0 <= pushed.length == popped.length <= 1000
  2. 0 <= pushed[i],popped[i] < 1000
  3. pushed 是 popped 的排列。

2 解题(Java)

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Deque<Integer> stack = new LinkedList<>();
        int i = 0;
        for(int num : pushed) {
            stack.push(num); // num 入栈
            while(!stack.isEmpty() && stack.peek() == popped[i]) { // 循环判断与出栈
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

3 复杂性分析

  • 时间复杂度O(N):其中N为数组pushed的长度,每个元素最多入栈与出栈一次,即最多共2N次出入栈操作;
  • 空间复杂度O(N):辅助栈stack最多同时存储N个元素;

队列的最大值

1 题目描述

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入: 
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]

示例 2:

输入: 
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]

限制:

  • 1 <= push_back,pop_front,max_value的总操作数 <= 10000
  • 1 <= value <= 10^5

2 解题(Java)

class MaxQueue {
	// data_queue为队列
    Deque<Integer> data_queue;
    // max_deque为非严格递减的双端队列
    Deque<Integer> max_deque;

    public MaxQueue() {
        data_queue = new LinkedList<>();
        max_deque = new LinkedList<>();
    }
    
    public int max_value() {
        if (max_deque.isEmpty()) {
            return -1;
        }
        return max_deque.peek();
    }
    
    public void push_back(int value) {
    	// 维护max_deque从头到尾非严格递减,这样max_deque的队头元素始终代表队列的最大值
        while (!max_deque.isEmpty() && max_deque.peekLast() < value) {
            max_deque.pollLast();
        }
        max_deque.offer(value);
        // data_queue无论如何加入元素
        data_queue.offer(value);
    }
    
    public int pop_front() {
        if (data_queue.isEmpty()) {
            return -1;
        }
        // 如果data_queue队头元素等于max_deque队头元素,让max_deque队头元素出队列,否则不出
        if (data_queue.peek().equals(max_deque.peek())) {
            max_deque.poll();
        }
        // data_queue队头元素无论如何出队列
        return data_queue.poll();
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

3 复杂性分析

  • 时间复杂度O(1):删除操作与求最大值操作显然只需要 O(1) 的时间。而插入操作虽然看起来有循环,做一个插入操作时最多可能会有 n 次出队操作。但要注意,由于每个数字只会出队一次,因此对于所有的 n 数字的插入过程,对应的所有出队操作也不会大于 n 次。因此将出队的时间均摊到每个插入操作上,时间复杂度为 O(1)。
  • 空间复杂度O(N):需要用队列存储所有插入的元素。

用两个栈实现队列

1 题目要求

用两个栈实现一个队列。

队列的声明如下:

请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

2 代码实现

class CQueue {
    Deque<Integer> stack1;
    Deque<Integer> stack2;
    public CQueue() {
        stack1 = new LinkedList();
        stack2 = new LinkedList();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if (stack2.isEmpty()){
            while (!stack1.isEmpty()) stack2.push(stack1.pop());
        }
        if (stack2.isEmpty()) {
            return -1;
        } else {
            return stack2.pop();
        }
    }
}

3 复杂性分析

  • 时间复杂度O(1):插入不多说,对于删除操作,由于每个元素只会至多被插入和弹出 stack2 一次,因此均摊下来每个元素被删除的时间复杂度仍为 O(1);
  • 空间复杂度O(N):需要使用两个栈存储已有的元素;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hellosc01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值