堆排序

堆排序（Heapsort）是指利用-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-這種數據結構所設計的一種排序算法。-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-是一個近似完全二叉樹的結構，並同時滿足堆積的性質：即子節點的鍵值或索引總是小於（或者大於）它的父節點。

**概述**
若以升序排序說明，把陣列轉換成最大堆積（Max-Heap Heap），這是一種滿足最大堆積性質（Max-Heap Property）的二元樹：對於除了根之外的每個節點i, Aparent(i) ≥ Ai。
重複從最大堆積取出數值最大的結點（把根結點和最後一個結點交換，把交換後的最後一個結點移出堆），並讓殘餘的堆積維持最大堆積性質。

**-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-節點的訪問**
通常-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-是通過一維數組來實現的。在陣列起始位置為0的情形中：
*父節點i的左子節點在位置;
*父節點i的右子節點在位置;
*子節點i的父節點在位置;

**-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的操作**
在-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的資料結構中，-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中的最大值總是位於根節點（在優先隊列中使用-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的話-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中的最小值位於根節點）。-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中定義以下幾種操作：
*最大-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-調整（Max Heapify）：將-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的末端子節點作調整，使得子節點永遠小於父節點
*建立最大-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-（Build Max Heap）：將-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中的所有數據重新排序
*堆排序（HeapSort）：移除位在第一個數據的根節點，並做最大-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-調整的遞迴運算

**實作範例**

**C語言**

