C数据结构与算法-基础整理-排序-03:堆排序

0x01.关于堆排序

堆排序(英语:Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

0x02.堆的结构

堆是具有以下性质的一棵树:

  • 是一棵完全二叉树
  • 每个结点的值都大于等于其左右孩子结点的值,称为大顶堆
  • 每个结点的值都小于等于其左右孩子结点的值,称为小顶堆

0x03.堆排序算法

调整堆结构算法:

//s是要需要调整堆结构的顺序表的下标元素
//调整完后使之成为大顶堆,m是堆的大小
void HeapAdjust(SqList* L, int s, int m)
{
	int temp, j;
	temp = L->r[s];//记录需要调整的这个元素的值
	//j的初始化的含义是等于s的左子树的下标值
	//j*=2的含义是每次循环过后,j都等于左子树的下标值
	for (j = 2 * s; j <= m; j *= 2)
	{
		//j+1的下标值其实就是下标s值右子树
		if (j < m && L->r[j] < L->r[j + 1])//如果左子树的值小于右子树的值,那么j的下标含义变为右子树的下标值
		{
			j++;
		}
		//此时的temp其实就是传进来需要调整的下标值,此时的j是传进来的结点的左右子树中值较大的值
		if (temp >= L->r[j])//如果传进来的这个值已经比左右子树都要大了,那么直接退出循环
		{
			break;
		}
		L->r[s] = L->r[j];//如果之前那个结点的值不比左右子树都大,那么就把左右子树中的较大值给这个结点
		s = j;//此时需要改变的结点是左右子树中较大的结点,再次循环
	}
	L->r[s] = temp;//找到需要插入的位置,插入元素
}

堆排序算法:

void HeapSort(SqList* L)
{
	int i;
	for (i = L->length / 2; i > 0; i--)//建造一个大顶堆,因为一半就已经包含所有有孩子的结点,所以一半已经能创建大顶堆序列了
	{
		HeapAdjust(L, i, L->length);
	}
	for (i = L->length; i > 1;i--)
	{
		swap(L, 1, i);//每次将堆顶元素往后面放
		HeapAdjust(L, 1, i - 1);//放完一个堆顶元素后,必须重新调整以下堆的结构
	}
}

0x04.原理深究

堆排序其实没有真正的堆:

从上面的代码可以看出,整个代码中都没有涉及树的结构,而是一直在用那个需要排序的是顺序表。其实,堆排序并没有真正的用到堆的结构,而是将待排序的顺序表中的元素顺序变为堆的层序遍历的顺序,因为最终用到堆的地方,就是每次从堆顶拿出哟个最大的元素放到表尾。

初始的顺序表可以看成一个完成二叉树:

初始的顺序表其实可以看出一个按层序遍历的二叉树序列,所以,我们要想将这个顺序表变为一个大顶堆,只需要对这个顺序表的一半的元素进行堆调整进行了,因为一半已经是完全二叉树所有的中间结点了。

堆调整原理:

堆调整就是对传入的结点不断的与左右结点比较,取得最大值,直到这个结点找到合适的位置插入,在插入这个结点前,已经对这个结点本来位置到插入的位置,都进行了堆调整。具体如何实现,可以试着模仿计算机进行运行该程序,找到这个调整的思想。

堆排序原理:

堆排序的前半部分,把顺序表构成了大顶堆结构,后半部分只需要每次把一个堆顶值放到末尾的地方,就完成了排序的过程,相当于是倒着降序。

时间复杂度:

堆排序的最忧,最坏,平均时间复杂度都是O(n*logn)

发布了50 篇原创文章 · 获赞 35 · 访问量 1305

猜你喜欢

转载自blog.csdn.net/ATFWUS/article/details/104442322
今日推荐