堆排序

堆分为大根堆(双亲节点大于孩子节点)和小根堆(双亲节点小于孩子节点)

一般用数组来表示堆。当一个节点的下标为i时,它的左右孩子下标为2i+1,2i+2
为什么最后一个非叶子节点的位置是n/2-1?
①堆的最后一个非叶子节点若只有左孩子

②堆的最后一个非叶子节点有左右两个孩子

完全二叉树的性质之一是:如果节点序号为i,在它的左孩子序号为2i+1,右孩子序号为2i+2。

对于①左孩子的序号为n-1,则n-1=2*i+1,推出i=n/2-1;

对于②左孩子的序号为n-2,在n-2=2i+2,推出i=(n-1)/2-1;右孩子的序号为n-1,则n-1=2i+2,推出i=(n-1)/2-1;

很显然,当完全二叉树最后一个节点是其父节点的左孩子时,树的节点数为偶数;当完全二叉树最后一个节点是其父节点的右孩子时,树的节点数为奇数。

整数除不尽时向下取整,则若n为奇数时(n-1)/2-1=n/2-1。

因此对于②最后一个非叶子节点的序号也是n/2-1。
由于我将进行从小到大的排序,因此我将使用的是大根堆。
此处我所将要进行的堆排序思路大致如下:
1)假设待排序数据元素有n个,那么我们就从n/2-1下标处(最后一个非叶子节点的位置)开始倒着进行进行调整
2)当我们进行了n/2logn次调整之后,此时最大的值就在堆顶,交换堆顶数据和n-1处的数据,此时最大的数值就在数组的末尾了。
3)至此,我们已经大致上建立了一个大根堆,但是仍需进一步调整。由于n-1下标处已经是最大,这下我们从下标为0开始调整,直到n-2处,每调整一次则交换一次。

代码如下:

void Show_arr(int *arr, int n)
{
	for (int i = 0; i<n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void swap(int *x, int *y)
{
	int tmp;

	tmp = *x;
	*x = *y;
	*y = tmp;
}

基本思想:
先调整一遍堆, 为大根,然后从头依次再去遍历非叶子节点,调整堆
void Heap_Adjust(int *arr, int start, int end)
{
	int tmp = arr[start];
	int i = 2 * start + 1;
	while (i <= end)
	{
		if (arr[i] < arr[i + 1] && i + 1 <= end)
		{
			i++;
		}
		if (arr[i] > tmp)
		{
			arr[start] = arr[i];
		}
		else
		{
			break;
		}
		start = i;
		i = 2 * start + 1;
	}
	arr[start] = tmp;
}

void HeapSort(int *arr, int n)
{
	//建立堆(大根堆)n/2*logN
	for (int i = n / 2 - 1; i >= 0; --i)
	{
		Heap_Adjust(arr, i, n - 1);
	}
	swap(&arr[0], &arr[n - 1]);

	//调整堆 n*logN
	for (int j = n - 2; j>0; --j)
	{
		Heap_Adjust(arr, 0, j);
		swap(&arr[0], &arr[j]);
	}
}

int main()
{

	int arr[] = { 16, 3, 6, 18, 2, 45, 4, 38, 2, 99, 34, 36, 27, 1 };

	int n = sizeof(arr) / sizeof(arr[0]);

	HeapSort(arr, n);
	Show_arr(arr, n);

	return 0;
}

运行结果如下:
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值