你好,栈(^.^)

简介: 你好,栈(^.^)

励志:🔔半身浸江,全身如林,所谓万丈深渊,下去也是前程万里。我启程了,你呢?

一、LIFO栈

1.逆序链表

题:剑指 Offer 06. 从尾到头打印链表

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

示例 1:

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

限制:

0 <= 链表长度 <= 10000

解:

解题思路:辅助栈
AC代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        while(head != null) {
            stack.push(head.val);
            head = head.next;
        }
        int[] res = new int[stack.size()];
        for(int i = 0; i < res.length; i ++) {
            res[i] = stack.pop();
        }
        return res;
    }
}
  • 时间复杂度:O(N),入栈、出栈使用O(N)时间
  • 空间复杂度:O(N),辅助栈和数组使用额外的O(N)空间

解题思路:递归

AC代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    List<Integer> arr = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        dfs(head);
        return arr.stream().mapToInt(k -> k).toArray();
    }
    void dfs(ListNode node) {
        if(node == null) return; // 递归出口
        dfs(node.next);
        arr.add(node.val);
    } 
}
  • 时间复杂度:O(N)
  • 空间复杂度:O(N)

题:206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

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

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

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

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

示例 3:

输入:head = []
输出:[]

提示:

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解:

解题思路:三人行模板

AC代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null) {
            ListNode node = cur.next;
            cur.next = pre;
            pre = cur;
            cur = node;
        }
        return pre;
    }
}
  • 时间复杂度:O(N),遍历链表,N为链表长度
  • 空间复杂度:O(1),指针消耗常数空间O(1)

解题思路:递归

AC代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}
  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

2.括号匹配

题:20. 有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

示例 4:

输入:s = "([)]"
输出:false

示例 5:

输入:s = "{[]}"
输出:true

提示:

1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成

解:

解题思路:辅助栈

AC代码:

class Solution {
    public boolean isValid(String s) {
        if((s.length() & 1) != 0) return false; // s长度为奇数
        Map<Character, Character> map = new HashMap<>(){{
            put(')', '(');
            put('}', '{');
            put(']', '[');
        }};
        Stack<Character> stack = new Stack<>();
        for(Character ch : s.toCharArray()) {
            if(map.containsKey(ch)) {
                if(stack.isEmpty() || stack.peek() != map.get(ch)) {
                    return false;
                }
                stack.pop();
            }else {
                stack.push(ch);
            }
        }
        return stack.isEmpty(); // 注意栈判空
    }
}
  • 时间复杂度:O(N),N为字符串长度。
  • 空间复杂度:O(N +C),N为栈长,C为字符集。

题:32. 最长有效括号

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

示例 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

示例 3:

输入:s = ""
输出:0

提示:

0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'

解:

📖 解题思路:动态规划

  1. 最长 => 动态规划
  2. dp[i]表示以i结尾的最长有效括号
  3. 情况分类讨论:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

AC代码:

class Solution {
    public int longestValidParentheses(String s) {
        int[] dp = new int[s.length()];
        int maxVal = 0;
        // dp[0] = 0;
        for(int i = 1; i < dp.length; i ++) {
            if(s.charAt(i) == ')') {
                if(s.charAt(i - 1) == '(') {
                    dp[i] = 2; // ()
                    if(i - 2 >= 0) { // ()()
                        dp[i] = dp[i - 2] + 2;
                    }
                }else if(dp[i - 1] > 0) {
                    if(i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') { // (())
                        dp[i] = dp[i - 1] + 2;
                        if((i - dp[i - 1] - 2) >= 0) { // ()(())
                            dp[i] += dp[i - dp[i - 1] - 2];
                        }
                    }
                }

            }
            maxVal = Math.max(maxVal, dp[i]);
        }
        return maxVal;
    }
}
  • 时间复杂度:O(N),N为字符串长度,遍历整个字符串一次,求出dp数组
  • 空间复杂度:O(N),N为数组dp的大小

解题思路:

AC代码:

class Solution {
    public int longestValidParentheses(String s) {
        Stack<Integer> stack = new Stack<>(); // 栈存储字符串下标
        int maxVal = 0;
        stack.push(-1); // 防止栈判空
        for(int i = 0; i < s.length(); i ++) {
            if(s.charAt(i) == '(') {
                stack.push(i);
            }else {
                stack.pop();
                if(stack.isEmpty()) {
                    stack.push(i);
                }else {
                    maxVal = Math.max(maxVal, i - stack.peek());
                }
            }
        }
        return maxVal;
    }
}
  • 时间复杂度:O(N)
  • 空间复杂度:O(N)

3.回文链表

题:剑指 Offer II 027. 回文链表

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

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

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

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

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

