堆排序(简洁易懂)---C++

一、前言

          在实现堆排序之前,我们首先要知道堆的一些相关知识。

         1、 简而言之,堆就是一种特殊的完全二叉树,可分为最大堆和最小堆

         2、 最大堆就是该二叉树的每一个双亲结点都大于它的左右孩子结点,最小堆反之。

         3、从而堆的根节点必然是所有结点的最大(小)值

    正是由于最值的出现,让我们可以利用堆这个数据结构实现排序操作。

  二、堆的创建

         在堆排序之前,我们首先要创建一个堆,在这里我们采用向下调整的方法(还有其他方法)

         所谓向下调整,就是从根节点开始,沿着二叉树结构逐渐向下与孩子结点进行比较并作出调整,使得每一个双亲节点始终是最大(小)值。

          由于排序一般是对数组结构进行操作,我们这里在数组结构的基础之上模拟堆结构(以最大堆为例),并进行向下调整。

代码如下:

1.向下调整(最大堆)

void AdjustDown(int a[],int n,int root)
{
	int parent = root;    //从根节点开始
	int child = 2 * parent + 1;     //根节点数组下标从0开始,则左孩子结点为2*parent+1;
	                             //若根节点数组下标从1开始,则左孩子结点为2*parent;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])   //若右孩子结点存在,且值大于左孩子结点的值,将右孩子与根节点比较
		{
			child++;
		}

		if (a[parent] < a[child])     //若堆顶元素不是最大值.交换孩子结点与双亲结点,并将孩子结点作为新的根结点
		{
			swap(a[parent], a[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else break;        //否则则说明堆已建成
	}
}

  2.建堆(最大堆)

        我们可以发现,上述代码中,只有当根节点的左右结点已经为最大堆时,才能实现最大堆的创建。 那对于一般的情况,我们应该如何建堆呢? 事实上,我们只需要从最后一个非叶节点开始,直到整个树的根节点结束,将其作为根节点向下调整,最后就可以得到一个堆了!

代码如下:

void HeapCreat(int a[],int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)   //从倒数第一个非叶节点开始向下调整
	{
		AdjustDown(a, n, i);
	}
}

三、堆排序

     通过上述步骤,我们就得到了一个最大堆了,现在我们就可以正式进行堆排序了。

1.原理

     实际上,堆排序的原理很简单:还记得前言说的堆顶元素是最大(小)值吗?于是我们就只需要将最大值交换至数组末尾,并再次向下调整堆结构,使得堆顶元素再次成为最大值(除了已经交换至最后的元素),重复上述步骤,最后我们就可以得到一个递增的数列了!

2.代码

void HeapSort(int a[], int n)
{
	HeapCreat(a, n);           //建堆之后,堆顶即为最大元素
	for (int i = n - 1; i > 0; i--)    //每次将堆顶元素与数组最后位置交换,然后再次调整堆,使得堆顶每次都是当前最大值
	{
		swap(a[i], a[0]);
		AdjustDown(a, i, 0);     //注意每次向下调整至当前元素即可
	}
}

四、测试与总结

1.测试

        我们最后对我们的堆排序进行测试-------

int main()
{
	int a[8] = { 1,7,8,3,9,2,34,4 };
	HeapSort(a, 8);
	for (int x : a)
	{
		cout << x << " ";
	}
	return 0;
}

结果如下: 

数组递增,排序完成,测试成功!

2.全部代码

#include<iostream>
using namespace std;
void AdjustDown(int a[],int n,int root)
{
	int parent = root;
	int child = 2 * parent + 1;     //根节点数组下标从0开始,则左孩子结点为2*parent+1;
	                             //若根节点数组下标从1开始,则左孩子结点为2*parent;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])   //若右孩子结点存在,且值大于左孩子结点的值,将右孩子与根节点比较
		{
			child++;
		}

		if (a[parent] < a[child])     //若堆顶元素不是最大值.交换孩子结点与双亲结点,并将孩子结点作为新的根结点
		{
			swap(a[parent], a[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else break;        //否则则说明堆已建成
	}
}
void HeapCreat(int a[],int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)   //从倒数第一个非叶节点开始向下调整
	{
		AdjustDown(a, n, i);
	}
}
void HeapSort(int a[], int n)
{
	HeapCreat(a, n);           //建堆之后,堆顶即为最大元素
	for (int i = n - 1; i > 0; i--)    //每次将堆顶元素与数组最后位置交换,然后再次调整堆,使得堆顶每次都是当前最大值
	{
		swap(a[i], a[0]);
		AdjustDown(a, i, 0);
	}
}
int main()
{
	int a[8] = { 1,7,8,3,9,2,34,4 };
	HeapSort(a, 8);
	for (int x : a)
	{
		cout << x << " ";
	}
	return 0;
}

                                             读者可自行分析时间复杂度🙂

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值