看正月点灯笼老师的笔记—堆排序


视频笔记:https://www.bilibili.com/video/BV1Eb41147dK?from=search&seid=7630499509795698118

 

一,大根堆

堆排序要用到 大根堆的数据结构

1,为完全二叉树:添加新节点的顺序是:从上到下,从左到右

2,满足:父节点 > 子节点

 

二,heapify:堆调整

1,针对结点 i,将其两个子节点找出来,此三个结点构成一个最小单位的完全二叉树(越界的忽略)

2,找到这个最小单位的完全二叉树 的最大值,并将其交换至父节点的位置

3,递归调用,维护交换后 子节点与其子结点被破坏的堆关系,递归出口为叶节点

4,结果,构造一个最小单位的完全二叉树对应的堆

注意边界的判定:上界:元素个数:n,在求孩子节点时,如果孩子结点的下标大于等于n,则不参与任何操作(指:不参与堆调整)。而且递归的递归出口是元素下标大于等于n,这是因为堆调整是自顶向下进行调整的,同样因此,就不用考虑元素下标小于0的情况。

 

二,build_heap:构造堆

① 用一维数组表示:堆(因为堆的结构与完全二叉树一样)

  使用数组下标为 0 的位置,则 a[ i ] 的父节点为 a[ (i-1/)2 ],两个子节点为 a[ i*2+1 ] 和 a[ i*2+2 ]

② 由于 heapify 会递归调用维护下面的堆,所以要构造完整的堆,要从下面往上构造:先找到最后一个父结点,再从最后一个父结点开始向上调用 heapify 

 

三,heap_sort:利用堆进行堆排序

由堆可以得到什么呢 ?首先根结点是最大的,第二层的两个节点是 第二大和第三大的。

于是,利用这点就可以排序了

1,可以想到:既然根结点已经是最大的,那么就把这个数提走,剩下的再构造一个新堆。然后再提走根结点,再构造新的堆,循环往复。

2,但是这样就会面临一个问题?拿走根结点之后,根结点的数值由谁取代?

3,由于该结点要满足:移动该节点到根结点后,要对堆的结构破坏尽可能小,而且堆的大小还要因为结点少了一个进行减1(即 数组表示堆的范围要减 一)。显然最后一个结点是最好的选择了。移动后既不会影响到后面的结点,原来的位置还因为空了可以直接取消掉。

4,关键的 一步出现了。我们用最后一个结点替换根结点,此时根结点和第二层的两个结点构成一个最小单位的最小二叉树,此时就可以直接调用 heapify函数 ,形成一个新的堆。没有必要用 build_heap函数,因为此时最大值就在第二层,根结点会因为递归维护被替换到最后一层,其他结点也会因为递归维护而得到调整。

5,至于拿出来的结点放哪,当然还是放在原来的数组里咯。由于数组前面存的是堆,于是可以从最后面的位置依次往前放,还可以形成升序序列。这也是为什么要用大根堆,因为默认我们想要的是升序序列,如果想要降序序列的话,也可以用小根堆。

 

四,代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void swap(int a[], int i, int j) // 交换结点位置
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}
void heapify(int tree[], int n, int i) // 堆调整
{
	if (i >= n)
		return;
	int c1 = 2 * i + 1;
	int c2 = 2 * i + 2;

	int max = i;
	if (c1 < n&&tree[c1] > tree[max])
		max = c1;
	if (c2 < n&&tree[c2] > tree[max])
		max = c2;
	if (max != i)
	{
		swap(tree, max, i); // 将最大值移至父节点
		heapify(tree, n, max); // 递归维护下面的结点
	}
}
void build_heap(int tree[], int n) // 构造堆
{
	int last_node = n - 1;
	int parent = (last_node - 1) / 2; // 最后一个父结点

	for (int i = parent; i >= 0; i--) // 从最后一个父结点开始向上调用 heapify
		heapify(tree, n, i);
}
void heap_sort(int tree[], int n) // 利用堆进行堆排序
{
	build_heap(tree, n); 
	for (int i = n - 1; i >= 0; i--) // 每次取出根结点后,堆减少一个节点
	{
		swap(tree, i, 0);
		heapify(tree, i, 0); // 构造一个新堆
	}
}
int main(void)
{
	int tree[] = { 2,5,3,1,10,4 };
	int n = 6;

	heap_sort(tree, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", tree[i]);
	}puts("");

	system("pause");
	return 0;
}

  

over ! !  感谢观看!!

========== ========= ========= ======= ====== ===== ==== === == =

寄扬州韩绰判官  杜牧 唐

青山隐隐水迢迢,秋尽江南草未凋。

二十四桥明月夜,玉人何处教吹箫。

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM