6.栈(六)

题目汇总:https://leetcode-cn.com/tag/stack/

1130. 叶值的最小代价生成树中等(不打算做了)

1190. 反转每对括号间的子串中等[✔]

1209. 删除字符串中的所有相邻重复项 II中等[✔]

1249. 移除无效的括号中等[✔]

1381. 设计一个支持增量操作的栈中等(没有做)

1410. HTML 实体解析器中等(不做了)

1441. 用栈操作构建数组简单[✔]

1541. 平衡括号字符串的最少插入次数中等(评论区的代码)

1544. 整理字符串简单[✔]

剑指 Offer 09. 用两个栈实现队列简单[✔]

1190. 反转每对括号间的子串中等[✔]

给出一个字符串 s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。注意,您的结果中 不应 包含任何括号。
示例 1:
输入:s = "(abcd)",输出:"dcba"
示例 2:
输入:s = "(ed(et(oc))el)",输出:"leetcode"
示例 3:
输入:s = "a(bcdefghijkl(mno)p)q",输出:"apmnolkjihgfedcbq"
提示:

  • 0 <= s.length <= 2000
  • s 中只有小写英文字母和括号
  • 我们确保所有括号都是成对出现的
思路:

使用栈去匹配左括号,不断的反转两个括号之间的内容,最后输出的时候省略掉左括号和右括号。

class Solution {//执行用时:1 ms, 在所有 Java 提交中击败了99.78%的用户,2020/08/09
    public String reverseParentheses(String s) {
        Stack stack = new Stack<>();
        char[] ch = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            if(ch[i] == '('){
                stack.push(i);//遇到左括号就把左括号的索引入栈
            }else if(ch[i] == ')'){
                reverse(ch, stack.pop() + 1, i - 1);//反转左右括号之间的内容
            }
        }

        for(char c : ch){
            if(c != '(' && c != ')'){//忽略左括号和右括号输出结果
                sb.append(c);
            }
        }
    return sb.toString();
    }

    public void reverse(char[] arr, int left, int right){
        while(right > left){
            char temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
        
    }
}

1209. 删除字符串中的所有相邻重复项 II中等[✔]

相似题目:1047. 删除字符串中的所有相邻重复项
给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
在执行完所有删除操作后,返回最终得到的字符串。
本题答案保证唯一。
示例 1:
输入:s = "abcd", k = 2
输出:"abcd"
解释:没有要删除的内容。
示例 2:
输入:s = "deeedbbcccbdaa", k = 3
输出:"aa"
解释: 先删除 "eee" 和 "ccc",得到 "ddbbbdaa",再删除 "bbb",得到 "dddaa",最后删除 "ddd",得到 "aa"
示例 3:
输入:s = "pbbcggttciiippooaais", k = 2
输出:"ps"
提示:

  • 1 <= s.length <= 10^5
  • 2 <= k <= 10^4
  • s 中只含有小写英文字母。
思路:

将字符与该字符的计数都存储在栈中,当栈为空或者栈顶元素不等于当前元素时,将该元素入栈,计数为1。如果当前字符与栈顶元素相同,则栈顶元素计数加 1。如果栈顶元素计数等于 k,则弹出栈顶元素。最后根据留在栈中的元素及其计数构建结果字符串。

