题目汇总: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
。
你需要从字符串中删除最少数目的'('
或者')'
(可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 **任意一条 **要求:
空字符串或只包含小写字母的字符串
可以被写作AB
(A
连接B
)的字符串,其中A
和B
都是有效「括号字符串」
可以被写作(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
- 每种方法
increment
,push
以及pop
分别最多调用1000
次
1441. 用栈操作构建数组简单[✔]
给你一个目标数组
target
和一个整数n
。每次迭代,需要从list = {1,2,3..., n}
中依序读取一个数字。
请使用下述操作来构建目标数组target
:
Push:从list
中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含1
到n
之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。
示例 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. 用两个栈实现队列简单
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数
appendTail
和deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,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();
*/