堆
要理解堆排序,首先要了解堆
這個數(shù)據(jù)結(jié)構(gòu),因為堆排序是利用堆
這種數(shù)據(jù)結(jié)構(gòu)而設(shè)計的一種排序算法缓升。堆
首先是一種完全二叉樹(滿二叉樹疯趟、完全二叉樹南誊、平衡二叉樹、二叉排序樹、紅黑樹or平衡二叉搜索樹、哈弗曼樹or最優(yōu)二叉樹)撇寞,其次滿足下面的性質(zhì):
- 每個結(jié)點的值都大于或等于其左右孩子結(jié)點的值,稱為大頂堆堂氯,即:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 蔑担;
- 每個結(jié)點的值都小于或等于其左右孩子結(jié)點的值,稱為小頂堆咽白,即:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] 啤握。
堆排序
堆排序是一種選擇排序,最壞晶框,最好排抬,平均時間復(fù)雜度均近似為O(nlogn),是不穩(wěn)定排序三妈。
算法流程主要包括2部分(以大頂堆為例):
- 構(gòu)建初始堆畜埋,比如造成一個大頂堆,那么整個序列的最大值就是堆頂?shù)母?jié)點畴蒲;
- 交換堆頂元素和末尾元素,此時末尾就為最大值对室。然后將剩余n-1個元素重新構(gòu)造成一個堆模燥,這樣會得到n個元素的次小值咖祭。如此反復(fù)執(zhí)行,便能得到一個有序序列了蔫骂。
其中構(gòu)建初始堆經(jīng)推導(dǎo)復(fù)雜度為O(n)么翰,在交換并重建堆的過程中,需交換n-1次辽旋,而重建堆的過程中浩嫌,根據(jù)完全二叉樹的性質(zhì),[log2(n-1),log2(n-2)...1]逐步遞減补胚,近似為nlogn码耐。所以堆排序時間復(fù)雜度一般認為就是O(nlogn)級。
下面貼上javascript代碼:
function sort(arr) {
//1.構(gòu)建大頂堆
for (var i = Math.floor(arr.length / 2) - 1; i >= 0; i--) {
//從第一個非葉子結(jié)點從下至上溶其、從右至左調(diào)整結(jié)構(gòu)
adjustHeap(arr, i, arr.length);
}
//2.調(diào)整堆結(jié)構(gòu) && 交換堆頂元素與末尾元素
for (var j = arr.length - 1; j > 0; j--) {
swap(arr, 0, j);//將堆頂元素與末尾元素進行交換
adjustHeap(arr, 0, j);//從第一個結(jié)點從下至上骚腥、從右至左調(diào)整結(jié)構(gòu)
}
}
function adjustHeap(arr, i, length) {
var temp = arr[i];
for (var k = i * 2 + 1; k < length; k = k * 2 + 1) {//從i結(jié)點的左子結(jié)點開始,也就是2i+1處開始
if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子結(jié)點小于右子結(jié)點瓶逃,k指向右子結(jié)點
k++;
}
if (arr[k] > temp) {//如果子節(jié)點大于父節(jié)點束铭,將子節(jié)點值賦給父節(jié)點(不用進行交換)
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;//將temp值放到最終的位置
}
function swap(arr, a, b) {
var temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
var arr = [22, 1, 324, 4, 6, 22, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1];
sort(arr);
console.log(arr)//[ 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 11, 22, 22, 324 ]
小頂堆排序的話只需要改adjustHead函數(shù)中的兩行代碼:
-
if (k + 1 < length && arr[k] < arr[k + 1])
改為if (k + 1 < length && arr[k] > arr[k + 1])
-
if (arr[k] > temp)
改為if (arr[k] < temp)
。
參考文獻: