前言 

 前面差不多学习了插入排序、选择排序、冒泡排序、归并排序。这些排序除了归并排序在时间上消耗为:θ(nlgn)外,其余排序时间消耗都为:θ(n2). 

接下来要讲的就是两种比较优雅的比较排序算法:堆排序和快速排序。 堆排序最坏情况下可以达到上界:ο(nlgn).快速排序平均情况下可以达到:θ(nlgn)。

 

堆排序

  堆排序的关键在于完全二叉树。堆排序开始要构建一个完全二叉树,且该完全二叉树必须满足某一个结点大于左子节点的值和右子结点的值。我们假设根结点中的值为数组中下标为0的元素,根结点的左子结点下标为1,根结点的右子结点下标为2...依此类推。假设某一个结点的值对应数组中下标为i的元素,那么该结点的左子结点的值对应数组的数组元素下标为(2*i+1),右子结点的值对应数组元素下标为(2*i+2)。堆排序的步骤是:

1. 首先要构造一个以数组中下标为0的元素作为根结点的完全堆二叉树。此二叉树构造完成之后可以保证根结点的值为数组中最大的元素且对于堆中的所有结点的值都大于它的左子结点和右子结点的值。

2. 将堆中的根结点和堆中最后一个结点替换,然后重新构造以数组下标为0,长度为数组长度减1的完全堆二叉树。

3. 重复第二部直到需要构建的堆中元素只剩下唯一一个。

具体代码实现如下:

  

 1 public static void main(String[] args)
 2     {
 3         int[] arr = {1, 12, 11, 4, 5, 7, 3, 1, 23, 56, 34, 76, 25};
 4         heapSort(arr);
 5     }
 6 
 7     /**
 8      * 堆排序
 9      * @param arr 待排序的数组
10      */
11     private static void heapSort(int[] arr) {
12         for (int i=arr.length/2; i>=0; i--)
13         {
14             buildHeapTree(arr, i, arr.length);
15         }
16         
17         for (int i=arr.length-1; i>=0; i--)
18         {
19             swap(arr, 0, i);        // 交换根结点和堆树中最后一个结点
20             buildHeapTree(arr, 0, i);    // 再次以下标为0的元素开始构建层次递减的堆树
21         }
22         
23         System.out.println(Arrays.toString(arr));
24     }
25     
26     /**
27      * 构建堆树(满足时一颗完全二叉树,且任何结点大于他的子结点)
28      * @param arr  传入的数组
29      * @param rootIndex 根结点对应的数组下标(即以当前下标作为根结点来将数组中改下标之后的元素作为其子节点的堆树)
30      * @param deep     堆的深度(对于超过堆深度的数组中的元素不进行堆树的构建)
31      */
32     private static void buildHeapTree(int[] arr, int rootIndex, int deep) {
33         int leftChildIndex = rootIndex * 2 + 1;
34         int rightChildIndex = rootIndex * 2 + 2;
35         int maxIndex = rootIndex;
36         if (leftChildIndex < deep && arr[leftChildIndex] > arr[rootIndex])
37         {
38             // 如果左子结点大于右子结点,说明当前最大结点的下标应该为左子结点的下标
39             maxIndex = leftChildIndex;
40         }
41         
42         if (rightChildIndex < deep && arr[rightChildIndex] > arr[maxIndex])
43         {
44             // 如果右子结点的值大于与左子结点比较之后得到的最大的结点的值,则说明最大结点的下标应该为右子结点的下标
45             maxIndex = rightChildIndex;
46         }
47         
48         if (maxIndex != rootIndex)
49         {
50             // 以当前下标从数组中的元素开始没法够造成堆树,此时需要在树中将最大值所在的结点和根结点的位置互换,又因为堆树的构建是自下往上的,互换了之后需要考虑对前面最大值所在堆树的影响,所以需要再次递归调用以前面最大值元素所在位置为根结点构建堆树、
51             swap(arr, maxIndex, rootIndex);
52             buildHeapTree(arr, maxIndex, deep);
53         }
54     }
55 
56     private static void swap(int[] arr, int index1, int index2) {
57         int temp = arr[index1];
58         arr[index1] = arr[index2];
59         arr[index2] = temp;
60     }
HeapSort

相关文章: