放牛娃谈堆排序---(为了说明白,我重新学习了如何说“人话”)

原创不易,如果你觉得对你有用,哪怕一点点也好,请留下一个赞再走,谢谢啦啦啦!!

一、前期知识储备

  1. 堆排序是对于完全二叉树而言

完全二叉树的那些事:

  1. 定义:对于一个树高为h的二叉树,如果其第0层至第h-1层的节点都满。如果最下面一层节点不满,则所有的节点在左边的连续排列,空位都在右边。这样的二叉树就是一棵完全二叉树(不理解没关系,是我的错,不许喷我
  2. 性质:如果n个节点的完全二叉树的节点按照层次并按从左到右的顺序从0开始编号,对于每个节点都有:
    • 序号为0的节点是根对于i>0,
    • 其父节点的编号为(i-1)/2。
    • 若2·i+1<n,其左子节点的序号为2·i+1,否则没有左子节点
    • 若2·i+2<n,其右子节点的序号为2·i+2,否则没有右子节点。

是不是很枯燥?是不是看不下去?小伙子,别着急,磨刀不误砍柴工。,我给你上个图,你就懂上面所说的一切了。
在这里插入图片描述
2. 那堆又是什么呢?

  • 堆的定义:
  • 每个非叶子结点的值都大于或等于其左孩子和右孩子结点的值,称之为大根堆
  • 每个结点的值都小于或等于其左孩子和右孩子结点的值,称之为小根堆
  • 大根堆常用于升序操作
  • 小根堆常用于降序操作

千言万语不胜一图,看图哈
在这里插入图片描述

二、堆如何跟排序挂上钩的?

理由很简单,因为堆这种数据结构可以用于排序(虽然感觉是废话,哈哈哈哈,不管了,先说人话)不信你往下看。

  • 根据大顶堆的特性,我们可以知道大根堆根节点就是所有节点的最大值
  • 所以我们可以将最大值的根节点取下(人话:替换)与最后一个节点的值进行互换,然后将剩余的节点继续构造成大根堆

三、堆排序思想

非常重要呀呀呀,不懂也没关系,下面有图有真相

  1. 首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端(根节点)

  2. 将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

  3. 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

四、如何进行堆排序(附亲手画的图)

上面说的是不是还是很懵逼???,没关系,既然是说人话,那我就上个图来解释:

说明:以下画的树必须满足完全二叉树,因为这是堆这个数据结构的前提

  1. 给定一个数组,如何知道非哪些元素是非叶子节点个数呢?
  • 根据一条公式即可知道了,total = arry.length-1(下标从0开始)
  • 看下图哈
    在这里插入图片描述
  1. 构建大顶堆顺序
  • 依次处理非叶子节点
  • 顺序是从上往下、从右往左

在这里插入图片描述

  1. 堆排序

在这里插入图片描述
简要说一下人话:

  1. 给定一个待排序的数组,我们要将它变成大顶堆数组,这里之所以要给出完全二叉树来,是因为这样便于理解哈
  2. 我们是直接操作数组使其变成大顶堆,而不是树,但是原理是一样的,因为我们操作数组的时候就是用到上面说的完全二叉树的性质(下标索引找孩子节点
  3. 这里用树来演示,就是为了便于理解,真正操作是对数组直接操作。

再放两个图:
仔细品,不懂可以留言交流呀!图丑也欢迎留言喷哈哈哈
在这里插入图片描述
调整好大顶堆后,根(arry[0])节点值跟倒二节点(arry[5])值又互换,看图
在这里插入图片描述

五、撸代码(详细注释,不怕你看不懂)

纸上得来终且浅,得知此时要躬行,看了再多还是得撸代码
如果你看到这里了,要个赞不过分吧,哈哈哈哈

代码:

package suanFa;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 堆排序的学习:
 *  时间复杂度:O(nlogn),远比冒泡,简单选择,直接插入的O(n^2)好很多,但是它是个不稳定的算法
 * @公众号 放牛娃学编程
 *
 */

public class HeapSort {
	public static void main(String[] args) {
//		int[] arry = {20};
//		//测试一波
//		Heapsort(arry);
//		System.out.println("数组已经排序完了:"+Arrays.toString(arry));
		//性能测试一波,8千万数据排序
		int[] arry = new int[80000000];
		for(int i = 0; i < 80000000; i++)
		{
			arry[i] = (int) (Math.random()*80000000);
		}
		Date start = new Date();
		Heapsort(arry);
		Date end = new Date();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String startTime = simpleDateFormat.format(start);
		String endTime = simpleDateFormat.format(end);
		System.out.println("排序前的时间:"+startTime);
		System.out.println("排序后的时间:"+endTime);
		
		
	}
	
	//构建大顶堆
	public static void Heapsort(int[] arry)
	{
		//构建一棵大顶堆,策略:依次处理非叶子节点,从右往左,从上往下
		for(int i = arry.length / 2 - 1; i >= 0; i--)
		{
			HeapAdjust(arry, i, arry.length);
		}
		//开始排序(取大顶堆的根节点,即使最大值,跟数组最后一个调换位置,然后将剩余的数重新调整为大顶堆)
		for(int j = arry.length - 1; j > 0 ; j--)
		{
			//交换位置
			swap(arry,0, j);
			//将剩余的节点(这里是数组)调整为大顶堆
			HeapAdjust(arry, 0, j);
		}
		
	}
	
	private static void swap(int[] arry, int i, int j) {
		// TODO Auto-generated method stub
		int temp = arry[i];
		arry[i] = arry[j];
		arry[j] = temp;
	}

	//调整堆
	/**
	 * 
	 * @param arry 需要调整的数组
	 * @param index 非叶子节点下标索引
	 * @param length 需要调整的数组长度
	 */
	public static void HeapAdjust(int[] arry, int index, int length)
	{
		//1.0 用一个临时变量将该节点值暂时保存起来
		int temp = arry[index];
		int k;
		//下标为什么要这样变化,根据完全二叉树的父节点与子节点的关系得出来的(编号从0开始编号)
		for(k=2*index + 1; k < length; k = 2*k+1)
		{
			if(k+1 < length && arry[k] < arry[k+1])
			{
				//指向右子节点(较大的下标)
				++k;
			}
			/**如果父节点已经是大于等于孩子节点(那直接退出循环,为什么可以这样呢?
			   因为我们采取的策略是:处理非叶子节点时,从右往左,从上往下。假设非叶子节点编号为:4,3,2,1)
			**/
			if(temp >= arry[k])
			{
				break;
			}
			//替换
			arry[index] = arry[k];
			index = k;
		}
		//插入
		arry[index] = temp;
	}
}
六、 八千万数据测试堆排序性能

上图程序中,我用8千万的数据进行堆排序,我的电脑用时:
在这里插入图片描述

  1. 可能我的电脑配置比较差劲,但是比冒泡、简单排序好太多了,不信你自己玩玩,我这破电脑就不试了哈
  2. 欢迎留言看看你的运行时间是多少,看是不是吊打我电脑呢,哈哈哈哈哈
  3. 看在我熬夜肝图的份上就点个赞吧,图不好也可以喷哈哈哈

都已经看到这了,再不给赞,那就说不过去了吧

七、分享交流

最后有兴趣一起交流的,可以关注我的公众号:这里你能够学到很实用的技巧,不是常用的我不说,公众号回复提取码即可获取以下学习资料啦啦啦啦,喜欢就拿去吧!!

(链接时常会失效,若出现此类情况,可以加我微信:17722328325(加时请备注:学习资料))

  1. Java web从入门到精通电子书

  2. Python机器学习电子书

  3. Python400集(北京尚学堂)

  4. JavaScript项目案例、经典面试题

  5. Java300集(入门、精通)

  6. Java后端培训机构录集(同事培训内部提供)

  7. java重要知识pdf文档(价值连城呀呀,不收藏你会后悔的)

额外一堆电子书:
在这里插入图片描述
在这里插入图片描述



        喜欢就关注吧,点个赞吧

在这里插入图片描述
这是分享生活、电影、资料、书籍的一个公众号,有需要的也可以看看哟!


猜你喜欢

转载自blog.csdn.net/qiukui111/article/details/106558600