题目
给你一个由 ‘(’、’)’ 和小写字母组成的字符串 s。
你需要从字符串中删除最少数目的 ‘(’ 或者 ‘)’ (可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 任意一条 要求:
- 空字符串或只包含小写字母的字符串
- 可以被写作 AB(A 连接 B)的字符串,其中 A 和 B 都是有效「括号字符串」
- 可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」
解题思路
本题可以使用栈实现,也可以用创建模拟栈顶指针实现
模拟栈顶指针思路:大体思路是创建栈顶指针top,左括号top+1,右括号top-1,正向遍历去除多余的')'
,再反向遍历去除多余的'('
即可
栈方法思路:遍历字符串,遇到左括号,将其下标入栈,遇到右括号时,如果栈空,删除元素,否则弹栈,最后遍历栈,根据下标删除多于的左括号即可
代码
方法一:模拟指针
public String minRemoveToMakeValid(String s) {
char[] c = s.toCharArray();
StringBuffer s1 = new StringBuffer();
//第一轮正向遍历:去掉多余的')'
for (int i = 0, top=0; i < c.length; i++) {
if(c[i] == '(') {
//如果遇到左括号,拼接进缓冲区,top+1;
s1.append(c[i]);
top++;
} else if(c[i] == ')') {
//如果遇到右括号,如果当前没有左括号跟它匹配,那么不拼接金缓冲区,直接开始下次循环,否则拼接,top-1
if(top == 0) continue;
top--;
s1.append(c[i]);
}
else{
//如果遇到字母,直接拼接
s1.append(c[i]);
}
}
//第二轮反向遍历,去掉多余的'('
s = s1.toString();//缓冲区转为字符串
c = s.toCharArray();//获取字符数组
s1 = new StringBuffer();//开辟一个新的缓冲区
for (int i = c.length-1, top = 0; i >= 0; i--) {
if(c[i] == ')'){
//如果遇到右括号,直接拼接 top +1
top++;
s1.append(c[i]);
}else if(c[i] == '('){
if(top == 0) continue;
top--;
s1.append(c[i]);
}else
s1.append(c[i]);
}
//此时的缓冲区是逆序的,因为appen方法是追加至末尾,所以需要翻转缓冲区,再转换为字符串返回即可
return s1.reverse().toString();
}
方法二:栈
String[] str = s.split("");//转为字符串数组
Stack<Integer> stack = new Stack<Integer>();//实例化栈
//遍历数组
for (int i = 0; i < str.length; i++) {
//左括号
if("(".equals(str[i])){
//下标入栈
stack.push(i);
}
//右括号
if(")".equals(str[i])){
//如果栈非空,弹栈
if(!stack.empty()) stack.pop();
//否则删除该元素
else{
str[i]=null;
}
}
}
//遍历栈删除多余的左括号
for (Integer integer : stack) {
str[integer] = null;
}
//将数组拼接为字符串
StringBuffer sb = new StringBuffer();
for (int i = 0; i < str.length; i++) {
if(str[i] != null){
sb.append(str[i]);
}
}
return sb.toString();
总结
- JAVA中拼接字符串尽量使用StringBuffer,因为String类型本来就是"immutable(不可直接修改)的变量",使用"+"拼接会导致JVM内存泄露。
- StringBuffer中的append是追加到末尾,insert是追加到指定位置,虽然insert看着很方便,有时候不用翻转字符串,但是由于底层实现的原因,时间复杂度会大大高于append。
- 思路收获:所有数据结构内存的值,不仅仅是具体元素,也可以是它们的下标