提示:

链表 L 的长度范围为 [1, 105]
0 <= node.val <= 9 

进阶:能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解:

解题思路:

  1. 找中点
  2. 反转后半
  3. 比对
  4. 还原(可有可无)

AC代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        // 1.找中点
        ListNode mid = getMid(head);
        // 2.反装后半 123 2 | 1234 2
        ListNode temp = mid.next;
        mid.next = null;
        ListNode B = reverseList(temp);
        // 3.比对
        boolean flag = compareList(head, B);
        // 4.还原
        mid.next = reverseList(B);

        return flag;
    }
    // 链表取中点
    ListNode getMid(ListNode head) {
        ListNode p = head, q = head;
        while(q.next != null && q.next.next != null) {
            p = p.next;
            q = q.next.next;
        }
        return p;
    }
    // 链表反装
    ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null) {
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
    // 链表比较
    boolean compareList(ListNode A, ListNode B) {
        // A可能比B长
        while(B != null) {
            if(A.val != B.val) return false;
            A = A.next;
            B = B.next;
        }
        return true;
    }
}
  • 时间复杂度:O(N),N为链表长度
  • 空间复杂度:O(1),指针消耗常数空间

题:234. 回文链表

题:面试题 02.06. 回文链表

4.表达式求值

题:682. 棒球比赛

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:

  1. 整数 x - 表示本回合新获得分数 x
  2. "+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
  3. "D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
  4. "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" 操作,题目数据保证记录此操作时前面总是存在一个有效的分数

解:

解题思路:

AC代码:

class Solution {
    public int calPoints(String[] ops) {
        Stack<Integer> stack = new Stack<>();
        for(String op : ops) {
            if(op.equals("+")) {
                // 需要得到栈前两个元素,必须保存弹出栈元素
                int a = stack.pop();
                int b = a + stack.peek();
                stack.push(a);
                stack.push(b);
            }else if(op.equals("C")) {
                stack.pop();
            }else if(op.equals("D")) {
                int a = stack.peek();
                stack.push(a * 2);
            }else {
                stack.push(Integer.valueOf(op));
            }
        }
        int ans = 0;
        for(int score : stack) ans += score;
        return ans;
    }
}
  • 时间复杂度:O(N),N是字符串ops的长度。
  • 空间复杂度:O(N),用于存储stack的空间。

5.双栈判等

### 题:844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。

如果相等,返回 true ;否则,返回 false 。

注意:如果对空文本输入退格字符,文本继续为空。

示例 1:

输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。

示例 2:

输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 “”。

示例 3:

输入:s = "a##c", t = "#a#c"
输出:true
解释:s 和 t 都会变成 “c”。

示例 4:

输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 “c”,但 t 仍然是 “b”。 

提示:

  • 1 <= s.length, t.length <= 200
  • s 和 t 只含有小写字母以及字符 '#'

    解:

    解题思路:

    AC代码:

class Solution {

public boolean backspaceCompare(String s, String t) {
    if(s == null || t == null) return false;
    return buildStack(s.toCharArray()).equals(buildStack(t.toCharArray()));
}
public Stack<Character> buildStack(char[] chars) {
    Stack<Character> stack = new Stack<>();
    for(char ch : chars) {
        if(ch == '#') {
            if(!stack.isEmpty()) {
                stack.pop();
            }           
        }else {
            stack.push(ch); 
        }           
    }
    return stack;
}

}

- 时间复杂度:O(N)
- 空间复杂度:O(N)

解题思路:`双指针`

AC代码:

class Solution {

public boolean backspaceCompare(String s, String t) {
    // 双指针法
    int i = s.length() - 1, j = t.length() - 1;
    int s_count = 0;
    int t_count = 0;
    while(i >= 0 || j >= 0) {
        while(i >= 0) {
            if(s.charAt(i) == '#') {
                s_count ++;
                i --;
            }else if(s_count > 0){
                s_count --;
                i --;
            }else {
                break;
            }
        }
        while(j >= 0) {
            if(t.charAt(j) == '#') {
                t_count ++;
                j --;
            }else if(t_count > 0) {
                t_count --;
                j --;
            }else {
                break;
            }
        }            
        if(i >= 0 && j >= 0) {
            if(s.charAt(i) != t.charAt(j)) return false;
        }else {
            if(i >= 0 || j >= 0) return false;
        }
        i --;
        j --;
    }
    return true;
}

}

 - 时间复杂度:O(N)
 - 空间复杂度:O(1)
# 二、单调栈
## 1.最小栈
### 题:155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。 

