深入理解堆排序

>>堆排序


堆排序是插入排序的一种,且利用了数据结构中的完全二叉树
关于堆排序的实际流程,和什么是完全二叉树可以从这篇博客中了解:《堆排序就这么简单》

堆排序的思路: 给定一个数组,将数组排成这样一个完全二叉树:每个顶点大于子节点的叫大顶堆,每个顶点小于子节点的叫小顶堆,大顶堆是升序,小顶堆的降序。然后将第一个节点所得到的最值,与最后一个节点交换,并立即维护这个完全二叉树,循环直到只剩一个节点。
平均时间复杂度: O(n log n)
最好情况: O(n log n)
最坏情况: O(n log n)
空间复杂度: O(1)
稳定性: 不稳定


: ) 堆排序代码

代码如下:

	public static void heapSort(int[] value,int length){
		//构建初始完全二叉树,从最后一个拥有子节点的节点开始
		for(int j=(length/2-1);j>=0;j--){
			buildHeapTree(value,j,length);
		}

		//这里不需要遍历到最后,因为当第二个数都已经排好序,那么第一个也就排好序了。
		for(int i=length-1;i>0;i--){
			//获得第一个值,这个值是最大值,放到最后
			swap(value,0,i);
			//获得值之后,保持二叉树是一个大顶堆
			//从0开始维护,因为刚刚交换了它
			//并且这里只需要维护到当前堆的大小,而不是整个数组的大小
			//恰好,由于数组是0开始,这时的i就刚好等于排除最后一个数后堆的大小
			buildHeapTree(value,0,i);
		}
	}
	/*
	 *	n :是当前待排顶点
	 *	len : 是为了用于判断下标是否越界,所以传入的是待排二叉树大小
	 * */
	private static void buildHeapTree(int[] value,int n,int len){
		//获得左子节点
		int left=n*2+1;
		//获得右子节点
		int right=left+1;
		//将当前节点与子节点中最大且大于当前节点的值交换
		//默认当前节点最大
		int max=n;
		//比较左节点
		if(left<len&&value[left]>value[max]){
			max=left;
		}
		//比较右节点
		if(right<len&&value[right]>value[max]){
			max=right;		
		}
		if(max!=n){
			swap(value,max,n);
			//这一步非常重要,是构建和维护完全二叉树的关键
			//因为将子节点中的最大值给了n,所以要确保被交换的子节点max(max是left,或者right)依然最大顶点
			buildHeapTree(value,max,len);
		}
	}
	private static void swap(int[] value,int i,int j){
		int temp=value[i];
		value[i]=value[j];
		value[j]=temp;
	}

代码量其实并不多,如果将注释去掉也就20来行。

代码里写了很多注释,就不再去解释代码了。

我想强调的是buildHeapTree()函数中 递归调用那一段buildHeapTree(value,max,len);

这段代码非常关键,是构建和维护完全二叉树最核心的句子。

我写的注释是:因为将子节点中的最大值给了n,所以要确保被交换的子节点max(max是left,或者right)依然是最大顶点。


: ) 运行结果

在这里插入图片描述
在这里插入图片描述
我利用快速排序做了比较,以测试排序是否正确,懒嘛,程序员的优点 ?,也作为一次复习。

快速排序代码如下:

	/*
	 *	快速排序,用来检查堆排序是否正确
	 * */
	public static void quikSort(int[] value,int start,int end){
		if(start<end){
			int i=start-1;
			int j=start;
			int X=value[end];
			while(j<=end){
				if(value[j]<=X){
					i++;
					swap(value,i,j);
				}
				j++;
			}
			quikSort(value,start,i-1);
			quikSort(value,i+1,end);
		}
	}

: ) 总结

最初,堆排序对我来说相当的陌生,在搜查了资料后发现给出的代码根本啃不动,因此又去搜索了很多资料,思考了每一行代码,才终于弄清楚。上面的代码和我搜索到的大多数版本的代码不同,也许是我想的不够多,但是我还是将那些我觉得没有必要的代码删除掉了。精简后的代码是不是看着很舒服(如下)?

	public static void heapSort(int[] value,int length){
		for(int j=(length/2-1);j>=0;j--){
			buildHeapTree(value,j,length);
		}
		for(int i=length-1;i>0;i--){
			swap(value,0,i);
			buildHeapTree(value,0,i);
		}
	}
	private static void buildHeapTree(int[] value,int n,int len){
		int left=n*2+1;
		int right=left+1;
		int max=n;
		if(left<len&&value[left]>value[max]){
			max=left;
		}
		if(right<len&&value[right]>value[max]){
			max=right;		
		}
		if(max!=n){
			swap(value,max,n);
			buildHeapTree(value,max,len);
		}
	}
	private static void swap(int[] value,int i,int j){
		int temp=value[i];
		value[i]=value[j];
		value[j]=temp;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值