堆排序(个人理解

堆排序
先放无注解的代码

    public static void heapSort(int[] arr) {

        for (int i = arr.length/2 -1 ; i >= 0 ; i--){
            adjust(arr,i,arr.length);
        }
        int temp;
        //对大顶堆进行递归
        for (int j = arr.length -1; j > 0; j--) {
            temp = arr[0];
            arr[0] = arr[j];
            arr[j] = temp;
            adjust(arr,0,j);
        }
    }

    private static void adjust(int[] arr, int i, int length) {
        int temp = arr[i];
        //先获取子节点
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            if (arr[k] > temp) {
                //如果子节点比当前节点大,则替换
                arr[i] = arr[k];
                i = k; 
            }else {
                break;
            }
        }
        arr[i] = temp;
    }

首先理解什么是堆:堆是一个完全二叉树,完全二叉树:除了最后一层,其他层都是满的,最后一层从左到右依次有
叶子节点:没有子节点的节点
满二叉树每一层的结点个数都达到了最大值, 即满二叉树的第i层上有个2的i-1次方个结点 (i≥1) ,满二叉树就一共有2的i次方-1个 (注意区别)
好比有10层,那就是有2^10-1个节点1023,第10层有2的9次方个节点 512个
假设第10层没满,树一共有800个节点
800/2-1=399,到511之间 都是第9层
至于从第一层到第8层的每一层的节点个数是固定的,2的k-1次方
第八层有256个
求一个完全二叉树的叶子节点的个数,已知总节点个数为n,公式【(n+1)/2】 取整数

arr.length/2 -1 (这个地方是直接除以2)

这样的话是每次取出来一个最大的数

堆排序的构想:大顶堆,小顶堆
大顶堆,叶子节点,与非叶子节点的关系:
首先:
arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆,arr[i] <= arr[2i+2] && arr[i] <= arr[2i+2]

这个地方理解一下为什么是这样的关系

public void heapSort(int[] arr) {
	//第一步,获取到大顶堆
	//按照倒叙,将每个非叶子节点排一次序,到最后的时候,整个树就是大顶堆了
	//为什么arr.length/2-1是最后一个非叶子节点的下标呢?
	//因为arr.length /2 为非叶子节点的个数(注意,这个地方是Java的int类型的除,是不留小数点后面的数字的!
	//那拿到了个数 -1 ,就是下标啦
	//至于为什么arr.length/2就是非叶子节点的个数呢?在其他地方去验证吧!
	for (int i = arr.length /2 -1; i >= 0; i --) {
		adjustHeapNode(arr,i,arr.length);
	}
	//此时获取到了一个大顶堆,然后,获取到大顶堆以后呢,将最大值丢到最后面
	//然后呢,将最后一个值放到最前面,去比较
	int temp ;
	for (int i = length-1; i > 0; i--) {
		//这一步是将arr[0]和未排序的数组中最后一个值做对换
		temp = arr[i]
		arr[length] = arr[0];
		arr[0] = temp

		adjustHeapNode(arr,0,i); //这个地方记得区分下标与长度的区别
	}

}


//这个方法不应该叫构建大顶堆
//应该叫做获取数组中最大的值
public void adjustHeapNode(int[] arr,int i ,int length) {
	int tmep = arr[i];
	//傻瓜注解
	//k = i * 2 +1 这一步的操作是为了获取该节点的左子节点
	//k = k * 2 + 1,这个操作是当结束循环时,将指针指向K节点的左字节点
	if (int k = i *2 +1 ; k < length ; k = k * 2 + 1) {
		if (k + 1 < length && arr[k] < arr[k+1]) {
			k = k + 1; //指针层面的转换
		}
		//此时,k 为 左右子节点较大的那个指针
		
		if (arr[k] > temp) {
			//当arr[k] > temp时,那么我们是不确定,该节点下的子树中是否有比temp更大的
			//此时先将k的值给它的父节点,,arr[i] = temp 
			arr[i] = arr[k];
			//然后呢,i = k 这个操作是指针指向了k节点(k是一个父节点)
			i = k;
		} else {
			//当arr[k] < temp时,此时需要先理清楚一个概念
			//我们是对大顶堆进行排序的,大顶堆的特点是节点比它的左右子节点都大
			//那如果说arr[k] < temp的话, 那k的子树(无论下面有多少层)的所有子节点都比它小,那就可以break了,没必要比下去了
			//而当数组不是大顶堆时,我们是倒叙着去排每一个非叶子节点的
			//简单理解一下,即便是构建大顶堆的时候,也不会出现父节点比叶子节点小
			break;
		}
	}
			//顺着上面的if来说的话,当我把i = k 后,i就是子节点了
		//                                            我先把if判断去掉,代码是这个样子的 假设i = 1 k = 2  arr[i] = 4 arr[k] = 10
 		//int temp = arr[i]  temp = 4
		//arr[i] = arr[k]  arr[1] = 10
		// i = k   i = 2
		//arr[i] = temp arr[2] = 4
		//对比一下结果,开始的时候是 arr[1] = 4,arr[2] = 10,现在是ar[1] = 10,arr[2] = 4,同时呢,我们是要继续往下对比的
		//i 就变大了呀
		//简化逻辑,就是找到一个比他大的数,然后替换就完事了
		//当i找到了子节点比它大的数的时候
 		arr[i] = temp;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值