1-3年的Android開發工程師看過來,架構師必備技能
正文
我們今天將說明以下 14 種模式:
1.滑動視窗
2.二指標或迭代器
3.快速和慢速指標或迭代器
4.合併區間
5.迴圈排序
6.原地反轉連結串列
7.樹的寬度優先搜尋(Tree BFS)
8.樹的深度優先搜尋(Tree DFS)
9.Two Heaps
10.子集
11.經過修改的二叉搜尋
12. 前 K 個元素
13. K 路合併
14.拓撲排序
我們開始吧!
1.滑動視窗
滑動視窗模式是用於在給定陣列或連結串列的特定視窗大小上執行所需的操作,比如尋找包含所有 1 的最長子陣列。從第一個元素開始滑動視窗並逐個元素地向右滑,並根據你所求解的問題調整視窗的長度。在某些情況下視窗大小會保持恆定,在其它情況下視窗大小會增大或減小。
下面是一些你可以用來確定給定問題可能需要滑動視窗的方法:
- 問題的輸入是一種線性資料結構,比如連結串列、陣列或字串
- 你被要求查詢最長/最短的子字串、子陣列或所需的值
你可以使用滑動視窗模式處理的常見問題:
- 大小為 K 的子陣列的最大和(簡單)
- 帶有 K 個不同字元的最長子字串(中等)
- 尋找字元相同但排序不一樣的字串(困難)
2.二指標或迭代器
二指標(Two Pointers)是這樣一種模式:兩個指標以一前一後的模式在資料結構中迭代,直到一個或兩個指標達到某種特定條件。二指標通常在排序陣列或連結串列中搜索配對時很有用;比如當你必須將一個數組的每個元素與其它元素做比較時。
二指標是很有用的,因為如果只有一個指標,你必須繼續在陣列中迴圈回來才能找到答案。這種使用單個迭代器進行來回在時間和空間複雜度上都很低效——這個概念被稱為「漸進分析(asymptotic analysis)」。儘管使用 1 個指標進行暴力搜尋或簡單普通的解決方案也有效果,但這會沿 O(n2) 線得到一些東西。在很多情況中,二指標有助於你尋找有更好空間或執行時間複雜度的解決方案。
用於識別使用二指標的時機的方法:
- 可用於你要處理排序陣列(或連結列表)並需要查詢滿足某些約束的一組元素的問題
- 陣列中的元素集是配對、三元組甚至子陣列
下面是一些滿足二指標模式的問題:
- 求一個排序陣列的平方(簡單)
- 求總和為零的三元組(中等)
- 比較包含回退(backspace)的字串(中等)
3.快速和慢速指標
快速和慢速指標方法也被稱為 Hare & Tortoise 演算法,該演算法會使用兩個在陣列(或序列/連結串列)中以不同速度移動的指標。該方法在處理迴圈連結串列或陣列時非常有用。
通過以不同的速度進行移動(比如在一個迴圈連結串列中),該演算法證明這兩個指標註定會相遇。只要這兩個指標在同一個迴圈中,快速指標就會追趕上慢速指標。
如何判別使用快速和慢速模式的時機?
- 處理連結串列或陣列中的迴圈的問題
- 當你需要知道特定元素的位置或連結串列的總長度時
何時應該優先選擇這種方法,而不是上面提到的二指標方法?
- 有些情況不適合使用二指標方法,比如在不能反向移動的單鏈接連結串列中。使用快速和慢速模式的一個案例是當你想要確定一個連結串列是否為迴文(palindrome)時。
下面是一些滿足快速和慢速指標模式的問題:
- 連結串列迴圈(簡單)
- 迴文連結串列(中等)
- 環形陣列中的迴圈(困難)
4.合併區間
合併區間模式是一種處理重疊區間的有效技術。在很多涉及區間的問題中,你既需要找到重疊的區間,也需要在這些區間重疊時合併它們。該模式的工作方式為:
給定兩個區間(a 和 b),這兩個區間有 6 種不同的互相關聯的方式:
理解並識別這六種情況有助於你求解範圍廣泛的問題,從插入區間到優化區間合併等。
那麼如何確定何時該使用合併區間模式呢?
- 如果你被要求得到一個僅含互斥區間的列表
- 如果你聽到了術語「重疊區間(overlapping intervals)」
合併區間模式的問題:
- 區間交叉(中等)
- 最大 CPU 負載(困難)
5. 迴圈排序
這一模式描述了一種有趣的方法,處理的是涉及包含給定範圍內數值的陣列的問題。迴圈排序模式一次會在陣列上迭代一個數值,如果所迭代的當前數值不在正確的索引處,就將其與其正確索引處的數值交換。你可以嘗試替換其正確索引處的數值,但這會帶來 O(n^2) 的複雜度,這不是最優的,因此要用迴圈排序模式。
如何識別這種模式?
- 涉及數值在給定範圍內的排序陣列的問題
- 如果問題要求你在一個排序/旋轉的陣列中找到缺失值/重複值/最小值
迴圈排序模式的問題:
- 找到缺失值(簡單)
- 找到最小的缺失的正數值(中等)
6.原地反轉連結串列
在很多問題中,你可能會被要求反轉一個連結串列中一組節點之間的連結。通常而言,你需要原地完成這一任務,即使用已有的節點物件且不佔用額外的記憶體。這就是這個模式的用武之地。該模式會從一個指向連結串列頭的變數(current)開始一次反轉一個節點,然後一個變數(previous)將指向已經處理過的前一個節點。以鎖步的方式,在移動到下一個節點之前將其指向前一個節點,可實現對當前節點的反轉。另外,也將更新變數「previous」,使其總是指向已經處理過的前一個節點。
寫在最後
對程式設計師來說,很多技術的學習都是“防禦性”的。也就是說,我們是在為未來學習。我們學習新技術的目的,或是為了在新專案中應用,或僅僅是為了將來的面試。但不管怎樣,一定不能“止步不前”,不能荒廢掉。
![
文章以下內容會給出阿里與美團的面試題(答案+解析)、面試題庫、Java核心知識點梳理等