引言
隊列這種數據結構,據瓶子君瞭解,前端須要瞭解的隊列結構主要有:雙端隊列、滑動窗口,它們都是算法中是比較經常使用的數據結構。前端
所以,本節主要內容爲:git
-
數據結構:隊列(Queue) -
雙端隊列(Deque) -
雙端隊列的應用:翻轉字符串中的單詞 -
滑動窗口 -
滑動窗口應用:無重複字符的最長公共子串 -
最後來一道 leetcode 題目:滑動窗口最大值問題
下面進入正文吧👇github
1、數據結構:隊列
隊列和棧相似,不一樣的是隊列是先進先出 (FIFO) 原則的有序集合,它的結構相似以下:web
常見隊列的操做有:enqueue(e)
進隊、 dequeue()
出隊、 isEmpty()
是不是空隊、 front()
獲取隊頭元素、clear()
清空隊,以及 size()
獲取隊列長度。面試
代碼實現算法
function Queue() {
let items = []
this.enqueue = function(e) {
items.push(e)
}
this.dequeue = function() {
return items.shift()
}
this.isEmpty = function() {
return items.length === 0
}
this.front = function() {
return items[0]
}
this.clear = function() {
items = []
}
this.size = function() {
return items.length
}
}
查找:從對頭開始查找,從時間複雜度爲 O(n)編程
插入或刪除:進棧與出棧的時間複雜度爲 O(1)數組
2、雙端隊列(Deque)
1. 什麼是 Deque
Deque 在原有隊列的基礎上擴充了:隊頭、隊尾均可以進隊出隊,它的數據結構以下:瀏覽器
代碼實現:緩存
function Deque() {
let items = []
this.addFirst = function(e) {
items.unshift(e)
}
this.removeFirst = function() {
return items.shift()
}
this.addLast = function(e) {
items.push(e)
}
this.removeLast = function() {
return items.pop()
}
this.isEmpty = function() {
return items.length === 0
}
this.front = function() {
return items[0]
}
this.clear = function() {
items = []
}
this.size = function() {
return items.length
}
}
下面看一道經典的雙端隊列問題👇
2. 字節&leetcode151:翻轉字符串裏的單詞
給定一個字符串,逐個翻轉字符串中的每一個單詞。
示例 1:
輸入: "the sky is blue"
輸出: "blue is sky the"
示例 2:
輸入: " hello world! "
輸出: "world! hello"
解釋: 輸入字符串能夠在前面或者後面包含多餘的空格,可是反轉後的字符不能包括。
示例 3:
輸入: "a good example"
輸出: "example good a"
解釋: 若是兩個單詞間有多餘的空格,將反轉後單詞間的空格減小到只含一個。
說明:
-
無空格字符構成一個單詞。 -
輸入字符串能夠在前面或者後面包含多餘的空格,可是反轉後的字符不能包括。 -
若是兩個單詞間有多餘的空格,將反轉後單詞間的空格減小到只含一個。
解題思路:使用雙端隊列解題
-
首先去除字符串左右空格 -
逐個讀取字符串中的每一個單詞,依次放入雙端隊列的對頭 -
再將隊列轉換成字符串輸出(已空格爲分隔符)
畫圖理解:
代碼實現:
var reverseWords = function(s) {
let left = 0
let right = s.length - 1
let queue = []
let word = ''
while (s.charAt(left) === ' ') left ++
while (s.charAt(right) === ' ') right --
while (left <= right) {
let char = s.charAt(left)
if (char === ' ' && word) {
queue.unshift(word)
word = ''
} else if (char !== ' '){
word += char
}
left++
}
queue.unshift(word)
return queue.join(' ')
};
更多解法詳見 圖解字節&leetcode151:翻轉字符串裏的單詞
3、滑動窗口
1. 什麼是滑動窗口
這是隊列的另外一個重要應用
顧名思義,滑動窗口就是一個運行在一個大數組上的子列表,該數組是一個底層元素集合。
假設有數組 [a b c d e f g h ],一個大小爲 3 的 滑動窗口在其上滑動,則有:
[a b c]
[b c d]
[c d e]
[d e f]
[e f g]
[f g h]
通常狀況下就是使用這個窗口在數組的 合法區間 內進行滑動,同時 動態地 記錄一些有用的數據,不少狀況下,可以極大地提升算法地效率。
下面看一道經典的滑動窗口問題👇
2. 字節&Leetcode3:無重複字符的最長子串
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
示例 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)
更多解法詳見 字節&Leetcode3:無重複字符的最長子串
最後,來嘗試一道leetcode題目吧!
4、leetcode239:滑動窗口最大值問題
給定一個數組 nums
和滑動窗口的大小 k
,請找出全部滑動窗口裏的最大值。
示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:
滑動窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你能夠假設 k
老是有效的,在輸入數組不爲空的狀況下,1 ≤ k ≤ 輸入數組的大小。
能夠本身嘗試解答一下,歡迎將答案提交到 https://github.com/sisterAn/JavaScript-Algorithms/issues/33 ,瓶子君將明日解答😊
5、往期精彩
6、前端算法集訓營第一期免費加入啦
歡迎關注「前端瓶子君」,回覆「算法」自動加入,從0到1構建完整的數據結構與算法體系!
在這裏,瓶子君不只介紹算法,還將算法與前端各個領域進行結合,包括瀏覽器、HTTP、V八、React、Vue源碼等。
在這裏,你能夠天天學習一道大廠算法題(阿里、騰訊、百度、字節等等)或 leetcode,瓶子君都會在次日解答喲!
⬆️ 掃碼關注公衆號「前端瓶子君」,回覆「算法」便可自動加入 👍👍👍
「在看轉發」是最大的支持
本文分享自微信公衆號 - 前端瓶子君(pinzi_com)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。