在二叉樹中每一層要麼是滿的,要麼是從左到右依次變滿。git
而堆就是一棵徹底二叉樹。除了有徹底二叉樹的特性外,對於堆,任何一個節點爲頭的子樹最大值(或者最小值)爲頭節點本身。github
9
/ \
3 6
/ \
1 2
複製代碼
以上就是一個大根堆算法
用數組結構實現。好比對於堆存儲的數組中是 [9,3,6,1,2],對於任意 i 位置來講,左孩子是2*i+1,右孩子是2*i+2,父節點是 (i-1)/2。api
對於堆來講,有兩個基本操做heapInsert(往堆中插入元素)、heapify(調整堆的位置)。所以掌握這兩個操做是重點。數組
場景: 好比對於堆 [5, 1, 4, 0,], insert 3 後。這就不是大根堆了,所以須要調整。bash
5
/ \
1 4
/ \
0 3
複製代碼
一、定義變量 heapSize, 這個變量有兩個做用 一、表示堆大小 二、堆插入時下一個數的位置。
二、對於新插入的位置 i,計算 i 的父親 p = (i-1)/2,若是 arr[i] > arr[p], 交換 i、p 位置,把p的位置賦值給 i。
三、循環執行2,直到 p === 0 或者 arr[i] <= arr[p] 跳出循環
如下代碼能夠知足 p === 0 或者 arr[i] <= arr[p] 跳出循環的邏輯
while(arr[index] > arr[parseInt((index - 1)/2)]) {}
複製代碼
場景: 好比對於堆 [5, 4, 1, 0, 3],彈出堆頂元素 5 ,把剩餘的元素繼續調整堆結構。ui
5
/ \
4 1
/ \
0 3
複製代碼
僞代碼:spa
一、把堆頂元素記錄下來,以備之後返回,而後把堆中最後一個元素覆蓋堆頂,heapSize--。
二、i 和 i 左、右孩子較大一個進行比較,若是孩子大,則把 i 和左右孩子中較大一個進行交換,交換後,i 移動到較大孩子的節點。
三、循環執行2,直到 i 比左右孩子較大的那個小,或者 i > heapSize
複製代碼
堆排序就是把 heapInsert 和 heapify 結合起來的一個算法
一、先經過 heapInsert 把一個數組調整成大根堆
二、把堆頂元素和堆最後一個元素交換,而後 heapSize--。根據堆頂進行 heapify 操做。
三、重複執行2,直到 heapSize === 0,說明數組已經排好序
複製代碼
heapInsert是 O(logN), 也要進行 N 次,總共建堆的時間是 O(NlogN)。
heapify的最可能是調整樹的高度 O(logN),而要進行 N 次,堆中全部元素調整完成時間複雜度是 O(NlogN)
因此 堆排序的時間複雜度是 O(NlogN)
在建堆的過程當中,時間複雜度能夠縮短成O(N),但在 heapify 操做是不能改變的仍是 O(NlogN)。因此時間複雜度仍是 O(NlogN)
排序過程當中只用到了某些變量,好比 heapSize 、lc 、rc。
所以空間複雜度是 O(1)。
任何一個節點爲頭的子樹最大值爲頭節點本身。
任何一個節點爲頭的子樹最小值爲頭節點本身。
如圖經過上面小根堆代碼一次彈出堆頂元素,是依次變大的。
有一個源源不斷吐出整數的數據流,假設你有足夠的空間來保存吐出的數。
請設計一個叫medianHolder函數。這個函數能夠隨時吐出這些數的中位數。
要求
一、若是MedianHolder已經保存了吐出的N個數,那麼任意時刻將一個新數加入到MedianHolder的過程,其時間複雜度是 O(logN)
二、隨時吐出整數的時間複雜度是 O(1)
複製代碼
這個數組中較大的 N/2 放在小根堆中,較小的 N/2 放到大根堆中,而且在放入的過程當中, 若是大小根堆的差值超過1,則經過彈出、加入的方式調整堆。
最後若是整個數據流吐出是奇數時,則把數據比較大的那個堆頂返回;偶數時,則返回兩個堆頂元素的平均值。
原文地址 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫助到了你,歡迎點贊和關注。你的star✨、點贊和關注是我持續創做的動力!