示例:

    输入:
    ["MinStack","push","push","push","getMin","pop","top","getMin"]
    [[],[-2],[0],[-3],[],[],[],[]]

    输出:
    [null,null,null,null,-3,null,0,-2]

    解释:
    MinStack minStack = new MinStack();
    minStack.push(-2);
    minStack.push(0);
    minStack.push(-3);
    minStack.getMin();   --> 返回 -3.
    minStack.pop();
    minStack.top();      --> 返回 0.
    minStack.getMin();   --> 返回 -2.
 

提示:

    pop、top 和 getMin 操作总是在 非空栈 上调用。

### 解:
解题思路:`递减栈`

AC代码:

class MinStack {

Stack<Integer> stack;
Stack<Integer> min_stack;

public MinStack() {
    stack = new Stack<>();
    min_stack = new Stack<>();
}

public void push(int val) {
    stack.push(val);
    if(min_stack.isEmpty() || val <= min_stack.peek()) {
        min_stack.push(val);
    }
}

public void pop() {
    // 声明成 int 类型,完成自动拆箱,因此下面的比较可以使用 "==" 运算符
    int x = min_stack.peek();
    if(stack.pop() == x) { 
        min_stack.pop();
    }      
}

public int top() {
    return stack.peek();
}

public int getMin() {
    return min_stack.peek();
}

}

/**

  • Your MinStack object will be instantiated and called as such:
  • MinStack obj = new MinStack();
  • obj.push(val);
  • obj.pop();
  • int param_3 = obj.top();
  • int param_4 = obj.getMin();

*/


另解:先将Integer自动拆箱为int,再比较

class MinStack {

Stack<Integer> stack;
Stack<Integer> min_stack;

public MinStack() {
    stack = new Stack<>();
    min_stack = new Stack<>();
}

public void push(int val) {
    stack.push(val);
    if(min_stack.isEmpty() || val <= min_stack.peek()) {
        min_stack.push(val);
    }
}

public void pop() {
    // 或者直接用equals判等
    if(stack.pop().equals(min_stack.peek())) { 
        min_stack.pop();
    }      
}

public int top() {
    return stack.peek();
}

public int getMin() {
    return min_stack.peek();
}

}

/**

  • Your MinStack object will be instantiated and called as such:
  • MinStack obj = new MinStack();
  • obj.push(val);
  • obj.pop();
  • int param_3 = obj.top();
  • int param_4 = obj.getMin();

*/

- 时间复杂度:O(1)
- 空间复杂度:O(N) 两个栈的大小

### 题:面试题 03.02. 栈的最小值
### 题:剑指 Offer 30. 包含min函数的栈
## 2.栈模拟队列
### 题:232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
 

**说明:**

- 你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
 

**进阶:**

- 你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。
 

示例:

    输入:
    ["MyQueue", "push", "push", "peek", "pop", "empty"]
    [[], [1], [2], [], [], []]
    输出:
    [null, null, null, 1, 1, false]

    解释:
    MyQueue myQueue = new MyQueue();
    myQueue.push(1); // queue is: [1]
    myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
    myQueue.peek(); // return 1
    myQueue.pop(); // return 1, queue is [2]
    myQueue.empty(); // return false
 

提示:

    1 <= x <= 9
    最多调用 100 次 push、pop、peek 和 empty
    假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)
### 解:
解题思路:`双栈模拟`

AC代码:

class MyQueue {

Stack<Integer> in;
Stack<Integer> out;

public MyQueue() {
    in = new Stack<>();
    out = new Stack<>();
}

public void push(int x) {
    in.push(x);
}

public int pop() {        
    if(out.isEmpty()) {
        while(!in.isEmpty()) out.push(in.pop());
    }
    return out.pop();   
}

public int peek() {
    if(out.isEmpty()) {
        while(!in.isEmpty()) out.push(in.pop());
    }
    return out.peek(); 
}

public boolean empty() {
    return in.isEmpty() && out.isEmpty(); // 注意这里的优雅写法
}

}

/**

  • Your MyQueue object will be instantiated and called as such:
  • MyQueue obj = new MyQueue();
  • obj.push(x);
  • int param_2 = obj.pop();
  • int param_3 = obj.peek();
  • boolean param_4 = obj.empty();

*/

- 时间复杂度:O(1)
- 空间复杂度:O(N),两个栈in,out所耗空间

### 题:剑指 Offer 09. 用两个栈实现队列
### 题:面试题 03.04. 化栈为队
## 3.辅助栈
### 题:1352. 最后 K 个数的乘积
请你实现一个「数字乘积类」ProductOfNumbers,要求支持下述两种方法:

1.add(int num)

- 将数字 num 添加到当前数字列表的最后面。

2.getProduct(int k)

- 返回当前数字列表中,最后 k 个数字的乘积。
- 你可以假设当前列表中始终 至少 包含 k 个数字。

