您的位置:首页 > 其它

改进排序算法:堆排序(对简单选择排序的改进)

2017-11-07 20:02 429 查看
简单选择排序:在待排序的n个记录中选择一个最小的记录需要比较n-1次。但是并没有把每一次的比较结果保存下来,在后一次的比较中,有许多比较在前一次已经做过了,但由于前一次排序时未保存这些比较结果,所以后一次排序时又重复执行了这些比较操作,因而比较次数较多。

堆排序:每次在选择到最小记录的同时,根据比较结果对其他记录做出相应的调整,使得排序的总体效率变高。

大顶堆:每个节点的值都大于或等于其左右孩子结点的值

小顶堆:每个节点的值都小于或等于其左右孩子结点的值

按照层序遍历的方式,给结点从1开始编号,则结点之间满足:

ki>=k2i,ki>=k2i+1或ki<=k2i,ki<=k2i+1,其中1<=i<=[n/2]

一颗完全二叉树,如果i=1,则结点i是二叉树的根,无双亲;如果i>1,其双亲是结点[i/2]。

堆排序:利用堆进行排序的方法(假设利用大顶堆)。

将待排序的序列构造成一个大顶堆。整个序列的最大值就是堆顶的根节点,将它移走(将其与堆数组的末尾元素交换,此时末尾元素是最大值),然后将剩余n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复。

如何将待排序的序列构建成一个大顶堆:从下往上,从右往左,将每个非终端节点(非叶节点)当做根节点,将其和其子树调整成大顶堆。

在JAVA中,数组下标从0开始,若将一个完全二叉树按照层序遍历的方式存入数组中,节点i的左孩子为2i+1,右孩子为2i+2,有孩子的结点范围为0到arr.length/2 - 1。

从小到大排序,建立一个大顶堆;从大到小排序,建立一个小顶堆

import java.util.Arrays;

public class Solution {
public static void main(String[] args) {
Solution s = new Solution();
int[] arr = {4,5,1,6,2,7,3,8,8,4};
s.HeapSort(arr);
System.out.println(Arrays.toString(arr));    //输出:[1, 2, 3, 4, 4, 5, 6, 7, 8, 8]
}

private void HeapSort(int[] arr)
{
for (int i = (arr.length - 2) / 2; i >= 0; i--) //从0到(arr.length - 2) / 2都是有孩子的节点。第一个循环将待排序序列构建成一个大顶堆。
HeapAdjust(arr, i, arr.length);
for (int i = arr.length - 1; i >= 0; i--) //第二个循环逐步将每个最大值的根节点与末尾节点交换,并且在调整其成为大顶堆。
{
swap(arr, 0, i );
HeapAdjust(arr, 0, i);
}
}

private void HeapAdjust(int[] arr, int s, int m) //m表示数组长度
{
int temp = arr[s];
int j = 2 * s + 1;
for (; j < m; j = 2 * j + 1) //数组下标从0开始,第i个节点的左孩子为2i+1,右孩子为2i+2
{
if (j < m - 1 && arr[j] < arr[j + 1])
++j;
if (temp >= arr[j])
break;
arr[s] = arr[j];
s = j;
}
arr[s] = temp;
}
private void swap(int[] arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

}


堆排序复杂度分析:

时间复杂度:

运行时间主要消耗在初始构建堆和在重建堆的反复筛选上。

在构建堆的过程中,完全二叉树从最下层最右边的非终端节点开始构建的,将它与其孩子节点进行比较和若有必要的互换。对于每个非终端节点,最多进行两次比较和互换操作。构建堆的时间复杂度为O(n)。

在正式排序时,第i次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个节点到根节点的距离为[logi] + 1),并且需要取n-1次堆顶记录。重建堆的时间复杂度为O(nlogn)。

总体来说,堆排序的时间复杂度为O(nlogn)。

并且堆排序对原始记录的排序状态并不敏感,无论最好或最坏或平均时间复杂度均为O(nlogn)。远优于冒泡、简单选择和直接插入的O(n^2)的时间复杂度。

空间复杂度:

只有一个用来交换的暂存单元。

由于记录的比较和交换是跳跃式进行,堆排序也是一种不稳定的排序方法。

由于初始构建堆所需的比较次数较多,所以不适合待排序序列个数较少的情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