你們好,我是神三元。今天給你們分享一道有意思的算法題,在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;
}
複製代碼
兩種方法都順利經過。
估計作完這道題,仔細回味一下,也可以發現這道題的經典之處了:
作完這道題,是否是刷新了本身對於棧這種數據結構的認知呢?
它其實具備着自然的遞歸性質,只是咱們初學的時候,容易先入爲主地把這種先入後出的數據結構想的太簡單。固然它還有其餘神奇的功能,咱們放在下一期來分享。
若是你以爲這篇內容對你挺有啓發,我想邀請你幫我兩個小忙:
點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
關注公衆號「前端三元同窗」,每日堅持靈魂之問,碰見更好的本身!