本篇內容主要為 Grokking the Coding Interview: Patterns for Coding Questions 的翻譯與整理,行有餘力建議可以購買該專欄課程閱讀並在上面進行練習。
0295
Find Median from Data StreamPermalink0480
Sliding Window Median0502
IPO設計一個類別(class)來計算一組數字流(number stream)的中位數(median),該類別需要具備以下兩個方法:
insertNum(int num)
將數字儲存於類別中findMedian()
返回目前插入數字流的中位數當數字流中的個數為偶數時,其中位數為中間兩個數字的平均。
Example
正如我們所知,所謂的中位數(median)是排序後的整數序列的中間值,因此一個直觀的暴力解法可以是維護一個有序陣列來儲存插入的數字,這樣一來便可以有效率地返回其中位數;其中,在有序陣列中插入數字需要花費 的時間,此處的 為數字個數,插入的過程類似於進行 插入排序(Insertion Sort)。我們能不能有更好的處理方式?我們能不能利用此題只需要關注中間的數值的事實,而不需要維護完整的有序陣列?
假設 是一組數字中的中位數,這表示數字中的一半元素會小於等於他,而另外一半元素則會大於等於他。所以我們可以將一組數字分成兩半:一半用於儲存所有較小的數(記為 smallNumList
),一半用於儲存所有較大的數(記為 largeNumList
);此時中位數便會是 largeNumList
中的最小數字或是 smallNumList
中的最大數字,當元素數量為偶數時,則為兩數的平均值。
最適合用來查找一組數字中極值的資料結構,非 堆積(Heap) 莫屬,因此我們可以使用堆積來處理這個問題,並有以下演算法:
smallNumList
,因為我們要找出其中的最大數字largeNumList
,因為我們要找出其中的最小數字補充:如何判斷數字屬於前半還是後半?
這個問題等同於「要怎麼確定今天的數字要往最大堆積放?還是往最小堆積放?」;判斷的依據其實是與目前最大堆積中的最大元素進行比較,而且需要維護使得兩個堆積中的元素個數儘量保持平衡:
- 如果當前數字比
maxHeap
中的數字要小,插入maxHeap
- 如果當前數字比
maxHeap
中的數字要大,插入minHeap
- 每次插入後需要檢查兩個堆積是否保持平衡(不能讓一個堆積獨大)
- 如果最大堆積比最小堆積多出了兩個元素,往
minHeap
搬動元素- 如果當前數字個數為奇數,往
maxHeap
搬動元素
上述步驟如下圖所示:
1. insertNum(3)
:優先往最大堆積中插入數字
2. insertNum(1)
:由於 1 比 3 小,往最大堆積中插入數字
3. 檢查堆積是否平衡,此時最大堆積較多元素,將其中的最大值 3 往最小堆積放
4. findMedian()
:兩個堆積數量相同,數字個數為偶數,取兩個頂部元素的平均即為中位數,(1 + 3) / 2 = 2.0
5. insertNum(5)
:由於 5 比 1 大,往最小堆積中插入數字,兩個堆積處於不平衡狀態,將多餘元素優先往最大堆積放
6. findMedian()
:兩個堆積數量不同,取最大堆積中的頂部元素 3
7. insertNum(4)
:由於 4 比 3 大,往最小堆積中插入數字
4. findMedian()
:兩個堆積數量相同,數字個數為偶數,取兩個頂部元素的平均即為中位數,(3 + 4) / 2 = 3.5
在 JavaScript 中並沒有原生支持 Heap 這種資料結構,必須引入其他人的實現,此處使用 collections.js
insertNum()
需要 往堆積中插入數字findMedian()
需要 從堆積中獲取頂部元素給定一組數字組成的陣列與數字 ,找出每 個元素為一組的子陣列之中位數。
Example 01
Example 02
這一題遵循 雙重堆積(Two Heaps) 模式,且與 Find the Median of a Number Stream 存在相似之處。我們一樣維護一個最大堆積與最小堆積來查找陣列的中位數。
唯一差別在於我們必須 追蹤大小為 的滑動窗口中之數字,也就是在每一次迭代的過程,在往堆積中插入新數字的同時,必須從堆積中移去一個數字(脫離窗口的數字),並且在每次操作後都需要對堆積進行平衡(rebalance)
給定一組投資方案與其對應的收益,以及初始的資本額和允許投資的案件數量,我們需要從中找出最佳收益的投資案;當有足夠資本額就可以進行投資,當選定投資方案後,可以假設其收益已經成為我們的資本額。
Example 01
Example 02
選擇投資方案時,我們有以下兩個限制:
採用貪心策略可以獲得最佳解。在選擇方案時,我們需要進行以下操作:
因此我們可以遵循 雙重堆疊(Two Heaps) 模式,使用與 Find the Median of a Number Stream 相同的解題策略。以下是我們演算法的解題步驟:
minHeap
中,用來從中選出滿足最小資本需求的方案maxHeap
中,用來從中選出最大收益的方案