堆排序

堆排序基本介绍

1) 堆排序是利用堆(heap)这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为 O(nlogn),它也是不稳定排序

堆排序的基本思想是:

1. 将待排序序列构造成一个大(小)顶堆
2. 此时,整个序列的最大(小)值就是堆顶的根节点。
3. 将其与末尾元素进行交换,此时末尾就为最大(小)值。
4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小(大)值。如此反复执行,便能得到一个有序序列了。

堆的介绍

1. 必须是完全二叉树
2. 每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆
3. 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
注意 : 没有求结点的左孩子的值和右孩子的值的大小关系,一般升序采用大顶堆,降序采用小顶堆 

大顶堆:

 

小顶堆:

 

 堆排序步骤图解

步骤一
构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

原始的数组 [4, 6, 8, 5, 9]

1) .假设给定无序序列结构如下


2) .此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点arr.length/2-1=5/2-1=1,也就是下面的 6 结点),从右至左,从下至上进行调整。

3) .找到第二个非叶节点 4,由于[4,9,8]中 9 元素最大,4 和 9 交换。

 

 

4) 这时,交换导致了子根[4,5,6]结构混乱,继续调整(通过递归方法完成),[4,5,6]中 6 最大,交换 4 和 6。

此时,我们就将一个无序序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换
得到第二大元素。如此反复进行交换、重建、交换。

1) .将堆顶元素 9 和末尾元素 4 进行交换

2) .重新调整结构,使其继续满足堆定义

3) .再将堆顶元素 8 与末尾元素 5 进行交换,得到第二大元素 8.

4) 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

 

 代码实现

1. //要求将数组进行升序排序  
2. public class HeapSort {  
3.     public static void main(String[] args) {  
4.         int[] arr = {11, 100, 108, -17, 125, 13, 114, 21};  
5.   
6.         heapSort(arr, arr.length);  
7.   
8.         for (int data : arr) {  
9.             System.out.println(data);  
10.         }  
11.     }  
12.   
13.     /** 
14.      * @param arr 要变成堆的数组 
15.      * @param len 数组的长度 
16.      * @param i   对哪一个节点进行堆化 
17.      * @TODO 将某棵完全二叉树进行堆化操作 
18.      */  
19.     public static void heapify(int[] arr, int len, int i) {  
20.         if (i >= len) {//递归结束条件  
21.             return;  
22.         }  
23.         int left = i * 2 + 1;//i结点的左子结点  
24.         int right = i * 2 + 2;//i结点的右子结点  
25.         int max = i;//默认令下标为i的结点为最大值  
26.         if (left < len && arr[left] > arr[max]) {//找到当前二叉树左子结点与该节点的较大值  
27.             max = left;  
28.         }  
29.         if (right < len && arr[right] > arr[max]) {//将上面比较出的较大值与该节点的右子结点比较获取最大值  
30.             max = right;  
31.         }  
32.         if (max != i) {//如果max不等于i,说明i结点不是最大值,因此就需要将i结点值与最大值的结点进行交换  
33.             int temp = arr[max];  
34.             arr[max] = arr[i];  
35.             arr[i] = temp;  
36.             /*交换完成后,就可以保证i结点的二叉树是堆化的, 
37.             但不能保证左右子结点的子树(i的左右子结点为父节点时) 
38.             是否也是堆化因此需要递归操作进行之后的堆化*/  
39.             heapify(arr, len, max);  
40.   
41.         }  
42.     }  
43.   
44.     /** 
45.      * 通过此方法就可将整棵树进行堆化 
46.      * 流程:从最后一个父节点(注意不是叶子节点)开始进行堆化,直到根节点 
47.      * 
48.      * @param arr 
49.      * @param len 
50.      */  
51.     public static void buildHeap(int[] arr, int len) {  
52.         int lastNode = len - 1;//最后一个叶子节点  
53.         int i = (lastNode - 1) / 2;//最后一个结点的父节点,即最后一个父结点  
54.         for (int j = i; j >= 0; j--) {//从最后一个父节点开始进行堆化,直到根节点(j==0)  
55.             heapify(arr, len, j);  
56.         }  
57.     }  
58.   
59.     /** 
60.      * 此方法能够将数组进行堆排序(有序(列)化) 
61.      * 
62.      * @param arr 要排序的数组 
63.      * @param len 要排序的数组长度 
64.      */  
65.     private static void heapSort(int[] arr, int len) {  
66.         buildHeap(arr, len);//首先通过此方法将其堆化,变为大顶堆(arr[0]为树中最大值)  
67.         for (int i = len - 1; i > 0; i--) {//从最后一个节点开始,i可以不用等于0,因为最后一个数一定是有序的  
68.             int temp = arr[i];  
69.             arr[i] = arr[0];  
70.             arr[0] = temp;//将arr[0]与arr[i]进行交换,arr[i]之后都是确定有序的  
71.             heapify(arr, i, 0);//交换完成后,又不是大顶堆规则了,需要再次堆化,找到次大值。注意现在在0到i之中找出最大值  
72.         }  
73.     }  
74.   
75. }  

 

posted @ 2020-11-02 16:28  白刃天使  阅读(449)  评论(0编辑  收藏  举报