排序算法之堆排序

来源:互联网 发布:js 点击图片放大 编辑:程序博客网 时间:2024/06/11 22:15

注:此文需要对有序二叉堆、优先队列等知识有所了解,建议看完用有序二叉堆实现优先队列后再阅读此文

和优先队列的关系

因为优先队列本身已经提供了删除最小(或最大)元素的接口,最简单的做法是将待排序数组依次插入优先队列中,再依次删除最小元素。此种做法需额外的空间(优先队列),且时间复杂度较高(相当于插入排序,外循环用于便利所有待排序元素,内循环用于向优先队列insert元素)。

操作步骤

  • 待排序的数组可看做一个无序二叉堆(节点从0开始),从最后一个父节点((N-1)/2)开始依次进行sink操作,将其变为有序二叉堆
  • 设int j = N-1, 将顶部根节点a[0](此时根节点为最大元素)和元素a[j]交换(相当于将最大元素置于数组末尾),然后将新的a[0]进行sink()动作,sink完成后新的最大元素(次于开始时的根节点)会置于a[0]位置。
  • j- -,依次进行上述动作,直至j = 1;

代码实现

public void sort(Comparable<T>[] a ){    int N = a.length;    int i = (N - 1) / 2; //i初始指向最后一个父节点    while(i >= 0){        sink(i, N-1);        i--;    }//将数组变为堆有序    int j = N - 1;//j初始指向数组最后一个位置    while(j >= 1){        swap(0, j);//将根节点和最末节点交换,则最大元素会位于数组最末        sink(0, j);//新的根节点“下沉”到正确位置后(但不能超出j的范围,因为j已经排序过),新的最大节点(仅次于交换前的根节点)会位于根节点位置        j--;//对下一个j排序    }}//和[用有序二叉堆实现优先队列](http://blog.csdn.net/a854702872/article/details/77687288)中的sink动作相比,加入bounds参数,限制sink的范围private void sink(int m, int bounds){    int n = m * 2 + 1;    if (n >= bounds) {        break;    }    if (a[n] < a[n++]) {        n++;    }    if (a[m] < a[n]) {        swap(m, n);    }}