//2020/08/09
class Solution {//执行用时:14 ms, 在所有 Java 提交中击败了64.19%的用户
     class Node{
        public char val;
        public int count;
        public Node(char val, int count){
            this.val = val;
            this.count = count;
        }
    }
    public String removeDuplicates(String s, int k) {
        Stack stack = new Stack<>();
        for(int i = 0; i < s.length(); i++){
            char ch = s.charAt(i);
            if(stack.isEmpty() || stack.peek().val != ch){
                stack.push(new Node(ch, 1));
            }else{
                if(++stack.peek().count == k){
                    stack.pop();
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        while(!stack.isEmpty()){
            Node n = stack.pop();
            for(int j = 0; j < n.count; j++)
                sb.append(n.val);
        }
        return sb.reverse().toString();
    }
}

1249. 移除无效的括号中等[✔]

给你一个由 '('')' 和小写字母组成的字符串 s
你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 **任意一条 **要求:
空字符串或只包含小写字母的字符串
可以被写作 ABA 连接 B)的字符串,其中 AB 都是有效「括号字符串」
可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」
示例 1:
输入:s = "lee(t(c)o)de)"
输出:"lee(t(c)o)de"
解释:"lee(t(co)de)" , "lee(t(c)ode)" 也是一个可行答案。
示例 2:
输入:s = "a)b(c)d"
输出:"ab(c)d"
示例 3:
输入:s = "))(("
输出:""
解释:空字符串也是有效的
示例 4:
输入:s = "(a(b(c)d)"
输出:"a(b(c)d)"
提示:

  • 1 <= s.length <= 10^5
  • s[i] 可能是 '('')' 或英文小写字母
思路:

移除无效的括号要先找到它,栈用来存放所有遍历到的左括号的索引,数组用来标记无效括号的索引。

//2020/08/09
class Solution {//执行用时:18 ms, 在所有 Java 提交中击败了89.87%的用户
    public String minRemoveToMakeValid(String s) {
        Deque stack = new ArrayDeque<>();//存放所有遍历到的左括号的索引
        boolean[] isInvalidIndex = new boolean[s.length()];//标记无效括号的索引
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            char ch = s.charAt(i);
            if(ch == '('){
                stack.push(i);//左括号入栈
                isInvalidIndex[i] = true;//标记为无效的
            }else if(ch == ')'){
                if(!stack.isEmpty()){
                    isInvalidIndex[stack.pop()] = false;//'('出栈,更新标记为有效的
                }else{
                    isInvalidIndex[i] = true;//标记为无效的
                }
            }else{
                isInvalidIndex[i] = false;//除了括号之外的字母标记为有效的
            }
        }
        for (int i = 0; i < s.length(); i++) {
            if (!isInvalidIndex[i]) {//有效的索引最后才组合
                res.append(s.charAt(i));
            }
        }
        return res.toString();


    }
}


1381. 设计一个支持增量操作的栈中等

请你设计一个支持下述操作的栈。
实现自定义栈类 CustomStack

  • CustomStack(int maxSize):用 maxSize 初始化对象,maxSize 是栈中最多能容纳的元素数量,栈在增长到 maxSize 之后则不支持 push 操作。
  • void push(int x):如果栈还未增长到 maxSize ,就将 x 添加到栈顶。
  • int pop():弹出栈顶元素,并返回栈顶的值,或栈为空时返回 -1
  • void inc(int k, int val):栈底的 k 个元素的值都增加 val 。如果栈中元素总数小于 k ,则栈中的所有元素都增加 val

示例:
输入:
["CustomStack","push","push","pop","push","push","push","increment","increment","pop","pop","pop","pop"]
[[3],[1],[2],[],[2],[3],[4],[5,100],[2,100],[],[],[],[]]
输出:
[null,null,null,2,null,null,null,null,null,103,202,201,-1]
解释:
CustomStack customStack = new CustomStack(3); // 栈是空的 []
customStack.push(1); // 栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.pop(); // 返回 2 --> 返回栈顶值 2,栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.push(3); // 栈变为 [1, 2, 3]
customStack.push(4); // 栈仍然是 [1, 2, 3],不能添加其他元素使栈大小变为 4
customStack.increment(5, 100); // 栈变为 [101, 102, 103]
customStack.increment(2, 100); // 栈变为 [201, 202, 103]
customStack.pop(); // 返回 103 --> 返回栈顶值 103,栈变为 [201, 202]
customStack.pop(); // 返回 202 --> 返回栈顶值 202,栈变为 [201]
customStack.pop(); // 返回 201 --> 返回栈顶值 201,栈变为 []
customStack.pop(); // 返回 -1 --> 栈为空,返回 -1
提示:

  • 1 <= maxSize <= 1000
  • 1 <= x <= 1000
  • 1 <= k <= 1000
  • 0 <= val <= 100
  • 每种方法 incrementpush 以及 pop 分别最多调用 1000

1441. 用栈操作构建数组简单[✔]

给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3..., n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1n 之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。
示例 1:
输入:target = [1,3], n = 3
输出:["Push","Push","Pop","Push"]
解释: 读取 1 并自动推入数组 -> [1],读取 2 并自动推入数组,然后删除它 -> [1]
,读取 3 并自动推入数组 -> [1,3]
示例 2:
输入:target = [1,2,3], n = 3
输出:["Push","Push","Push"]
示例 3:
输入:target = [1,2], n = 4
输出:["Push","Push"]
解释:只需要读取前 2 个数字就可以停止。
提示:

  • 1 <= target.length <= 100
  • 1 <= target[i] <= 100
  • 1 <= n <= 100
  • target 是严格递增的
思路:

只有两种情况:["Push","Pop"] 和 "Push"

class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户,//2020/08/09
    public List buildArray(int[] target, int n) {
        List res = new ArrayList<>();
        int j = 1;
        for(int i = 0; i < target.length; i++){
            while(j < target[i]){
                res.add("Push");
                res.add("Pop");
                j++;
            }
            if(target[i] == j){
                res.add("Push");
                j++;
            }
        }
    return res;
    }
}

1541. 平衡括号字符串的最少插入次数中等

给你一个括号字符串 s ,它只包含字符 '('')' 。一个括号字符串被称为平衡的当它满足:
任何左括号 '(' 必须对应两个连续的右括号 '))'
左括号 '(' 必须在对应的连续两个右括号 '))' 之前。
比方说 "())""())(())))""(())())))" 都是平衡的, ")()""()))""(()))" 都是不平衡的。
你可以在任意位置插入字符 '(' 和 ')' 使字符串平衡。
请你返回让 s 平衡的最少插入次数。
示例 1:
输入:s = "(()))"
输出:1
解释:第二个左括号有与之匹配的两个右括号,但是第一个左括号只有一个右括号。我们需要在字符串结尾额外增加一个 ')' 使字符串变成平衡字符串 "(())))" 。
示例 2:
输入:s = "())"
输出:0
解释:字符串已经平衡了。
示例 3:
输入:s = "))())("
输出:3
解释:添加 '(' 去匹配最开头的 '))' ,然后添加 '))' 去匹配最后一个 '(' 。
示例 4:
输入:s = "(((((("
输出:12
解释:添加 12 个 ')' 得到平衡字符串。
示例 5:
输入:s = ")))))))"
输出:5
解释:在字符串开头添加 4 个 '(' 并在结尾添加 1 个 ')' ,字符串变成平衡字符串 "(((())))))))" 。
提示:

  • 1 <= s.length <= 10^5
  • s 只包含 '('')'
思路:
class Solution {
    public int minInsertions(String s) {
        Stack stack = new Stack<>();
        int left = 0;
        int ans = 0;
        while(left < s.length()){
            while(left < s.length() && s.charAt(left) == '('){
                stack.push('(');
                left++;
            }
            int cnt = 0;
            while(left < s.length() && s.charAt(left) == ')'){
                cnt++;
                if(cnt >= 2 && !stack.isEmpty()){
                    cnt -= 2;
                    stack.pop();
                }
                left++;
            }
            if(cnt != 0){
                if(!stack.isEmpty()){
                    ans++;
                    stack.pop();
                }else{
                    if(cnt % 2 == 0) ans += cnt/2;
                    else ans += (cnt-1)/2+2;
                }
            }
        }
        ans += stack.size()*2;
        return ans;

    }
}

1544. 整理字符串简单[✔]

给你一个由大小写英文字母组成的字符串 s
一个整理好的字符串中,两个相邻字符 s[i]s[i + 1] 不会同时满足下述条件:
0 <= i <= s.length - 2
s[i] 是小写字符,但 s[i + 1] 是相同的大写字符;反之亦然
请你将字符串整理好,每次你都可以从字符串中选出满足上述条件的 两个相邻 字符并删除,直到字符串整理好为止。
请返回整理好的 字符串 。题目保证在给出的约束条件下,测试样例对应的答案是唯一的。
注意:空字符串也属于整理好的字符串,尽管其中没有任何字符。
示例 1:
输入:s = "leEeetcode"
输出:"leetcode"
解释:无论你第一次选的是 i = 1 还是 i = 2,都会使 "leEeetcode" 缩减为 "leetcode" 。
示例 2:
输入:s = "abBAcC"
输出:""
解释:存在多种不同情况,但所有的情况都会导致相同的结果。例如:
"abBAcC" --> "aAcC" --> "cC" --> ""
"abBAcC" --> "abBA" --> "aA" --> ""
提示:

  • 1 <= s.length <= 100
  • s 只包含小写和大写英文字母
思路:栈
class Solution {//执行用时:4 ms, 在所有 Java 提交中击败了100.00%的用户,//2020/08/10
    public String makeGood(String s) {
        Stack stack = new Stack<>();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            while(!stack.isEmpty() && i < s.length() && isSimilar(stack.peek(), s.charAt(i))){
                stack.pop();
                ++i;
            }
            if(i < s.length()){
                stack.push(s.charAt(i));
            }
        }
        while(!stack.isEmpty()){
            sb.append(stack.pop());
        }
    return sb.reverse().toString();
    }

    public boolean isSimilar(Character c1, Character c2){
        return (c1 - 'a') == (c2 - 'A') || (c1 - 'A') == (c2 - 'a');
    }
}

剑指 Offer 09. 用两个栈实现队列简单

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTaildeleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:

  • 1 <= values <= 10000
  • 最多会对 appendTail、deleteHead 进行 10000 次调用
思路:
class CQueue {
    Stack stack1;
    Stack stack2;

    public CQueue() {
        stack1 = new Stack();
        stack2 = new Stack();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if(stack2.isEmpty()){
            if(stack1.isEmpty()) return -1;
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            
        }
        return stack2.pop();
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

你可能感兴趣的:(6.栈(六))