你们好,我是神三元。今天给你们分享一道有意思的算法题,在leetcode平台上截图以下: 前端
近半年来广受各大公司的青睐,出现很是频繁,在腾讯仅仅半年就出现了17次,若是说给满分给5颗星的话,那么这一题算得上实打实的五星题。算法
那到底是什么让这个算法从众多的leetcode题库中脱颖而出,在各个大厂中一而再、再而三地出现呢?编程
接下来,就让咱们解开它的奥秘,领略这个算法到底多么经典!数据结构
刚开始拿到这道题,看到括号匹配问题,直觉上就想到了利用栈先进后出的性质,来完成先后字符串的拼接。可是后来尝试了好久,发现问题并无那么简单,主要概括以下:编程语言
接着,咱们利用不一样的方法,来一步步来解决这两个棘手的问题。spa
说一下总体思路。扫描一遍字符串,针对不一样的字符进行不一样的处理:code
首先有两个重要的变量,表示重复次数的 multi 值和累积字符串 res。cdn
数字
, 直接参加计算,累积multi值。[
,]
外),累积到 res 后面。[
, 将以前累积的字符串res压栈,当前multi值压到另外一个栈。而后当前 multi归零,res置空。]
, 取出栈中multi值,将当前的 res 字符串重复 multi 次,赋值给临时变量tmp,而后让另外一个存放累积字符串的栈中弹出栈顶元素和当前的tmp拼接,做为最新的累积字符串赋值给res。若是如今没看懂,没有关系,给出代码就明白了,让你们直观感觉一下:blog
var decodeString = function (s) {
// 存放 【重复次数】 的栈
let countStack = [];
// 存放 【累积字符串】 的栈
let resStack = [];
// 用来累积的字符串 res
let res = "";
// 表示重复次数
let multi = 0;
for (let i = 0; i < s.length; i++) {
let cur = s.charAt(i);
if (cur == '[') {
// 双双压栈,保存了当前的状态
countStack.push(multi);
resStack.push(res);
// 纷纷置空,准备下面的累积
multi = 0;
res = "";
} else if (cur == ']') {
// 遇到 ],表示累积结束,要算帐了。
// 【当前的串出现多少次】还保存在栈中,把它取出来
let count = countStack.pop();
let temp = "";
// 让 [ 和 ] 之间的字符串(就是累积字符串res)重复 count 次
for(let i = 0; i < count; i++) {
temp += res;
}
// 和前面已经求得的字符串进行拼接
res = resStack.pop() + temp;
} else if (cur >= '0' && cur <= '9') {
// multi累积
multi = multi * 10 + (cur - '0');
} else {
// 字符累积
res += cur;
}
}
return res;
};
复制代码
递归的思路就容易一点,一旦遇到[
,立马进入新的递归程序,扫描到对应的]
为止。也就是说,凡是遇到括号,括号里面的事情,所有交给子程序完成。建议你们看完代码再来体会这句话:递归
var decodeString = function (s) {
// 从第 0 个元素开始处理
return dfs(s, 0);
};
let dfs = (s, n) => {
let res = "";
// 保存起始索引
let i = n;
// 同上,表示重复的次数
let multi = 0;
while(i < s.length) {
let cur = s.charAt(i);
// 遇到数字,累积 multi 值
if(cur >= '0' && cur <= '9')
multi = multi * 10 + (cur - '0');
else if(cur === '[') {
// 划重点!给子程序,把对应的 ] 索引和括号包裹的字符串返回
// 即tmp 的格式为 [索引,字符串]
let tmp = dfs(s, i + 1);
// 这样下次遍历就是从对应的 ] 后面遍历了,由于当前已经把括号里面的处理完了
i = tmp[0];
// 须要重复的字符串已经返回来了
let repeatStr = tmp[1];
for(let j = 0; j < multi; j++) {
res += repeatStr;
}
// 当前已经把括号里面的处理完,multi 置零,为下一轮遍历准备
multi = 0;
}else if(cur === ']') {
// 遇到了对应的 ] ,返回 ] 索引和括号包裹的字符串
return [i, res];
} else {
res += cur;
}
// 继续遍历
i++;
}
return res;
}
复制代码
两种方法都顺利经过。
估计作完这道题,仔细回味一下,也可以发现这道题的经典之处了:
作完这道题,是否是刷新了本身对于栈这种数据结构的认知呢?
它其实具备着自然的递归性质,只是咱们初学的时候,容易先入为主地把这种先入后出的数据结构想的太简单。固然它还有其余神奇的功能,咱们放在下一期来分享。
若是你以为这篇内容对你挺有启发,我想邀请你帮我两个小忙:
点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
关注公众号「前端三元同窗」,每日坚持灵魂之问,碰见更好的本身!