数据结构之【堆排序】

堆排序的问题

堆排序肯定要优于直接选择排序【O(N^2)】才会有价值。

那么排升序是用大堆还是小堆? 答案是建大堆。

  升序,为什么不能建小堆?

问题在于选出最小的数字后如何选出次小的数字

建堆选出最小的数,花了O(N),紧接着选择次小的数,剩下的N-1个数继续建堆,又是O(N),

最终成为N^2{因为剩下数字的父子关系完全乱了,只能重新建堆,效率太低}

这样建堆排序的时间复杂度就和直接排序一样没有什么实际的意义。

不是不可以,而是没有什么价值

而通过建大堆,

首先选出最大的数字,让它与最后一个数字交换,紧接着选择次大的数,不把最后一个数看做是堆里面的,向下调整就能选出次大的

思路如图:

同理排降序 就应该建小堆,原因及思路和上面完全一致。

 排升序代码如下:

void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0],&a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

 堆排序的时间复杂度就是:O(NlogN)

  [ N+NlogN,N相对于logN太小了可以忽略]

 简单分析下,堆排序和冒泡排序的差别有多大

 数字小了看不出来啥区别

 假设排100W个数字

 N^2 需要100亿次

 NlogN 需要 100W*20 次 堆排的优势就显示出来了

完整代码:

#include <stdio.h>

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(int* a, int n, int parent)
{
	//找出左右孩子小的那个
	int child = parent * 2 + 1; //左孩子
	while (child < n)
	{
		//找出左右孩子小的那一个
		if (child + 1 < n && a[child + 1] > a[child])  
		{
			//默认做孩子小,如果右孩子小就++,加到右孩子
			++child;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}

		else  //b情况
		{
			break;
		}
		
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0],&a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}



int main()
{
	//前提左右字树是小堆
	int a[] = { 15,18,28,34,65,19,49,25,37,27 };
	int n = sizeof(a) / sizeof(a[0]);
	HeapSort(a, n);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值