【算法】————7、堆排序

算法简介

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

算法描述和实现

具体算法描述如下:

  • <1>.将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;
  • <2>.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
  • <3>.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后 再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元 素个数为n-1,则整个排序过程完成。

Javascript代码实现:

/*方法说明:堆排序
@param  array 待排序数组*/
function heapSort(array) {
    console.time('堆排序耗时');
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
        //建堆
        var heapSize = array.length, temp;
        for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
            heapify(array, i, heapSize);
        }

        //堆排序
        for (var j = heapSize - 1; j >= 1; j--) {
            temp = array[0];
            array[0] = array[j];
            array[j] = temp;
            heapify(array, 0, --heapSize);
        }
        console.timeEnd('堆排序耗时');
        return array;
    } else {
        return 'array is not an Array!';
    }
}
/*方法说明:维护堆的性质
@param  arr 数组
@param  x   数组下标
@param  len 堆大小*/
function heapify(arr, x, len) {
    if (Object.prototype.toString.call(arr).slice(8, -1) === 'Array' && typeof x === 'number') {
        var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;
        if (l < len && arr[l] > arr[largest]) {
            largest = l;
        }
        if (r < len && arr[r] > arr[largest]) {
            largest = r;
        }
        if (largest != x) {
            temp = arr[x];
            arr[x] = arr[largest];
            arr[largest] = temp;
            heapify(arr, largest, len);
        }
    } else {
        return 'arr is not an Array or x is not a number!';
    }
}
var arr=[91,60,96,13,35,65,46,65,10,30,20,31,77,81,22];
console.log(heapSort(arr));//[10, 13, 20, 22, 30, 31, 35, 46, 60, 65, 65, 77, 81, 91, 96]

JAVA:

 

package flyingcat.sort;
/**
 *
 * @author FlyingCat
 * Date: 2013-8-26
 *
 */
public class ArrayHeap {
    private int[] array;
    public ArrayHeap(int[] arr) {
        this.array = arr;
    }
    private int getParentIndex(int child) {
        return (child - 1) / 2;
    }
    private int getLeftChildIndex(int parent) {
        return 2 * parent + 1;
    }
    /**
     * 初始化一个大根堆。
     */
    private void initHeap() {
        int last = array.length - 1;
        for (int i = getParentIndex(last); i >= 0; --i) { // 从最后一个非叶子结点开始筛选
            int k = i;
            int j = getLeftChildIndex(k);
            while (j <= last) {
                if (j < last) {                     
                    if (array[j] <= array[j + 1]) { // 右子节点更大
                        j++;
                    }
                }
                if (array[k] > array[j]) {           //父节点大于子节点中较大者,已经找到最终位置
                    break; // 停止筛选
                } else {
                    swap(k, j);
                    k = j; // 继续筛选
                }
                j = getLeftChildIndex(k);
            }// loop while
        }// loop i
    }
    /**
     * 调整堆。
     */
    private void adjustHeap(int lastIndex) {
        int k = 0;
        while (k <= getParentIndex(lastIndex)) {
            int j = getLeftChildIndex(k);
            if (j < lastIndex) {
                if (array[j] < array[j + 1]) {
                    j++;
                }
            }
            if (array[k] < array[j]) {
                swap(k, j);
                k = j; // 继续筛选
            } else {
                break; // 停止筛选
            }
        }
    }
    /**
     * 堆排序。
     * */
    public void sort() {
        initHeap();
        int last = array.length - 1;
        while (last > 0) {
            swap(0, last);
            last--;
            if (last > 0) { // 这里如果不判断,将造成最终前两个元素逆序。
                adjustHeap(last);
            }
        }
    }
    private void swap(int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

算法性能/复杂度

堆排序的时间复杂度非常稳定(我们可以看到,对输入数据不敏感),为O(n㏒n)复杂度,最好情况与最坏情况一样。

但是,其空间复杂度依实现不同而不同。上面即讨论了两种常见的复杂度:O(n)与O(1)。本着节约空间的原则,我推荐O(1)复杂度的方法。

算法稳定性

堆排序存在大量的筛选和移动过程,属于不稳定的排序算法。

算法适用场景

堆排序在建立堆和调整堆的过程中会产生比较大的开销,在元素少的时候并不适用。但是,在元素比较多的情况下,还是不错的一个选择。尤其是在解决诸如“前n大的数”一类问题时,几乎是首选算法。

算法分析

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(nlogn)
  • 平均情况:T(n) = O(nlogn)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLy_鹏程万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值