题目数据保证:任何时候,任一连续数字序列的乘积都在 32-bit 整数范围内,不会溢出。

示例:

    输入:
    ["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"]
    [[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]

    输出:
    [null,null,null,null,null,null,20,40,0,null,32]

    解释:
    ProductOfNumbers productOfNumbers = new ProductOfNumbers();
    productOfNumbers.add(3);        // [3]
    productOfNumbers.add(0);        // [3,0]
    productOfNumbers.add(2);        // [3,0,2]
    productOfNumbers.add(5);        // [3,0,2,5]
    productOfNumbers.add(4);        // [3,0,2,5,4]
    productOfNumbers.getProduct(2); // 返回 20 。最后 2 个数字的乘积是 5 * 4 = 20
    productOfNumbers.getProduct(3); // 返回 40 。最后 3 个数字的乘积是 2 * 5 * 4 = 40
    productOfNumbers.getProduct(4); // 返回  0 。最后 4 个数字的乘积是 0 * 2 * 5 * 4 = 0
    productOfNumbers.add(8);        // [3,0,2,5,4,8]
    productOfNumbers.getProduct(2); // 返回 32 。最后 2 个数字的乘积是 4 * 8 = 32 
 
提示:

    add 和 getProduct 两种操作加起来总共不会超过 40000 次。
    0 <= num <= 100
    1 <= k <= 40000

### 解:
解题思路:`前缀积`

AC代码:

/**

  • 前缀积

*/
class ProductOfNumbers {

List<Integer> preProduct; // 前面的数字乘积
int Vlen; // 列表有效长度

public ProductOfNumbers() {
    preProduct = new ArrayList<>();
    Vlen = 0;
}

public void add(int num) {
    if(num == 0) {
        preProduct.clear();
        Vlen = 0;
    }else {
        if(preProduct.size() == 0) preProduct.add(num);
        else preProduct.add(preProduct.get(Vlen - 1) * num); // 注意下标从0开始
        Vlen ++;
    }
}

public int getProduct(int k) {
    if(Vlen < k) return 0;
    else if(Vlen == k) return preProduct.get(Vlen - 1);
    return preProduct.get(Vlen - 1) / preProduct.get(Vlen - 1 - k);  // 12345,345,k=3,=>12345/12
}

}

/**

  • Your ProductOfNumbers object will be instantiated and called as such:
  • ProductOfNumbers obj = new ProductOfNumbers();
  • obj.add(num);
  • int param_2 = obj.getProduct(k);

*/

- 时间复杂度:O(1)
- 空间复杂度:O(N),额外的一个辅助列表
## 4.最大矩形
### 题:84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

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

 

示例 1:
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/6d51b8759c7843eca36e32a5483ee444.png)

    输入:heights = [2,1,5,6,2,3]
    输出:10
    解释:最大的矩形为图中红色区域,面积为 10
示例 2:
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/83d41d27a8874f10ad935687072a1e43.png)

    输入: heights = [2,4]
    输出: 4

提示:

- 1 <= heights.length <=105
- 0 <= heights[i] <= 104
### 解:
解题思路:`单调栈`

![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/62f96e8e543d44288fc1058cb8c0dd86.png)
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/cf4d85d54ca54771bbc33aeb08ac9c9e.png)

![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/dc389eb354d04501a7985e4696dbfe23.png)
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/589baa360b0b4b7685cd52a3ef48162e.png)
单调栈模板:

![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/a0b2c5a596b04f718958bce809b028d4.png)

/**

  • 单调栈模板

*/
while(!stack.isEmpty() && stack.peek() < or > target) {

    stack.pop();

}
stack.push(i);



AC代码:

class Solution {

public int largestRectangleArea(int[] heights) {
    int res = 0;
    Stack<Integer> stack = new Stack<>();
    int[] newHeights = new int[heights.length + 2];
    newHeights[0] = 0;
    newHeights[newHeights.length - 1] = 0;
    for(int i = 1; i < newHeights.length - 1; i ++) {
        newHeights[i] = heights[i - 1];
    }
    // 单增栈
    for(int i = 0; i < newHeights.length; i ++) {
        // 栈不为空
        while(!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
            int height = newHeights[stack.pop()];
            int left = stack.peek();
            int right = i;
            res = Math.max(res, height * (right - left - 1));
        }
        stack.push(i);
    }
    return res;
}

}

- 时间复杂度:O(N)
- 空间复杂度:O(N),newHeights数组、stack栈额外占用N空间
相关文章
|
22天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
109 9
|
13天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
21 1
|
16天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
19天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
21天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
33 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
25天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
初步认识栈和队列
初步认识栈和队列
61 10
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
20 1
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
48 3