堆排序算法

1. 定义

1. 堆的含义:堆的本质是二叉树;

2. 大根堆:根节点是整颗树的最大值;

3. 小根堆:根节点是整棵树的最小值;

堆排序步骤:

Step1:将给定的数组构建除二叉树。

Step2:对该数组(二叉树)进行调整。

Step3:对调整后数组(二叉树)进行排序、调整。

相关概念:

1. 与排序二叉树的区别:排序二叉树不能有重复的数值,且根的左侧小于根节点的数值,根右侧大于根节点的数值。

2. 满二叉树:所有的终端节点都在同一层上,并除根结点外都有两个孩子;

3. 完全二叉树:除第 m 层外,其它各层 (1~m-1) 的结点数都达到最大个数,第 m 层所有的结点都连续集中在最左边,从左到右依次排列,如下图;

以上两图为例,i为根的数值;

左孩子是(2*i+1);

例:2的左孩子是:2*2+1=5;

右孩子是(2*i+2);

例:1的右孩子是:1*2+2=4;

所以把一颗二叉树可以写成一个数组;

2. 数组构建二叉树

建立一个长度为7的数组,找到其对应二叉树性质。

由数组构建一个二叉树的方法:找到最后一个孩子,总后往前依次推出来(最后一个有孩子的节点 (i / 2-1)=(7/2-1)=2;所以2位置的数为最后一个有孩子的节点),如下图;

所以需要两步骤:1. 调整 2. 排序

2.1 调整

首先判断,需要调整的根节点有几个孩子;

2.1.1有两个孩子

2.1.2有一个孩子

2.1.3没有孩子

调整函数的详细代码:

void Adjust(int arr[], int nLen, int index)
{
	while (1)
	{
		int i = index;
		//1. 两个孩子---左、右孩子
		if (2 * index + 2 < nLen)
		{
			//判断是否调整,左、右孩子 > 根的值,进行调整
			if (arr[index] < arr[2 * index + 1] || arr[index] < arr[2 * index + 2])
			{
				//找到左、右孩子的最大值max
				int max = arr[2 * index + 1] > arr[2 * index + 2] ? 2 * index + 1 : 2 * index + 2;
				//交换
				int t = arr[index];
				arr[index] = arr[max];
				arr[max] = t;
			}
			else
			{
				break;
			}
		}
		//2. 一个孩子---左孩子
		else if (2 * index + 1 < nLen)
		{
			if (arr[index] < 2 * index + 1)
			{
				int t = arr[2 * index + 1];
				arr[2 * index + 1] = arr[index];
				arr[index] = t;
			}
			else
			{
				break;
			}
		}
		//3. 没有孩子
		else
		{
			break;
		}
		
	}
}

2.2 排序

获取调整后的数值,调整后的第一个数值一定是最小的,0位置的数和nLen-1位置的数交换,在进行调整,不断循环操作,直至排序完成。

void HeapSort(int arr[], int nLen)
{
	if (arr == nullptr || nLen <= 0)
		return;
	//构建大根堆
	//调整
	for (int  i = nLen/2-1; i >=0; i--)//从最后一个有孩子的节点开始
	{
		Adjust(arr, nLen, i);//
	}
	//排序
	for (int i = nLen - 1; i > 0; i--)
	{
		int nTemp = arr[0];
		arr[0] = arr[i];
		arr[i] = nTemp;

		Adjust(arr, i, 0);
	}
}

3. 验证例子

int arr[] = { 2,6,3,654,34,57,35 };
HeapSort(arr, 7);
for (int n : arr)
{
	cout << n << " ";
}
cout << endl;

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值