C语言排序算法总结——堆排序

前言

这次我们介绍堆排序。堆是一种完全二叉树的数据结构。
他的父节点总会大于他的子节点叫做“大堆”。
他的父节点总会小于他的子节点叫做“小堆”。
在这里插入图片描述
虽然画的很丑,但是结构大体如图这样的。

思路

我们用数组来保存二叉树。每个父节点的左子节点的下标,是父节点下标的两倍加一,右子节点的的下标,是父节点下标的两倍加二。 我们之前在介绍二叉树时有介绍这样的方法。
所以我们先构建一个用来创建堆的结构体:

typedef struct Heap
{
	DATATYPE* _val;
	DATATYPE _size;
	DATATYPE _capacity;
}Heap;

这个结构体里包含元素集合, 目前元素个数,和最大容量三个变量。

当我们要构建一个小堆时,则需要满足,父节点始终小于子节点。在排序时,我们首先找到两个子节点中较小的那一个。然后与父节点比较,如果这个子节点比父节点小的话交换两个节点的位置。 如果完成交换。就对交换后的父节点和新的子节点重复这个过程,直到满足小堆的要求。
构建大堆也是这样的道理。
显而易见,使用堆排序可以更加快速的完成排序。因为我们不需要去依次遍历所有的数据。只需要向下遍历二叉树的深度就可以完成这个元素集合的处理。时间复杂度为O(nlogn)。
但是缺点也很明显。就是我们并没有对所有数据都完整的排序。也就意味着我们并不能一次性输出一个有序的完整元素集合。

代码实现

我们完成一趟排序,其中 n代表元素集合个数, root是我们开始向下调整的位置。

void AdJustDown(DATATYPE* data, int n, int root)   //向下调整  小堆
{
	int parent = root;
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 >= n);
		else if (data[child + 1] < data[child])
		{
			child += 1;
		}

		if (data[child] < data[parent])
		{
			Swap(&data[child], &data[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

在对所有的数据排序时,我们需要从后往前排。否则构建出来的堆是错误的,因为我们在排根节点时,需要保证他的左右子树皆为小堆(大堆),在调整根节点后才能保证依旧是小堆(大堆)。


Heap* HeapInit(DATATYPE* data, int n)
{
	Heap* heap = (Heap*)malloc( sizeof(Heap));
	heap->_val = (DATATYPE*)malloc(sizeof(DATATYPE) * n);
	heap->_capacity = heap->_size = n;
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
		AdJustDown(heap->_val,heap->_size,i);
	return heap;
}

当然我们还会有弹出,或者插入一个新的数据的操作。


DATATYPE HeapPop(Heap* heap)
{
	DATATYPE tmp = heap->_val[0];
	Swap(&(heap->_val[0]), &(heap->_val[heap->_size - 1]));
	heap->_size--;
	AdJustDown(heap->_val, heap->_size, 0);
	return tmp;
}

void HeapPush(Heap* heap, DATATYPE val)
{
	if (heap->_capacity == heap->_size)
	{
		heap->_capacity *= 2;
		heap->_val = (DATATYPE*)realloc(heap->_val, heap->_capacity * sizeof(DATATYPE));
	}
	heap->_size++;
	heap->_val[heap->_size-1] = val;
	AdJustUp(heap->_val, heap->_size, heap->_size - 1);
}

值得注意的是,当我们在使用堆排序时,一般都是在处理前几名,或者需要找到特定排名的问题时,在弹出操作时,我们弹出了堆顶,即根节点的值,在插入时,我们从构成二叉树的数组的末尾插入。然后在对末尾新插入的向上调整到合适的位置就好。


void AdJustUp(DATATYPE* data,int n, int child)  // 末尾向上调整
{
	int parent = (child-1)/2;
	while (parent >= 0)
	{
		if (data[parent] > data[child])
		{
			Swap(&data[parent], &data[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值