1. 祖马(连续地消除邻接的相同字符:aabbbacaa --> c)
思路:从左往右扫字符时,每遇到一个字符,在处理时都要往回看(进行对比),这种情况下栈的数据结构是很适合用的。如下图
方法一:
一个指针i(向后扫描),一个栈result(存放最终结果)
case 1: 如果str[i] !== result.top,直接把这个字符放进结果栈里面
case 2: 如果str[i] === result.top,指针继续往后移动,直到遇到跟栈顶不一样的,然后把栈顶弹出,这样就相当于联同中间的一样的全部一次剔除。但是不把这个新的加进去,而是i--去启动下一个循环,因为这个新的虽然跟它的直接前驱不同,但有可能跟你很久之前放进result的字符一样。
因为js的数组没有top或者peek方法去看栈顶元素,但是大家都知道array[array.length - 1]就是最后一个元素了。下面上代码:
function dedupRepeatedly(str) {
const result = []; // 虽然是数组,但是是栈,嗯。。
for (let i = 0; i < str.length; i++) {
const top = result[result.length - 1]; // 看栈顶
if (result.length > 0 && str[i] === top) {
// 如果相同,则一直往后看
let j = i + 1;
for (; j < str.length; j++) {
if (str[j] !== top) {
break; // 看到不同的才跳出
}
}
result.pop(); // 弹出已经放入result的那个相同的
i = j - 1; // 回退一格,让循环继续跟result栈顶比较
} else {
result.push(str[i]);
}
}
return result.join('');
}复制代码
第一个判断那,有人说js就算result[-1]也不会越界报错,为啥还要多此一举?我觉得是可以但是总有点奇技淫巧感觉,毕竟就算你赋值result[-1]=2了之后result.length还是0,这个时候就不是当数组用了而是当object。
方法二:
不用单独声明一个栈,直接用两个快慢指针模仿栈。
慢指针slow总是指向(想象的)栈顶,所以str的0~slow间的值即为result,快指针i还是做他以前的工作。
function dedupRepeatedly(str) {
str = str.split(''); // 首先把str变成数组,因为string是不可变的(immutable),后面str[i] = 'v'没用
let slow = -1; // 这我就直接-1开始了, 从0开始要考虑的情况比较多就算了
for (let i = 0; i < str.length; i++) {
const top = str[slow];
if (str[i] === top) {
let j = i + 1;
for (; j < str.length; j++) {
if (str[j] !== top) {
break;
}
}
slow--; // 栈顶指针往回移一位,相当于pop了
i = j - 1;
} else {
str[++slow] = str[i]; // 设置下一个栈顶元素
}
}
return str.slice(0, slow + 1).join('');
}复制代码