堆排序
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
-
最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
-
创建最大堆(Build Max Heap):将堆中的所有数据重新排序
-
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算 [1]
C语言
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include <stdio.h>#include <stdlib.h>void swap(int* a, int* b){ int temp = *b; *b = *a; *a = temp;}void max_heapify(int arr[], int start, int end) { //建立父节点指标和子节点指标 int dad = start; int son = dad * 2 + 1; while (son <= end) //若子节点指标在范围内才做比较 { if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的 son++; if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数 return; else //否则交换父子内容再继续子节点和孙节点比较 { swap(&arr[dad], &arr[son]); dad = son; son = dad * 2 + 1; } }}void heap_sort(int arr[], int len) { int i; //初始化,i从最後一个父节点开始调整 for (i = len / 2 - 1; i >= 0; i--) max_heapify(arr, i, len - 1); //先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕 for (i = len - 1; i > 0; i--) { swap(&arr[0], &arr[i]); 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); int i; for (i = 0; i < len; i++) printf("%d ", arr[i]); printf("\n"); return 0;} |
C++
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#include <iostream>#include <algorithm>using namespace std;void max_heapify(int arr[], int start, int end) { //建立父节点指标和子节点指标 int dad = start; int son = dad * 2 + 1; while (son <= end) //若子节点指标在范围内才做比较 { if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的 son++; if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数 return; else //否则交换父子内容再继续子节点和孙节点比较 { swap(arr[dad], arr[son]); 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(arr[0], arr[i]); max_heapify(arr, 0, i - 1); }}void 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 < len; i++) cout << arr[i] << ' '; cout << endl; system("pause");} |
Java语言
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
/** * 选择排序-堆排序 * @param array 待排序数组 * @return 已排序数组 */ public static int[] heapSort(int[] array) { //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1 for (int i = array.length / 2 - 1; i >= 0; i--) { adjustHeap(array, i, array.length); //调整堆 } // 上述逻辑,建堆结束 // 下面,开始排序逻辑 for (int j = array.length - 1; j > 0; j--) { // 元素交换,作用是去掉大顶堆 // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置 swap(array, 0, j); // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。 // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因 // 而这里,实质上是自上而下,自左向右进行调整的 adjustHeap(array, 0, j); } return array; } /** * 整个堆排序最关键的地方 * @param array 待组堆 * @param i 起始结点 * @param length 堆的长度 */ public static void adjustHeap(int[] array, int i, int length) { // 先把当前元素取出来,因为当前元素可能要一直移动 int temp = array[i]; for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树 // 让k先指向子节点中最大的节点 if (k + 1 < length && array[k] < array[k + 1]) { //如果有右子树,并且右子树大于左子树 k++; } //如果发现结点(左右子结点)大于根结点,则进行值的交换 if (array[k] > temp) { swap(array, i, k); // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断 i = k; } else { //不用交换,直接终止循环 break; } } } /** * 交换元素 * @param arr * @param a 元素的下标 * @param b 元素的下标 */ public static void swap(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } |
Python语言
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
def big_endian(arr,start,end): root=start child=root*2+1 #左孩子 while child<=end: #孩子比最后一个节点还大,也就意味着最后一个叶子节点了,就得跳出去一次循环,已经调整完毕 if child+1<=end and arr[child]<arr[child+1]: #为了始终让其跟子元素的较大值比较,如果右边大就左换右,左边大的话就默认 child+=1 if arr[root]<arr[child]: #父节点小于子节点直接交换位置,同时坐标也得换,这样下次循环可以准确判断:是否为最底层, #是不是调整完毕 arr[root],arr[child]=arr[child],arr[root] root=child child=root*2+1 else: break def heap_sort(arr): #无序区大根堆排序 first=len(arr)//2 - 1 for start in range(first,-1,-1): #从下到上,从左到右对每个节点进行调整,循环得到非叶子节点 big_endian(arr,start,len(arr)-1) #去调整所有的节点 for end in range(len(arr)-1,0,-1): arr[0],arr[end]=arr[end],arr[0] #顶部尾部互换位置 big_endian(arr,0,end-1) #重新调整子节点的顺序,从顶开始调整 return arr def main(): l=[3,1,4,9,6,7,5,8,2,10] print(heap_sort(l))if __name__=="__main__": main() |
PHP语言
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
function hsort(array &$arr, $len){ $idx = $len - 1; //创建堆操作,并使得堆有序 for($k=floor($len/2); $k>=0; $k--){ sink($arr, $k, $idx); } //排序操作 while($idx>0){ //获取最大值操作 list($arr[0], $arr[$idx]) = [$arr[$idx], $arr[0]]; $idx--; //堆调整下沉 sink($arr, 0, $idx); }}//堆调整下沉,小数字下沉function sink(array &$arr, $low, $high){ //从$low开始找子节点 while($low*2+1<=$high){ //获取low的直接左子节点 $j = $low*2+1; //判断$low位置节点子节点中的较大子节点 if($j<$high && $arr[$j]<$arr[$j+1]){ $j++; } if($arr[$low]>$arr[$j]){ break; } //交换low节点和子节点的值 list($arr[$low], $arr[$j]) = [$arr[$j], $arr[$low]]; //继续排查下沉后子节点是否需要进行下沉操作 $low = $j; }}$a = [4,3,6,7,5,1,9,0,2];hsort($a, count($a));print_r($a); |
成为不了聪明的人,那就做一个有耐心、肯钻研,坚持不懈,永不放弃的人……

浙公网安备 33010602011771号