堆詳解

徹底二叉樹

在二叉樹中每一層要麼是滿的,要麼是從左到右依次變滿。git

而堆就是一棵徹底二叉樹。除了有徹底二叉樹的特性外,對於堆,任何一個節點爲頭的子樹最大值(或者最小值)爲頭節點本身。github

9
   / \
  3   6
 / \
1   2 
複製代碼

以上就是一個大根堆算法

實現

用數組結構實現。好比對於堆存儲的數組中是 [9,3,6,1,2],對於任意 i 位置來講,左孩子是2*i+1,右孩子是2*i+2,父節點是 (i-1)/2api

基本操做

對於堆來講,有兩個基本操做heapInsert(往堆中插入元素)、heapify(調整堆的位置)。所以掌握這兩個操做是重點。數組

場景: 好比對於堆 [5, 1, 4, 0,], insert 3 後。這就不是大根堆了,所以須要調整。bash

5
   / \
  1   4
 / \
0   3
複製代碼

heapInsert

一、定義變量 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)]) {}

複製代碼

heapInsert代碼函數

heapify

場景: 好比對於堆 [5, 4, 1, 0, 3],彈出堆頂元素 5 ,把剩餘的元素繼續調整堆結構。ui

5
   / \
  4   1
 / \
0   3
複製代碼

僞代碼:spa

一、把堆頂元素記錄下來,以備之後返回,而後把堆中最後一個元素覆蓋堆頂,heapSize--。
二、i 和 i 左、右孩子較大一個進行比較,若是孩子大,則把 i 和左右孩子中較大一個進行交換,交換後,i 移動到較大孩子的節點。
三、循環執行2,直到 i 比左右孩子較大的那個小,或者 i > heapSize
複製代碼

heapify代碼設計

堆排序

堆排序就是把 heapInsert 和 heapify 結合起來的一個算法

一、先經過 heapInsert 把一個數組調整成大根堆
二、把堆頂元素和堆最後一個元素交換,而後 heapSize--。根據堆頂進行 heapify 操做。
三、重複執行2,直到 heapSize === 0,說明數組已經排好序
複製代碼

heapSort代碼

複雜度分析

時間複雜度

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)

大根堆

任何一個節點爲頭的子樹最大值爲頭節點本身。

大根堆代碼

小根堆

任何一個節點爲頭的子樹最小值爲頭節點本身。

小根堆代碼

如圖經過上面小根堆代碼一次彈出堆頂元素,是依次變大的。

bitMap

應用堆排序的思想的題目

獲取中位數

有一個源源不斷吐出整數的數據流,假設你有足夠的空間來保存吐出的數。
請設計一個叫medianHolder函數。這個函數能夠隨時吐出這些數的中位數。
要求
一、若是MedianHolder已經保存了吐出的N個數,那麼任意時刻將一個新數加入到MedianHolder的過程,其時間複雜度是 O(logN)
二、隨時吐出整數的時間複雜度是 O(1)
複製代碼

這個數組中較大的 N/2 放在小根堆中,較小的 N/2 放到大根堆中,而且在放入的過程當中, 若是大小根堆的差值超過1,則經過彈出、加入的方式調整堆。

最後若是整個數據流吐出是奇數時,則把數據比較大的那個堆頂返回;偶數時,則返回兩個堆頂元素的平均值。

代碼

原文地址 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫助到了你,歡迎點贊和關注。你的star✨、點贊和關注是我持續創做的動力!

相關文章
相關標籤/搜索