【数据结构】堆-C语言版(一)

简介: 【数据结构】堆-C语言版

堆的概念及结构

如果有一个关键码的集合K = ,把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:(或),i = 0,1,2,······,则称为小堆或大堆。

小根堆:父亲小于等于孩子。

大根堆:父亲大于等于孩子。


堆的性质:

(1)堆中某个结点的值总是不大于或不小于其父结点的值;

(2)堆总是一棵完全二叉树。

大根堆可以用来选择最大的数,小根堆可以用来选择最小的数。但大根堆和小根堆不能用来排序(从上图小根堆的排序结构可以看出)。

对堆中的结点进行编号:

由于堆是完全二叉树,物理存储结构就像数组,在堆中,如果已知父亲的下标是parent,则左右孩子的下标为:

leftchild = parent*2+1

rightchild = parent*2+2

如果已知孩子的下标,则父亲下标为:

parent = (child-1)/2

堆的实现

在进行堆的构造和堆排序之前需要了解向下调整算法:


向下调整算法:

如果一棵二叉树的左子树和右子树恰好都是小(大)堆,但只有根结点不满足小(大)堆的定义,如何把这棵二叉树调成小(大)堆呢?需要使用向下调整算法调成小(大)堆

1.选出左右孩子中的小(大)孩子

2.小(大)孩子跟父亲比:

  a.如果小孩子比父亲小(大),则让小(大)孩子跟父亲交换,并把原来孩子的位置当成父亲继续往下调整,直到走到叶子结点。

  b.如果小孩子比父亲大(小),则不需要处理,调整完成,整棵树已经是最小(大)堆。

如下面这棵二叉树,蓝色框内左右子树都是小根堆,但是堆顶27和左右孩子15、19并不构成小根堆,使用向下调整算法进行调整:

第1步:选出左右孩子中的小孩子,15和19相比,15小,15比父亲27小,15和27交换

第2步:把原来孩子的位置也就是现在27的位置当成父亲继续往下调整

第3步:选出左右孩子中的小孩子,18和28相比,18小,18比父亲27小,18和27交换

第4步:把原来孩子的位置也就是现在27的位置当成父亲继续往下调整

第5步:选出左右孩子中的小孩子,49和25相比,25小,25比父亲27小,25和27交换

第6步:把原来孩子的位置也就是现在27的位置当成父亲继续往下调整,发现27已经是叶子结点了,调整完毕。

如果左子树或右子树不是小根堆呢?就不能使用向下调整算法了吗?答案是需要先把左右子树构造成小根堆,再使用向下调整算法,最后整棵树调成小根堆。同理,如果左子树或右子树不是大根堆,需要先把左右子树构造成大根堆,再使用向下调整算法,最后整棵树调成大根堆。


构造小根堆:

从倒数第一个非叶子结点开始,从后往前,按编号,依次作为子树向下调整。

第1步:倒数第一个非叶子结点的下标为(n-1-1)/2,即(7-1-1)/2=3,从倒数第一个非叶子结点编号为3的结点5开始作为子树向下调整,5只有一个孩子4,4比5小,交换4和5

第2步:得到如下二叉树,对前一个编号2的位置即结点3作为子树向下调整,3的两个孩子1和7,1小,交换3和1

第3步:得到如下二叉树,对前一个编号1的位置即结点8作为子树向下调整,8的两个孩子5和2,2小,交换8和2

第4步: 得到如下二叉树,对前一个编号0的位置即结点6作为子树向下调整,6的两个孩子8和1,1小,交换6和1

第5步: 得到如下二叉树,现在以结点6为根结点的子树已经不符合小根堆的定义了,需要向下向下调整,6的两个孩子3和7,3小,交换6和3

此时,二叉树已经是小根堆了,调整完毕

建堆代码:

1. #define  _CRT_SECURE_NO_WARNINGS  1
2. void Swap(int *p1, int *p2) 
3. {
4.  int tmp = *p1;
5.  *p1 = *p2;
6.  *p2 = tmp;
7. }
8. 
9. //向下调整算法
10. AdjustDown(int* a, int n, int parent)
11. {
12.   int child = parent * 2 + 1;
13.   while (child <n)
14.   {
15.     //选出左右孩子中的小孩子
16.     if (child + 1 < n && a[child + 1] < a[child])
17.     {
18.       child++;
19.     }
20.     //小孩子比父亲小,交换小孩子和父亲,可能导致不满足堆的定义,需要调整
21.     if (a[child] < a[parent])
22.     {
23.       Swap(&a[child], &a[parent]);
24.       parent = child;
25.       child = parent * 2 + 1;
26.     }
27.     //小孩子比父亲大,跳出while循环,结束这一次的向下调整,否则一直死循环并且什么都不做
28.     else
29.     {
30.       break;
31.     }
32.   }
33. }
34. int main()
35. {
36.   int a[] = { 6,8,5,9,3,7,2 };
37.   //int a1[] = { 1,2,3,4,5,6,7 };
38.   int n = sizeof(a) / sizeof(a[0]);
39.   int i = 0;
40.   for (i = (n - 1 - 1) / 2; i >= 0; i--)
41.   {
42.     AdjustDown(a, n, i);
43.   }
44.   return 0;
45. }


相关文章
|
17天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
91 9
|
14天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
29 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
16天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
59 16
|
16天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
65 8
|
19天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
45 4
|
20天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
51 3
|
19天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
38 0
|
存储 缓存 C语言
数据结构——双链表(C语言)
数据结构——双链表(C语言)
|
5月前
|
存储
数据结构——双向链表(C语言版)
数据结构——双向链表(C语言版)
31 2
|
5月前
|
算法 C语言
数据结构——单向链表(C语言版)
数据结构——单向链表(C语言版)
45 2