**C++**
#include 
#include 
using namespace std;
void max_heapify(int arr, int start, int end) {
// 建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son  arrson) // 如果父節點大於子節點代表調整完畢，直接跳出函數
return;
else { // 否則交換父子內容再繼續子節點和孫節點比較
swap(arrdad, arrson);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int arr, int len) {
// 初始化，i從最後一個父節點開始調整
for (int i = len / 2 - 1; i >= 0; i--)
max_heapify(arr, i, len - 1);
// 先將第一個元素和已經排好的元素前一位做交換，再從新調整(剛調整的元素之前的元素)，直到排序完畢
for (int i = len - 1; i > 0; i--) {
swap(arr0, arri);
max_heapify(arr, 0, i - 1);
}
}
int main() {
int arr = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
int len = (int) sizeof(arr) / sizeof(*arr);
heap_sort(arr, len);
for (int i = 0; i 

**Java**
import java.util.Arrays;
public class HeapSort {
private int arr;
public HeapSort(int arr) {
this.arr = arr;
}
/*     * 堆排序的主要入口方法，共兩步。
*/
public void sort() {
/         *  第一步：將數組堆化-  beginIndex = 第一個非葉子節點。-  從第一個非葉子節點開始即可。無需從最後一個葉子節點開始。-  葉子節點可以看作已符合堆要求的節點，根節點就是它自己且自己以下值為最大。
*/
int len = arr.length - 1;
int beginIndex = (arr.length >> 1)- 1;
for (int i = beginIndex; i >= 0; i--)
maxHeapify(i, len);
/         * 第二步：對堆化數據排序- 每次都是移出最頂層的根節點A0，與最尾部節點位置調換，同時遍歷長度 - 1。- 然後從新整理被換到根節點的末尾元素，使其符合堆的特性。- 直至未排序的堆長度為 0。
*/
for (int i = len; i > 0; i--) {
swap(0, i);
maxHeapify(0, i - 1);
}
}
private void swap(int i, int j) {
int temp = arri;
arri = arrj;
arrj = temp;
}
/*     * 調整索引為 index 處的數據，使其符合堆的特性。- @param index 需要堆化處理的數據的索引- @param len 未排序的堆（數組）的長度
*/
private void maxHeapify(int index, int len) {
int li = (index  len) return;      // 左子節點索引超出計算範圍，直接返回。
if (ri  arrli) // 先判斷左右子節點，哪個較大。
cMax = ri;
if (arrcMax > arrindex) {
swap(cMax, index);      // 如果父節點被子節點調換，
maxHeapify(cMax, len);  // 則需要繼續判斷換下後的父節點是否符合堆的特性。
}
}
/*     * 測試用例- 輸出：- 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9
*/
public static void main(String args) {
int arr = new int {3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6};
new HeapSort(arr).sort();
System.out.println(Arrays.toString(arr));
}
}

**Python**
#!/usr/bin/env python
#-*-coding:utf-8-*-
def heap_sort(lst):
def sift_down(start, end):
"""最大堆調整"""
root = start
while True:
child = 2 * root + 1
if child > end:
break
if child + 1 

**JavaScript**
Array.prototype.heap_sort = function() {
	var arr = this.slice(0);
	function swap(i, j) {
		var tmp = arri;
		arri = arrj;
		arrj = tmp;
	}
	function max_heapify(start, end) {
		//建立父節點指標和子節點指標
		var dad = start;
		var son = dad * 2 + 1;
		if (son >= end)//若子節點指標超過範圍直接跳出函數
			return;
		if (son + 1 = 0; i--)
		max_heapify(i, len);
	//先將第一個元素和已排好元素前一位做交換，再從新調整，直到排序完畢
	for (var i = len - 1; i > 0; i--) {
		swap(0, i);
		max_heapify(0, i);
	}
	return arr;
};
var a = 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6;
console.log(a.heap_sort());

**PHP**
**$end)//若子節點指標超過範圍直接跳出函數
		return;
	if ($son + 1 = 0; $i--)
		max_heapify($arr, $i, $len);
	//先將第一個元素和已排好元素前一位做交換，再從新調整，直到排序完畢
	for ($i = $len - 1; $i > 0; $i--) {
		swap($arr0, $arr$i);
		max_heapify($arr, 0, $i);
	}
	return $arr;
}
$arr = array(3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6);
$arr = heap_sort($arr);
for ($i = 0; $i 

**Go**
package main
import (
	"fmt"
)
func HeapSort(array int) {
	m := len(array)
	s := m/2
	for i := s; i > -1; i-- {
		heap(array, i, m-1)
	}
	for i := m-1; i > 0; i-- {
		arrayi, array0 = array0, arrayi
		heap(array, 0, i-1)
	}
}
func heap(array int, i, end int){
	l := 2*i+1
	if l > end {
		return
	}
	n := l
	r := 2*i+2
	if r arrayl{
		n = r
	}
	if arrayi > arrayn{
		return
	}
	arrayn, arrayi = arrayi, arrayn
	heap(array, n, end)
}
func main()  {
	array := int{
		55, 94,87,1,4,32,11,77,39,42,64,53,70,12,9,
	}
	HeapSort(array)
	fmt.Println(array)
}

**Julia (程式語言)**
function HeapSort(array)
function adjust(l,u)
while true
j = 2*l # 左子點
if (j>u) # 代表沒有子點
break
end 
if ((j+1)  arrayl) #比較父點跟子點
arrayl, arrayj= arrayj, arrayl
l = j # 有交換
else
break # 沒交換跳出迴圈
end
end
end
n = length(array)
for i in n:-1:1 # 建 max Heap
adjust(i,n)
end 
#持續把第一個(最大)的元素最後一個交換
arrayn,array1=array1,arrayn 
for i in n-1:-1:1
adjust(1,i)
arrayi,array1=array1,arrayi
end
return array
end

**Rust**
fn max_heapify(data: &mut T, pos: usize, end: usize) {
let mut dad = pos;
let mut son = dad * 2 + 1;
while son  datason {
return;
} else {
data.swap(dad, son);
dad = son;
son = dad * 2 + 1;
}
}
}
fn heap_sort(data: &mutT) {
let len = data.len();
for i in (0..=len / 2 - 1).rev() {
max_heapify(data, i, len - 1);
}
for i in (1..=len - 1).rev() {
data.swap(0, i);
max_heapify(data, 0, i - 1);
}
}
fn main() {
let mut nums = vec!9, 2, 1, 7, 6, 8, 5, 3, 4;
heap_sort(nums.as_mut_slice());
}

**原地堆排序**
基於以上-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-相關的操作，我們可以很容易的定義堆排序。例如，假設我們已經讀入一系列數據並創建了一個-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-，一個最直觀的算法就是反覆的調用del_max()函數，因為該函數總是能夠返回-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中最大的值，然後把它從-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-中刪除，從而對這一系列返回值的輸出就得到了該序列的降序排列。真正的原地堆排序使用了另外一個小技巧。堆排序的過程是：
# 建立一個-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-
# 把-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-首（最大值）和-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-尾互換
# 把-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的尺寸縮小1，並調用shift_down(0),目的是把新的數組頂端數據調整到相應位置
# 重複步驟2，直到-{zh-cn:堆;zh-tw:堆積;zh-hk:堆積;}-的尺寸為1

**平均複雜度**
堆排序的平均時間複雜度為，空間複雜度為。

**參考**

**外部連結**
 常見排序算法 - 堆排序 (Heap Sort)-  堆算法的可視化演示 可以直觀看到堆算法的操作