堆排序(粗糙版)

    堆:一个完全二叉树
    大顶堆:一个二叉树,它的根节点上的值是整个树上最大的
    小顶堆:它的根节点上的值是整个树上最小的
    大致思路如下(升序排列):将一个数据视为一个二叉树,树根的角标为零,角标为n的位置的左子节点的角标为2*n+1,右子节点的角标为2*n+2。假设我们要排序数组中的前N个数,我们就将前n个树构造成一个大顶堆,然后树根(nums[0])就是最大的,将nums[0]与nums[n-1]交换,然后对前n-1个重复上述操作。直至n=0,排序完成。

代码如下:

package Sort;

import java.util.Arrays;

本程序为堆排序

    堆:一个完全二叉树
    大顶堆:一个二叉树,它的根节点上的值是整个树上最大的
    小顶堆:它的根节点上的值是整个树上最小的
    大致思路如下(升序排列):将一个数据视为一个二叉树,树根的角标为零,角标为n的位置的左子节点的角标为2*n+1,右子节点的角
标为2*n+2。假设我们要排序数组中的前N个数,我们就将前n个树构造成一个大顶堆,然后树根(nums[0])就是最大的,将nums[0]与nums[n-1]交换,
然后对前n-1个重复上述操作。直至n=0

public class HeapSort {
    /**
     *
     * @param nums   需要构造大顶堆的数组
     * @param i      需要构造大顶堆的树的树根在nums数组中的角标,该树可能是nums数组构成的二叉树中的一个子树,也可能就是数组构成的树
     * (当i=0的时候)
     * @param length 数组的最后一位数的角标
     */
    该方法只是将数组组成的二叉树中的某个子树构造成一个大顶堆,并不是将整个数组构造成大顶堆
    public static void heapBuild(int nums[],int i,int length){
        //在将以i为树根的树构造成大顶堆的时候,默认i的左子树和右子树均为大顶堆
        int temp=nums[i];
        int j;
        for(j=2*i+1;j<=length;j=j*2+1){
            因为i的左子树和右子树均为大顶堆,所以下面这个if语句执行完毕之后,nums[j]就是i的子树中的最大值
            if(j+1<=length&&nums[j]<nums[j+1])
                j++;
            if(temp>=nums[j]){
                因为i的左子树和右子树均为大顶堆,所以nums[j]就是i的子树中的最大值,由于temp>=nums[j],说明此时树根的值已经该子树的最大值
                也就是说该子树已经是一个大顶堆,直接结束
                break;
            }else{
                说明i位置上不是最大值,整个子树的最大值就是nums[j],将最大值赋予nums[i]
                nums[i]=nums[j];
                为什么要进行赋值操作,而不是进行数据的交换? 举一个例子
                
                             4
                         10       5
                       9    5   3    1
                      假如我们要将该树做成一个大顶堆,headBuild(nums,0,6)
                      我们直到以10和5为根节点的子树已经都是大顶堆了,执行到nums[i]=nums[j]之后的树如下图所示
                             10
                        10        5
                      9    5    3   1
                     这个时候temp还等于4,我们还得在该树中找到temp的位置,但是如何找到temp的位置呢?
                     思路如下:
                           因为我们把nums[j]赋值给nums[i],所以以角标j的跟根节点的子树相当于少了一个树,所以我们只需要将temp放在该树中就可以。
                     将temp的值与j的左子节点和右子节点作比较,如果temp最大,直接将他放在该位置上,直接结束;如果小于,直接将最大值放在该位置上,
                     然后重复上述操作
                 
                i=j;
                所以你看,其实第二个if 要将左右节点的值与temp相比,是不是其实这个方法的作用就是在为temp在大顶堆中找到一个位置。
            }
        }
        nums[i]=temp;
    }
    public static void heapSort(int nums[]){
        从最后一个非叶子节点开始将整个数组构成一个大顶堆
        for(int i=nums.length/2-1;i>=0;i--){
            heapBuild(nums,i, nums.length-1);
        }
        for(int i=nums.length-1;i>=0;i--){
            在这个时候整个数组就是一个大顶堆
            int temp=nums[0];
            nums[0]=nums[i];
            nums[i]=temp;
            除了角标为零的树,其余子树全是大顶堆,所以直接从零调整大顶堆
            heapBuild(nums,0,i-1);
        }
        System.out.println(Arrays.toString(nums));

    }


    public static void main(String[] args) {
        int nums[]={10,41,48,2,1,1,0};
//        heapBuild(nums,0,nums.length-1);
//        System.out.println(Arrays.toString(nums));
        heapSort(nums);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值