堆排序通俗版

本篇不是非常全面的教程,主要是堆排序的思路,希望能帮助到你。

堆排序给你一个数组,对这个数组进行排序。一个数组可以看成一个完全二叉树。

数组显然不是一个二叉树。那为什么说这玩意是二叉树,因为我们可以通过下标操作方式按照二叉树对其进行操作。

也就是让这个数组变成逻辑上的二叉树(蓝色是数组下标)。

堆排序步骤(注意这个不是最终版本,还要优化)

1.构建完全二叉树

把数组看成一个完全二叉树,计算时坐标按照下边:

父==>子:左孩子:2*i+1 右孩子:2*i+2;

子==>父:(i-1)/2

注意这里的i都是数组下标。

2.从数组最后一个叶子的父节点开始,依次进行堆调整heapify(图中红色顺序)

最后一个叶子下标就是数组长度-1 ,这个元素就是最后一个叶子。它的父节点对应数组就是(数组长度-1-1)/2 。从这个节点开始,把这颗小树调整为大根树,比较其与左右子节点的值,选择最大的节点作为父节点,并确保父节点大于或等于子节点。若发生交换,继续对被交换的子节点进行堆调整,以保持堆的性质。然后不断往前移动。挨个调整,等到遍历到整个树的根节点,那么这就是一个大根堆。(按照红色顺序)而这个树的根节点一定是最大的元素。

3.把最大的挑出来

把整棵树的根节点拿出来和数组最后一个下标的元素交换,而且交换后最后一个元素不参与这个排序过程了。就当组数末尾少了一个元素。这个元素不跟前边的玩了。

4.把剩余的再挑出一个最大的元素,再拿出来。

然后前边数组长度-1个元素再次进行步骤2,走一遍之后又挑出来一个最大的,再与新的数组末尾,也就是下标为(数组长度-1-1)交换。交换后这个元素也不参与前边剩余元素竞争了。

5.重复2,3,4操作,得到的就是一个大根堆。

就是不断从这一堆东西里挑出一个最大的按顺序从后往前放。得到的就是一个递增的数组。

聪明的你一定会发现:

在整棵树的根节点与最后一个元素交换后。此时整个树,只有以根节点为根的树是不一定满足二叉堆的性质的,其余的所有子树都满足二叉堆的性质,所以只需要对根节点再做一次堆化操作即可。比较跟节点与两个叶子节点谁最大,然后交换,让最大的上来。交换后,根节点成为了一个子节点,然后以这个子节点为根节点的树就不一定满足大根堆的性质了(最大元素一定是跟节点),所以要再次把以这个子节点为根节点的树调整。直到不发生交换,交换必定要调整。

优化后的堆排序步骤 

1.构建大根堆:

将给定的数组转换为逻辑上的完全二叉树,通过比较父节点和子节点的大小关系,使得每个父节点的值都大于或等于其子节点的值。从最后一个非叶子节点开始,向前遍历每个非叶子节点,对其进行堆调整操作。

2.堆调整(heapify):

对于当前的非叶子节点,比较其与左右子节点的值,选择最大的节点作为父节点,并确保父节点大于或等于子节点。若发生交换,继续对被交换的子节点进行堆调整,以保持堆的性质。

3.交换元素:

将堆顶元素(最大值)与数组中最后一个元素交换位置,即将最大值放到数组的末尾。

4.缩小堆范围:

将数组长度减一,剔除已经排好序的最大值,此时堆的范围缩小。

5.重复对整棵树的根节点进行对调整,直到堆的范围缩小为1,排序完成。

这个就是优化,不用从下往上依次调整了。

OK理论结束,上代码:

c++堆排序

//arr:排序的数组
//n:数组长度
//i:要操作的节点
void heapify(vector<int>& arr, int n, int i) 
{
    int largest = i; // 初始化当前节点为最大值
    int left = 2 * i + 1; // 左子节点
    int right = 2 * i + 2; // 右子节点
	// 如果左子节点大于根节点,则更新最大值
	if (left < n && arr[left] > arr[largest])
    	largest = left;

	// 如果右子节点大于当前最大值,则更新最大值
	if (right < n && arr[right] > arr[largest])
    	largest = right;

	// 如果最大值不是当前节点,则交换最大值和当前节点,并继续向下调整
	if (largest != i) 
    {
    	swap(arr[i], arr[largest]);
    	heapify(arr, n, largest);
	}
}
void heapSort(vector<int>& arr) 
{
    int n = arr.size();
    // 构建最大堆,从最后一个非叶子节点(就是最后一个叶子结点的根节点)开始向上调整
	for (int i = n-2 / 2 ; i >= 0; i--)
    	heapify(arr, n, i);

	// 依次将最大值交换到数组末尾,并重新调整堆
	for (int i = n - 1; i > 0; i--) 
    {
    	swap(arr[0], arr[i]); // 将最大值交换到末尾
    	heapify(arr, i, 0); // 重新调整堆,i也对应现在参与排序的数组长度,会不断减小
	}
}
  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值