瓶子君又來啦,她帶着前端算法來了👏👏👏前端
大廠面試愈來愈難,對算法的要求也愈來愈多,當面試官問到一個算法題,給出一份完美答案能大大提升面試官的好感度,本系列就是致力於打造一套適用於前端的算法。git
往期精彩系列文章:github
三篇交流羣刷題總結:面試
題目(題目僅僅會在「前端進階算法集訓營」裏發佈,每早 9: 00):瀏覽器
數組篇:緩存
鏈表篇:markdown
字符串篇:
棧篇:
本節是第四周的總結與回顧,下面開始進入正題吧!👇👇👇
function isPlalindrome(input) { if (typeof input !== 'string') return false; return input.split('').reverse().join('') === input; } 複製代碼
function isPlalindrome(input) { if (typeof input !== 'string') return false; let i = 0, j = input.length - 1 while(i < j) { if(input.charAt(i) !== input.charAt(j)) return false i ++ j -- } return true } 複製代碼
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
示例 1:
輸入: "abcabcbb" 輸出: 3 解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。 複製代碼
示例 2:
輸入: "bbbbb" 輸出: 1 解釋: 由於無重複字符的最長子串是 "b",因此其長度爲 1。 複製代碼
示例 3:
輸入: "pwwkew" 輸出: 3 解釋: 由於無重複字符的最長子串是 "wke",因此其長度爲 3。 請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。 複製代碼
解題思路: 使用一個數組來維護滑動窗口
遍歷字符串,判斷字符是否在滑動窗口數組裏
push
進數組push
進數組max
更新爲當前最長子串的長度遍歷完,返回 max
便可
畫圖幫助理解一下:
代碼實現:
var lengthOfLongestSubstring = function(s) { let arr = [], max = 0 for(let i = 0; i < s.length; i++) { let index = arr.indexOf(s[i]) if(index !== -1) { arr.splice(0, index+1); } arr.push(s.charAt(i)) max = Math.max(arr.length, max) } return max }; 複製代碼
時間複雜度:O(n2), 其中 arr.indexOf()
時間複雜度爲 O(n) ,arr.splice(0, index+1)
的時間複雜度也爲 O(n)
空間複雜度:O(n)
解題思路: 使用下標來維護滑動窗口
代碼實現:
var lengthOfLongestSubstring = function(s) { let index = 0, max = 0 for(let i = 0, j = 0; j < s.length; j++) { index = s.substring(i, j).indexOf(s[j]) if(index !== -1) { i = i + index + 1 } max = Math.max(max, j - i + 1) } return max }; 複製代碼
時間複雜度:O(n2)
空間複雜度:O(n)
解題思路:
使用 map
來存儲當前已經遍歷過的字符,key
爲字符,value
爲下標
使用 i
來標記無重複子串開始下標,j
爲當前遍歷字符下標
遍歷字符串,判斷當前字符是否已經在 map
中存在,存在則更新無重複子串開始下標 i
爲相同字符的下一位置,此時從 i
到 j
爲最新的無重複子串,更新 max
,將當前字符與下標放入 map
中
最後,返回 max
便可
代碼實現:
var lengthOfLongestSubstring = function(s) { let map = new Map(), max = 0 for(let i = 0, j = 0; j < s.length; j++) { if(map.has(s[j])) { i = Math.max(map.get(s[j]) + 1, i) } max = Math.max(max, j - i + 1) map.set(s[j], j) } return max }; 複製代碼
時間複雜度:O(n)
空間複雜度:O(n)
棧是一種聽從後進先出 (LIFO / Last In First Out) 原則的有序集合,它的結構相似以下:
代碼實現
function Stack() { let items = [] this.push = function(e) { items.push(e) } this.pop = function() { return items.pop() } this.isEmpty = function() { return items.length === 0 } this.size = function() { return items.length } this.clear = function() { items = [] } } 複製代碼
查找:從棧頭開始查找,時間複雜度爲 O(n)
插入或刪除:進棧與出棧的時間複雜度爲 O(1)
調用棧是 JavaScript 用來管理函數執行上下文的一種數據結構,它記錄了當前函數執行的位置,哪一個函數正在被執行。 若是咱們執行一個函數,就會爲函數建立執行上下文並放入棧頂。 若是咱們從函數返回,就將它的執行上下文從棧頂彈出。 也能夠說調用棧是用來管理這種執行上下文的棧,或稱執行上下文棧(執行棧)。
JavaScript 中的內存空間主要分爲三種類型:
代碼空間主要用來存放可執行代碼的。棧空間及堆空間主要用來存放數據的。接下來咱們主要介紹棧空間及堆空間。
當調用棧中執行完成一個執行上下文時,須要進行垃圾回收該上下文以及相關數據空間,存放在棧空間上的數據經過 ESP 指針來回收,存放在堆空間的數據經過副垃圾回收器(新生代)與主垃圾回收器(老生代)來回收。
詳細請看 前端進階算法5:全方位解讀前端用到的棧結構(+leetcode刷題)
設計一個支持 push
,pop
,top
操做,並能在常數時間內檢索到最小元素的棧。
push(x)
—— 將元素 x 推入棧中。pop()
—— 刪除棧頂的元素。top()
—— 獲取棧頂元素。getMin()
—— 檢索棧中的最小元素。示例:
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2. 複製代碼
在常數時間內檢索到最小元素的棧,即僅需保證 getMin
的時間複雜度爲 O(1) 便可
var MinStack = function() { this.items = [] this.min = null }; // 進棧 MinStack.prototype.push = function(x) { if(!this.items.length) this.min = x this.min = Math.min(x, this.min) this.items.push(x) }; // 出棧 MinStack.prototype.pop = function() { let num = this.items.pop() this.min = Math.min(...this.items) return num }; // 獲取棧頂元素 MinStack.prototype.top = function() { if(!this.items.length) return null return this.items[this.items.length -1] }; // 檢索棧中的最小元素 MinStack.prototype.getMin = function() { return this.min }; 複製代碼
時間複雜度:進棧O(1),出棧O(n),獲取棧頂元素O(1),獲取最小元素O(1)
空間複雜度:O(n)
詳見 字節&leetcode155:最小棧(包含getMin函數的棧)
給定一個只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判斷字符串是否有效。
有效字符串需知足:
注意空字符串可被認爲是有效字符串。
示例 1:
輸入: "()" 輸出: true 複製代碼
示例 2:
輸入: "()[]{}" 輸出: true 複製代碼
示例 3:
輸入: "(]" 輸出: false 複製代碼
示例 4:
輸入: "([)]" 輸出: false 複製代碼
示例 5:
輸入: "{[]}" 輸出: true 複製代碼
解題思路: 將字符串中的字符依次入棧,遍歷字符依次判斷:
{
、 (
、 [
,直接入棧}
、 )
、 ]
中的一種,若是該字符串有效,則該元素應該與棧頂匹配,例如棧中元素有 ({
, 若是繼續遍歷到的元素爲 )
, 那麼當前元素序列爲 ({)
是不可能有效的,因此此時與棧頂元素匹配失敗,則直接返回 false
,字符串無效當遍歷完成時,全部已匹配的字符都已匹配出棧,若是此時棧爲空,則字符串有效,若是棧不爲空,說明字符串中還有未匹配的字符,字符串無效
畫圖幫助理解一下:
代碼實現:
var isValid = function(s) { let map = { '{': '}', '(': ')', '[': ']' } let stack = [] for(let i = 0; i < s.length ; i++) { if(map[s[i]]) { stack.push(s[i]) } else if(s[i] !== map[stack.pop()]){ return false } } return stack.length === 0 }; 複製代碼
時間複雜度:O(n)
空間複雜度:O(n)
給出由小寫字母組成的字符串 S
,重複項刪除操做 會選擇兩個相鄰且相同的字母,並刪除它們。
在 S 上反覆執行重複項刪除操做,直到沒法繼續刪除。
在完成全部重複項刪除操做後返回最終的字符串。答案保證惟一。
示例:
輸入:"abbaca" 輸出:"ca" 解釋: 例如,在 "abbaca" 中,咱們能夠刪除 "bb" 因爲兩字母相鄰且相同,這是此時惟一能夠執行刪除操做的重複項。以後咱們獲得字符串 "aaca",其中又只有 "aa" 能夠執行重複項刪除操做,因此最後的字符串爲 "ca"。 複製代碼
提示:
<= S.length <= 20000
S
僅由小寫英文字母組成。解題思路: 遍歷字符串,依次入棧,入棧時判斷與棧頭元素是否一致,若是一致,即這兩個元素相同相鄰,則須要將棧頭元素出棧,而且當前元素也無需入棧
解題步驟: 遍歷字符串,取出棧頭字符,判斷當前字符與棧頭字符是否一致
遍歷完成後,返回棧中字符串
代碼實現:
var removeDuplicates = function(S) { let stack = [] for(c of S) { let prev = stack.pop() if(prev !== c) { stack.push(prev) stack.push(c) } } return stack.join('') }; 複製代碼
時間複雜度:O(n)
空間複雜度:O(n)
詳見 leetcode1047:刪除字符串中的全部相鄰重複項
從0到1構建完整的數據結構與算法體系!
在這裏,瓶子君不只介紹算法,還將算法與前端各個領域進行結合,包括瀏覽器、HTTP、V八、React、Vue源碼等。
在這裏,你能夠天天學習一道大廠算法題(阿里、騰訊、百度、字節等等)或 leetcode,瓶子君都會在次日解答喲!
掃碼加入,若羣人數已達上線,關注公衆號「前端瓶子君」,回覆「算法」便可自動加入
⬆️ 掃碼關注公衆號「前端瓶子君」,回覆「算法」便可自動加入 👍👍👍