堆排序(升序)

1、思想

堆即是一棵完全二叉树。堆排序的核心是堆调整算法。首先根据初始输入数据,利用堆调整算法shiftDown()形成最大堆;然后,将堆顶元素与堆尾元素交换,缩小堆的范围并重新调整为最大堆,如此往复。堆排序是一种不稳定的排序算法。

2、堆调整过程(将初始堆调整为最大堆)

最大堆定义:每个节点的值都大于或等于其左右孩子节点的值。

(1)对于初始堆,如下:

(2)此时我们从最后一个非叶子节点开始(叶节点自然不用调整,第一个非叶子节点的索引计算方法为arr.length/2-1=5/2-1,索引从0开始,即根节点的索引值为0),从右至左,从下至上进行调整。本图中最后一个非叶子节点是节点6。

应为【5,6,9】三个元素中,9元素最大,所以将6与9交换。

(3)找到第二个非叶节点4,由于【4,9,8】中9元素最大,4和9交换。

这时,交换导致了子根【4,5,6】的结构混乱,继续调整,【4,5,6】中6最大,交换4和6。

此时,我们就将一个初始堆调整为一个大顶堆。

3、代码

/**        
 * Title: 堆排序(选择排序),升序排序(最大堆),依赖于初始序列     
 * Description: 现将给定序列调整为最大堆,然后每次将堆顶元素与堆尾元素交换并缩小堆的范围,直到将堆缩小至1
 * 时间复杂度:O(nlgn)
 * 空间复杂度:O(1) 
 * 稳 定 性:不稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 * @author rico       
 * @created 2017年5月25日 上午9:48:06    
 */      
public class HeapSort {


    public static int[] heapSort(int[] target) {

        //判断数组,即判断堆是否存在
        if (target != null && target.length > 1) {
            // 从初始堆的最后一个非叶子节点开始调整
            int pos = target.length / 2-1;//因为索引从0开始,第N/2-1处(N为节点数量)的节点正好是二叉树的最后一个非叶子节点
            while (pos >= 0) {

                //从初始堆的最后一个非叶子节点开始,从右到左,从下到上的调整各个元素
                shiftDown(target, pos, target.length - 1);
                pos--;
            }


            // 经过一轮堆调整后,将大根堆的根元素与堆的最后一个元素交换
            for (int i = target.length-1; i > 0; i--) {
                int temp = target[i];//将最大堆的第一个元素和最后一个元素交换
                target[i] = target[0];
                target[0] = temp;
                shiftDown(target, 0, i-1);//此时只要将第一个元素放到合适的位置就行,因为其他位置都已经是有序的了
            }
            return target;
        }
        return target;
    }




    /**     
     * @description 自上而下调整为最大堆,将需要调整的元素放在合适的位置
     * @author rico       
     * @created 2017年5月25日 上午9:45:40     
     * @param target
     * @param start
     * @param end     
     */

    private static void shiftDown(int[] target, int start, int end) {//将节点i换到适合自己的位置,一直和子节点比较,

                                                                                                 //一直向下走,直到不能走了为止

        int i = start;
        int j = 2 * start + 1;//节点i左孩子的索引
        int temp = target[i];//从该元素开始调整堆
        while (j <= end) {   // 迭代条件
            if (j < end && target[j + 1] > target[j]) {  //找出较大子女,将节点i的左孩子和右孩子进行比较
                j = j + 1; //右孩子比较大
            }
            if (target[j] <= temp) {  // 父亲大于子女,这个temp是不变的,是要放置在正确位置的那个元素
                break;//不操作
            } else {//父亲小于子女
                target[i] = target[j];//将子女中较大的值赋给父亲
                i = j;//将较大的子节点作为根节点,比较该根节点和其子女和值大小,看是否需要调整。
                j = 2 * j + 1;
            }
        }
        target[i] = temp;
    }
}

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值