于2021.11.17练习
本题其实是一个“开心消消乐”的问题,下面展示了两种解法。
1.1 解法1:常规解法之栈
1.1.1 思路
思路:因为要删除的重复项是两个相邻且相同的字母,故每次都要比较当前元素和上一个元素,因此想到用栈。
首先遍历当前字符串,当遍历到某一个字符时,先判断该字符和栈顶元素是否相等,如果相等,就将栈顶元素弹出;如果不等或者此时堆栈为空,就将该字符入栈。
最后遍历完成后,将栈中的字符倒着输出。一开始我写的是res += stack.pop(),这种写法相当于res = res + stack.pop(),是将新弹出的元素添加在了后面,输出的结果为ca;而我们要的结果为ac,因此应该写成res = stack.pop() + res,这样保证新弹出的元素永远在最前面。
1.1.2 代码实现
执行用时:170 ms, 在所有 Java 提交中击败了5.18%的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了8.13%的用户
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
int n = s.length();
for(int i = 0;i < n;i++){
char cur = s.charAt(i);
if(stack.empty() || cur != stack.peek()){
stack.push(cur);
}
else{
stack.pop();
}
}
String res = "";
while(!stack.empty()){
res = stack.pop() + res;
}
return res;
}
}
上面利用String的+运算进行添加,效率会很低,因此考虑用StringBuilder做改进。这里可以详细看下String和StringBuilder的区别!
执行用时:28 ms, 在所有 Java 提交中击败了34.59%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了49.30%的用户
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
int n = s.length();
for(int i = 0;i < n;i++){
char cur = s.charAt(i);
if(stack.empty() || cur != stack.peek()){
stack.push(cur);
}
else{
stack.pop();
}
}
StringBuilder res = new StringBuilder();
while(!stack.empty()){
res.append(stack.pop());
}
return res.reverse().toString();
}
}
1.1.3 方法小结
代码中用到的一些StringBuilder的方法,进行下面总结:
-
添加元素,使用append()
-
反转字符序列,使用reverse()
-
将StringBuilder转换为String,使用toString()
1.2 解法2:双指针原地法
1.2.1 思路
思路:将字符串转换为字符数组,用一个指针i去遍历原字符数组,用一个指针top指向结果数组的最后一个位置。当top为-1(说明结果数组此时为空)或者原字符数组中的元素不等于top指向的元素,就将top后移一位,并且将元素赋给top位置;否则的话,说明相邻元素是重复的,就将top前移一位。整个过程都是在原字符数组上进行的,并没有开辟新的数组,因此称为原地算法。
小结:个人感觉这种双指针原地的思想类似于leetcode26.删除有序数组中的重复项以及leetcode27.移除元素。
1.2.2 代码实现
执行用时:3 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.8 MB, 在所有 Java 提交中击败了87.94%的用户
class Solution {
public String removeDuplicates(String s) {
char[] ch = s.toCharArray();
int top = -1;
for(int i = 0;i < ch.length;i++){
if(top == -1 || ch[i] != ch[top]){
ch[++top] = ch[i];
}
else{
top--;
}
}
return String.valueOf(ch,0,top + 1);
}
}
1.2.3 方法总结
代码中用到的一些方法,进行下面总结:
- 字符串转换为字符数组,用toCharArray()
- 字符数组转换为字符串String,利用包装类的转换,用String.valueOf(char[] data, int offset, int count),后面的offset和count参数表示从[offset,count)范围,是左闭右开的,因此右侧要取top+1