堆排序
同步微信公眾號樂享Coding,歡迎你的關注!
介紹:利用堆這種資料結構而設計的一種排序演算法,堆排序是一種選擇排序,它的最壞,最好,平均時間復雜度均為O(nlogn),它是不穩定排序,
對于堆排序,難點在于二叉樹的順序陣列儲存到大頂堆(小頂堆)的轉換,從資料存盤來看,陣列存盤方式和樹的存盤方式可以相互轉換,即陣列可以轉換成樹,樹也可以轉換成陣列,我習慣于不空談理論,拿實體講解,以下將針對陣列arr[1,2,5,4,3,7]進行大頂堆的資料結構轉換,
如圖:

堆是具有以下性質的完全二叉樹:每個結點的值都大于或等于其左右孩子結點的值,稱為大頂堆,每個結點的值都小于或等于其左右孩子結點的值,稱為小頂堆,
代碼實作思路:
步驟一 構造初始堆,將給定無序序列構造成一個大頂堆(一般升序采用大頂堆,降序采用小頂堆),

圖解大頂堆詳細思路:
-
我們從最后一個非葉子結點開始(葉結點自然不用調整,第一個非葉子結點 arr.length/2-1=5/2-1=1,也就是下面的索引為2的結點),從
右至左,從下至上進行調整,
-
由于[5,7]中7元素最大,5和7交換,

-
最后一個非葉子結點索引減1,找到第二個非葉結點(索引1),由于[4,3,2]中4元素最大,2和4交換,

-
非葉子結點索引減1,找到第三個非葉結點(索引0),由于[4,1,7]中7元素最大,1和7交換,

-
這時,交換導致了子根[1,5]結構混亂,繼續調整,[1,5]中5最大,交換1和5,

-
此時,我們就將一個無序序列構造成了一個大頂堆,

步驟二 將堆頂元素與末尾元素進行交換,使末尾元素最大,然后繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素,如此反復進行交換、重建、交換,

最后,就實作了堆排序,

文章末尾將代碼附上,如有不清楚的地方,可以Debug加深下理解!
public static void heapSort(int[] arr) {
//1.構建大頂堆
for (int i = arr.length / 2 - 1; i >= 0; i--) {
//從第一個非葉子結點從下至上,從右至左調整結構
adjustHeap(arr, i, arr.length);
}
//然后繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素,如此反復進行交換、重建、交換,
//2.調整堆結構+交換堆頂元素與末尾元素
for (int j = arr.length - 1; j > 0; j--) {
swap(arr, 0, j);//將堆頂元素與末尾元素進行交換
adjustHeap(arr, 0, j);//重新對堆進行調整
}
}
/**
* 調整大頂堆(僅是調整程序,建立在大頂堆已構建的基礎上, 也就是說只呼叫一次,并沒有得到大頂堆)
* 就是將arr[i] 的值放到本次 調整程序中適當的位置,
* @param arr : 陣列
* @param i : 非葉子節點的索引
* @param length : 對多少個元素進行調整,這個length是逐漸減少的..
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];//先取出當前元素i
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//從i結點的左子結點開始,也就是2*i+1處開始
if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子結點小于右子結點,k指向右子結點
k++;
}
if (arr[k] > temp) {//如果子節點大于父節點,將子節點值賦給父節點(不用進行交換)
arr[i] = arr[k];//把較大的值,賦給當前節點
i = k;//i 指向k,繼續回圈比較
} else {
break;
}
}
arr[i] = temp;//將temp值放到最終的位置
}
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/243931.html
標籤:其他
上一篇:2020,我一定會回來的!
