數組可以看成是一個完全二叉樹,大根堆是一個完全二叉樹
構造大根堆
例子1:[O(N)---->從下到上]
因爲堆是對父節點-左/右孩子節點之間的約束,所以從最後一個非葉子節點開始調整。
注意每次交換後,都要對下一層的子堆進行遞歸調整,因爲交換後有可能破壞已調整子堆的結構。
堆排序
例子2:[O(N)---->從下到上]
1、假設給定無序序列結構如下
2.此時我們從最後一個非葉子結點開始(葉結點自然不用調整,第一個非葉子結點 arr.length/2-1=5/2-1=1,也就是下面的6結點),從左至右,從下至上進行調整。【9下沉之後,9變成了葉子節點,因此不會對子葉產生影響】
4.找到第二個非葉節點4 【3/2 - 1 = 0】,由於[4,9,8]中9元素最大,4和9交換。【4下沉之後,變動了的子樹必須重新調整】
這時,交換導致了子根[4,5,6]結構混亂,繼續調整,[4,5,6]中6最大,交換4和6。
此時,我們就將一個無需序列構造成了一個大頂堆。
第3個例子【從上到下 】
從一個空的二叉樹開始:
從頭開始遍歷數組:
for(int i = 0; i < arr.length; i++){
選出arr[i],插入二叉樹
}
第一次循環:當前需要插入的索引是0,令cur = 0,它的父節點parent = (cur - 1) / 2 = 0 。
- arr[父節點索引] == arr[當前節點索引] ,因此進入下一輪循環
第二次循環:當前需要插入的數據索引是1【需要插入的二叉樹的位置索引也是1】,令cur = 1,它的父節點索引是 parent = (cur - 1) / 2 = 0, - arr[cur] 與arr[parent]比較,因爲 當前節點值 > 父節點值,因爲需要最大值登頂,因此進入內循環:
- 交換 當前節點和父節點的值
- 將當前索引指向父節點,重新計算父節點
- 因爲cur = 0, par = 0,兩者相等,因此跳出內循環
進入下一輪循環
第三次循環:需要插入的索引爲2,令cur = 2,其父節點 par = (2 - 1)/2 = 0,
- 當前索引值73與父節點值25比較,當前索引比較大,進入內循環:
- 交換當前節點與父節點的值
- 當前索引指向父節點索引,重新計算父節點的索引
- 因爲cur = 0, par = 0,因此跳出循環
第4次循環,需要插入的索引爲3,令cur =3,其父節點 par = (3 - 1)/2 = 1,
- 當前索引值98與父節點值10比較,當前索引比較大,進入內循環:
- 交換當前節點與父節點
- 令cur = 夫節點 = 1,重新計算父節點 par = 0
判斷是否還是在內循環中:
- 當前cur = 1, par = 0,因爲當前節點98比父節點73大,進入內層循環
- 交換98和73的位置
- 令cur = 父節點的索引 = 0, 重新計算par = (cur -1) / 2 = 0
第4次循環,需要插入的索引爲4,令cur =4,其父節點 par = (4- 1)/2 = 1,
第5次循環,需要插入的索引爲5,令cur =5,其父節點 par = (5- 1)/2 = 2,
**** 一直循環,直到當前數組循環完畢
總結: 上面的過程可以看成是向數組中不斷插入一個元素【向大根堆中插入一個元素】,總結上面經驗得知:
- 元素的插入索引是固定的【一定都是size】,但是插入的位置是不一定的。
- 如果插入的數據小於等於他的父索引【(size - 1)/2】,什麼也不幹
- 如果插入的數據大於它的父索引【(size - 1)/2】,那麼就讓這個數據不斷上浮,直到找到了應該的位置就表示插入成功了
節點上浮:當我們在向最大堆中插入一個節點時,我們必須滿足完全二叉樹的標準,那麼被插入節點的位置是固定的,而且要滿足父節點關鍵值不小於子節點關鍵值,那麼我們就需要去移動父結點和子結點的相互位置關係。
堆排序
例子1:
進行調整後,堆頂元素(array[0])爲最大值,將最大值與堆尾部元素(array[count-1])交換,並將count值減去1,則此時得到新的無序數組array[count],此時的堆被破壞;
對應到數組元素爲:
調整堆:與建堆過程類似,堆頂元素被一個比較小的值代替,所以從堆頂元素開始調整,在堆頂、堆頂的左孩子、堆頂的右孩子中找出最大的與堆頂進行交換,被交換的元素再次與他下一層的左右孩子進行比較(遞歸)調整。
然後一直重複,直到count = 0
例子2:
如果要排序:我們一般從最上面的那個元素開始。
從0開始遍歷數組:
令cur = 0, 並且cur 與 size - 1的元素交換位置,
代碼實現
大根堆進行從大到小的排序
public class TreeNode {
// 從最後一個非葉子節點開始調整,調整爲一個大根堆
private static void BuildMaxHeap(int[]arr){
if (arr == null || arr.length < 2){
return;
}
for (int i = arr.length/2 - 1; i > -1; i--){
adjustMaxHeap(arr, arr.length, i);
}
for (int i = arr.length; i > 0; i--){
swap(arr, i - 1, 0);
adjustMaxHeap(arr, i - 1, 0);
}
}
private static void adjustMaxHeap(int[]arr, int size, int i){
int left = 2 * i + 1;
int right = 2 * i + 2;
int max = i;
if (left < size && arr[left] > arr[max]){
max = left;
}
if (right < size && arr[right] > arr[max]){
max = right;
}
if (max != i){
swap(arr, i, max);
adjustMaxHeap(arr, size, max);
}
}
private static void swap(int []arr, int i, int j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int[] arr = new int[]{23,19,81,79,89,83,17,48,55,26};
BuildMaxHeap(arr);
System.out.println(Arrays.toString(arr));
}
}
大根堆中可能用到的行爲
// 從最好一個非葉子節點開始調整
// 下移過程
/**
* 將父節點爲aar[i]的子樹調整爲最大堆
* @param arr 堆數組
* @param size 堆數組長度
* @param index 節點索引
*/
private static void AdjustHeap(int[] arr, int size, int index){
if (arr == null || index < 0){
return;
}
int left = 2 * index + 1;
int right = 2 * index + 2;
int max = index;
if (left < size && arr[left] > arr[index]){
max = left;
}
if (right < size && arr[right] > arr[index]){
max = right;
}
if (max != index){
Swap(arr, max, index);
AdjustHeap(arr, size, max);
}
}
/**
* 根據輸入的數組構建一個最大堆
* @param arr 堆數組
* @param size 堆數組長度
* @return 堆數組長度
*/
private static int BuildMaxHeap(int arr[], int size) {
//對每一個非葉節點開始向下進行最大堆調整
for (int i = size / 2 - 1; i >= 0; i--)
{
AdjustHeap(arr, size, i);
}
return size;
}
/**
* 向指定的最大堆中插入節點:首先在堆的最後添加一個節點,然後沿着堆樹上升,直到堆樹再次調整爲最大堆
* @param arr
* @param size
* @param data
* @return
*/
int MaxHeapInsert(int arr[], int size,int data)
{
int index=size;
while (index>0 && data>arr[(index-1)/2])
{
arr[index]=arr[(index-1)/2];
index=(index-1)/2;
}
arr[index]=data;
return (size+1);
}
/**
* 最大堆堆頂節點的刪除:將堆樹中待刪除的節點A與堆樹中的最後一個節點B互換,然後調整節點B到合適的位置,最後從堆樹中刪除排在堆數組最後的節點A。
* @param arr 最大堆數組
* @param size 堆數組長度
* @return 刪除後的堆數組長度
*/
private static int MaxHeapDelete(int arr[], int size)
{
if(size<=0)return -1;
Swap(arr, 0, size - 1);
AdjustHeap(arr,size-1,0);//調整堆爲最大堆
return size-1;
}
private static void Swap(int arr[], int i, int j)
{
int temp=arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = new int[]{23,19,81,79,89,83,17,48,55,26,16,1,46,95,10};
BuildMaxHeap(arr, arr.length); //[95, 89, 81, 79, 26, 83, 23, 48, 55, 19, 16, 1, 46, 17, 10]
System.out.println(Arrays.toString(arr));
}