数据结构之堆和堆排序

堆的概念

如果有一个数的集合,把它的所有元素按完全二叉树额顺序存储方式存储在一个一维数组中,称为堆。
最小堆:任一结点的的值均小于它的左右孩子的值,位于堆顶结点的值最小。
最大堆:任一结点的的值均大于它的左右孩子的值,位于堆顶结点的值最大。

观察图中大堆和小堆。小堆:从根节点到任何一个叶子结点都是递减的。大堆:从根节点到任何一个叶子结点都是递增的。

创建堆

一般都用数组来表示堆,i 结点的父结点下标就为(i-1)/2,它的左右子结点下标分别为2*1+1和2*1+2。比如第0个结点的左右子结点下标分别是1和2。

创建思路:

先给一个普通的完全二叉树,然后进行调整,以大堆为例:
1、设堆的大小是Size,从结点(Size-1)/2开始调整,将每一棵子树都调整成一棵最大堆结构。
2、取该结点的左右孩子中较大的结点和父结点比较,如果比父结点大就交换,否则该子树符合大堆的要求。
3、取该结点的前一个结点,也就是下标减一,然后继续进行第2步。
4、检测每次调整是否会影响调整过的子树不符合大堆的要求,如果影响则需继续调整该子树。直到整体符合大堆要求为止。

void Adjustdown(int *array, size_t root, size_t size)
{
	size_t parent = root;//最后一个非叶子节点  
	size_t child = root * 2 + 1;//该节点的左孩子  
	while (child < size)//孩子存在  
	{
		if (child + 1 < size && array[child] > array[child + 1])//左右孩子比较大小  
		{
			child += 1;//标记较大的孩子  
		}
		if (array[parent] > array[child])//孩子小于双亲  
		{
			std::swap(array[child], array[parent]);//孩子与双亲交换  
			parent = child;//重新标记双亲节点  
			child = child * 2 + 1;
		}
		else{
			break;
		}
	}
}

堆排序

要进行堆排序,首先要创建堆。按递减序列排序时,要创建最大堆。
然后循环执行如下过程,直到数组为空:
1、把堆顶array[0]元素和当前最大堆的最后一个元素交换。
2、最大堆元素个数减1。
3、由于第1步后根节点不再满足最大堆定义,向下调整跟结点。

把一棵完成二叉树调整为堆,以及每次堆顶元素交换后进行调整的时间复杂度均为O(lg(n)),所以堆排序的时间复杂度为O(n*lg(n))。堆排序是一种不稳定的排序算法。

void HeapSort(int *array, size_t size)
{
	for (int idx = (size - 2) / 2; idx >= 0; --idx)//创建堆  
	{
		Adjustdown(array, idx, size);
	}
	int index = size - 1;//堆最后一个节点  
	while (index > 0)
	{
		std::swap(array[0], array[index]);
		Adjustdown(array, 0, index);
		index--;
	}
	for (int i = 0; i < 8; ++i)
	{
		cout << array[i] << " ";
	}
	cout << endl;
}
测试用例 

int main()
{
	int array[8] = { 53, 17, 78, 9, 45, 65, 87, 23 };
	HeapSort(array, 8);
	system("pause");
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值