堆排序算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wjlucc/article/details/71101888

堆是一个完全二叉树的结构,分为大顶堆和小顶堆。
用一个数组存放一个完全二叉树,在数组中索引为i的结点的左子节点索引为i*2+1,右子节点索引为i*2+2,父节点为(i-1)/2。
时间复杂度:

建堆的时间复杂度为O(n)。建堆算法是从最后一个非叶子结点开始下溯,也可以把建堆过程想成先对左子树建堆(T(n/2)),再对右子树建堆(T(n/2)),最后对根下溯(O(lg n)),所以递推式是T(n) = 2*T(n/2) + O(lg n),利用主定理可以得到解为 T(n) = O(n)。在正式排序时,第(n-i)次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根结点的距离为log2i+1),并且需要取n-1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)。总的时间复杂度为O(n)+O(nlogn)=O(nlogn)。
由于堆排序对原始记录的排序状态并不敏感,因此它在最好,最坏和平均情况下,时间复杂度都为O(nlogn)。空间复杂度:O(1)。是个不稳定的排序算法。

堆排序(元素从小到大)流程:
1.先对输入的数组input建立一个大顶堆

从数组的最大索引节点对应的父节点开始逐个向上调整每一个结点,使得以该结点为根的子树都能满足堆的要求。这个调整称为向下调整。
向下调整:对每一个节点,检测其值是否大于两个子节点值,如果小于,则交换该节点和子节点的值,可能会破坏子节点特性,递归地去调整。

2.将input[0] 和 input[length]交换位置,对0索引的元素进行向下调整,此时注意原数组最后一个元素已经是最大的了,无需调整。

// 堆排序算法
public void heapSort(int[] input){
    buildMinHeap(input);   // 先初始化建立一个堆
    for(int i = 0; i < input.length-1;i++){
        int temp = input[0];  // 将当前最大的值,移动到数组最后一位
        input[0] = input[input.length-1-i];
        input[input.length-1-i] = temp;
        adjustDown(input,0,input.length-1-i);  // 从索引为0处向下调整,注意传入的数组长度。
    }
}       
// 建立初始堆
public void buildMinHeap(int [] input){
    for(int i = (input.length-1-1)/2;i>=0;i--){ // i从最大下标的父节点开始调整。          
        adjustDown(input,i,input.length);       }
}
// 对传入的指定节点进行向下调整。
public void adjustDown(int[] input,int k,int length){
    int temp = input[k];
    for(int i = 2*k+1;i < length;i = i*2 + 1){ // 递归地进行向下调整。

        if(i+1 < length){
            if(input[i] < input[i+1])
                i += 1;
        }
        if(temp >= input[i]){
            break;
        }else{
            input[k] = input[i];
            k = i;
        }           
    }       
    input[k] = temp;    
}

附:
建堆过程中向下调整参考图:
这里写图片描述

堆也支持删除和插入操作,删除堆顶元素时,将最后一个元素替换堆顶元素,再对堆顶做一次向下调整即可。插入操作是将新节点放在堆的末端,再对这个新节点执行向上调整操作,如下图所示。
这里写图片描述
这里写图片描述

参考:
《2015年数据结构联考复习指导》 P283
http://www.cnblogs.com/cj723/archive/2011/04/22/2024269.html

展开阅读全文

没有更多推荐了,返回首页