堆結構:就是一顆完全二叉樹,二叉樹是不存在的,可以腦補,真正實現堆結構的是陣列
葉節點:沒有左右孩子的節點
滿二叉樹:最後一層都是葉節點,並且填滿最後一層
完全二叉樹:滿二叉樹屬於完全二叉樹,滿二叉樹從右往左依次去葉節點形成的樹就是完全二叉樹
陣列下標位置為i,在不越界的情況下:
2 * i+1是i位置數的左孩子
2 * i+2是i位置數的右孩子
(i-1)/2是父節點
越界可以認為沒有
堆結構分為大根堆和小根堆:
大根堆:在一顆完全二叉樹中,任何一顆子樹的最大值都是這顆子樹頭部
小根堆:在一顆完全二叉樹中,任何一顆子樹的最小值都是這顆子樹頭部
將陣列變為大根堆(建立大根堆 heapinsert):
將新的節點加到大根堆中,往上調整,繼續形成大根堆的過程。
思路:假如乙個陣列有5個數5,4,2,6,7 陣列中的位置分別為:0,1,2,3,4
假設0 - 4位置上的任意位置為i,我們把這個陣列建立成大根堆的過程,先看成 0 - i建立成大根堆,超過i我們就認為越界,比如,0位置元素為5,則5為根節點,我們就認為 0 - 0已經形成了大根堆,然後將 1位置的元素4加入到大根堆中,由上述公式的 4是5的左孩子,因為4 < 5,所以不用向上調整,此時0 -1就建立成大根堆,然後將2位置的元素2加入到大根堆,2是5的右孩子,2 < 5,不用向上調整,將3位置元素6加入到大根堆中,6是4的左孩子,6 >4,需要向上調整,和其父節點交換位置,此時變為:5,6,2,4,7,然後在比較與其新的父節點5的大小,6 > 5,再次向上調整,和其父節點交換位置,此時變為:6,5,2,4,7 由公式7是5的右孩子 7 > 5,和5交換位置,又因為7 > 6再次交換位置,此時變為:7,6,2,4,5,也就完成了大根堆的建立
由上述過程我們可以發現,當加入乙個新的節點時,需要向上調整的次數和這顆完全二叉樹的層次情況(在最差情況下),也就是說當我們加入第i個節點時,需要判斷前i - 1個節點構成了多少層,log(i - 1)層,所以整個建立大根堆的時間複雜度就變為
log(1) + log(2) + log(3) + …+ log(n - 1),由數學知識,該公式收斂於n,所以建立大根堆的時間複雜度為o(n)
小根堆同理
**實現:
public
static
void
heapinsert
(int
arr,
int index)
陣列中有乙個值變小了,不再是大根堆,該值往下沉,調整成大根堆的過程(heapify):
先比較該節點的左右孩子的大小,然後再和該值進行比較,如果大於該值,則交換,下沉,否則所有位置都不動,重複此過程,直到該節點的左右孩子小於該值
**實現:
public
static
void
heapify
(int
arr,
int index,
int heapsize)
swap
(arr, index, largest)
; index = largest;
left = index *2+
1;}}
public
static
void
swap
(int
arr,
int i,
int j)
堆排序public
static
void
heapsort
(int
arr)
//建立大根堆
for(
int i =
0; i < arr.length; i++
)//調整大根堆
int size = arr.length;
swap
(arr,0,
--size)
;while
(size >0)
}
資料結構與演算法之堆排序
堆排序 先用n個待排序的元素來初始化乙個大根堆,然後從堆中逐個提取元素 刪除 每次都取堆頂的元素,將其放在序列最後面,然後將剩餘的元素重新調整為最大堆,依次類推,最終得到排序的序列。結果這些元素按照非遞增的順序排列。初始化時間為o n 每次刪除的時間為o logn 因此總時間為o nlogn inc...
演算法與資料結構 之堆排序
堆的應用 優先佇列的主要操作 例子 在n個元素中選出前m個元素?比如,在100萬個元素中選出前100名 排序,nlogn 優先佇列,nlogm 維護乙個容量為m的最小堆,遍歷完100萬個陣列之後,形成的最小堆中的m個元素就是最小的 總共n個請求,使用普通陣列或者順序陣列,最差情況o n 2 使用堆,...
資料結構與演算法之堆排序
這是我最近幾天寫排序的最後乙個演算法排序了 我覺得這個堆排序和歸併排序可以說是這幾個演算法中寫起來最困難的 了。堆排序 是指利用堆這種資料結構所設計的一種排序演算法。因為堆是乙個近似完全二叉樹的結構,並同時滿足堆積的性質 即子結點的鍵值或索引總是小於 或者大於 它的父節點,所以建堆就和建樹一樣,必須...