堆排序

每天不emo發表於2024-09-03

定義

堆是一棵完全二叉樹。分為大頂堆和小頂堆
大頂推:所有節點都大於等於它的兩個子節點
小頂堆:所有節點都小於等於它的兩個子節點

虛擬碼

推排序步驟,以升序排列為例,用大頂堆。(降序排列,用小頂堆)

  1. 構建大頂推
  2. 把堆頂元素和堆尾元素交換,此時堆尾元素是最大的,堆的大小減一
  3. 堆頂元素下沉到指定位置
  4. 重複2-3步,直到堆的大小為1

關鍵在於如何構建一個大頂堆(對節點進行下沉):
從最後一個非葉子節點開始;
逐個檢查每個節點,如果一個節點的值小於其子節點的值,我們就將它與較大的子節點交換位置;
交換後,這個交換後的節點可能導致不滿足堆的特性,因此還需要繼續下沉(Heapify)

舉個例子

需要注意的是:對於一個完全二叉樹
它的最後一個非葉節點下標是 Math.floor(len/2) - 1,len是二叉樹的節點個數
下標為i的節點,它的左子節點是2*i+1, 右子節點是2*i+2

比如說對於[5,2,7,3,6,1,4]
最後一個非葉子節點下標是Math.floor(len/2) - 1=2, 也就是說最後一個非葉子節點是7, 構建最大堆的過程如下:

時間複雜度

O(nlogn)

實現

function heapSort(arr) {
	// 構建最大堆
	let n = arr.length;
	for(let i = Math.floor(n/2 - 1); i>=0;i--) {
		heapify(arr, n, i) // 把下標為i的節點放到合適的位置上
	}

	while(n>1) {
		// 交換堆頂元素和堆尾元素
		[arr[0], arr[n-1]] = [arr[n-1], arr[0]]
		// 堆的個數減一
		n--
        // 把堆頂元素放到合適的位置
		heapify(arr, n, 0)
	}
}

function heapify(arr, n, i) {
	let largestIndex = i
	let leftIndex = 2 * i + 1
	let rightIndex = 2 * i + 2

	if(leftIndex < n && arr[leftIndex] > arr[largestIndex]) {
		largestIndex = leftIndex
	}
	if(rightIndex < n && arr[rightIndex] > arr[largestIndex]) {
        largestIndex = rightIndex
    }
    if(largestIndex!==i) {
	    [arr[i], arr[largestIndex]] = [arr[largestIndex], arr[i]]
	    heapify(arr, n, largestIndex)
    }
}

相關文章