当前位置:   article > 正文

【C语言】数据结构#实现堆

【C语言】数据结构#实现堆


目录

(一)堆

(1)堆区与数据结构的堆

(二)头文件

(三)功能实现

 (1)堆的初始化

(2)堆的销毁

(3)插入数据

(4)删除堆顶的数据        

(5)得到堆顶的数据

(6)判断堆是否为空

(7)得到堆内数据个数


正文开始:

(一)堆

(1)堆区与数据结构的堆

          堆区和数据结构中的堆是两个不同的概念。

       

  1. 堆区 (Heap) :堆区是计算机内存中的一部分,用于存储动态分配的内存空间。在程序运行时,堆区用于存储使用 new 或 malloc 等方法分配的内存空间,在程序运行结束后,由程序员手动释放。堆区是一块较大的内存区域,用于存储动态分配的数据。堆区的大小可以动态增长或缩小,具有较高的灵活性。堆区的访问速度较慢,但能够存储较大的数据。

  2. 数据结构中的堆:在数据结构中,堆是一种特殊的树状结构,具有以下特点:

  • 堆是一个完全二叉树,即除了最底层外,其他层都是满的,最底层的结点从左到右连续排列。
  • 堆中的每个结点的值都大于等于(或小于等于)其子结点的值,根结点是树中最大(或最小)的结点。
  • 堆可以分为最大堆和最小堆,最大堆的根结点是整个堆中最大的元素,最小堆的根结点是整个堆中最小的元素。

        

         在数据结构中,堆通常用于实现优先队列(Priority Queue)和堆排序(Heap Sort)等算法。堆的插入和删除操作的时间复杂度都为 O(log n),其中 n 是堆中元素的个数。

(二)头文件

        STL(Standard Template Library)是C++标准库中的一个组件,提供了一系列的通用数据结构和算法,以及一些函数模板,用于简化C++程序的开发。STL包括了容器(Containers)、算法(Algorithms)和迭代器(Iterators)三个主要部分。

  1. 容器(Containers):STL提供了多种容器,包括向量(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等。这些容器提供了不同的数据结构和操作,方便了数据的存储和处理。

  2. 算法(Algorithms):STL提供了一系列的算法,包括排序、查找、合并、变序、计数等等。这些算法可以对容器中的元素进行操作,使得程序更加高效和简洁。

  3. 迭代器(Iterators):STL的迭代器是一个泛型指针,用于遍历容器中的元素。迭代器提供了一种统一的访问容器元素的方式,使得算法可以独立于容器而使用。

        本文根据Cpp的STL来实现堆的功能,包括堆的初始化,销毁,插入数据,删除堆顶的数据,得到堆顶的数据,判断堆是否为空,得到堆内数据个数等七个功能接口。

        本文基于顺序表实现堆;

        这里不加解释的给出头文件,根据头文件实现堆的功能:

        命名:Heap.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<stdbool.h>
  5. #include<assert.h>
  6. typedef int HPDatatype;
  7. typedef struct Heap
  8. {
  9. HPDatatype* a;//存储数据的数组
  10. int size; //堆内数据的个数
  11. int capacity; //顺序表的容量
  12. }HP;
  13. //初始化
  14. void HPinit(HP* php);
  15. //销毁
  16. void HPDestroy(HP* php);
  17. //插入数据
  18. void HPpush(HP* php, HPDatatype x);
  19. //删除数据,规定删除堆顶的数据
  20. void HPpop(HP* php);
  21. //得到堆顶数据
  22. HPDatatype HPtop(HP* php);
  23. //判空
  24. bool HPempty(HP* php);
  25. //数据个数
  26. int HPsize(HP* php);

         

(三)功能实现

 (1)堆的初始化

堆的初始化

        首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        将数组置空,顺序表的大小与容量置0;

  1. //初始化
  2. void HPinit(HP* php)
  3. {
  4. assert(php);
  5. php->a = NULL;
  6. php->capacity = php->size = 0;
  7. }

 

(2)堆的销毁

堆的销毁

          首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        释放掉动态申请的顺序表内的数组,顺序表的大小与容量置0;

  1. //销毁
  2. void HPDestroy(HP* php)
  3. {
  4. assert(php);
  5. free(php->a);
  6. php->capacity = php->size = 0;
  7. }

 

(3)插入数据

 

插入数据

         首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        其次判断数组内数据是否满了

        ——如果顺序表容量等于数组的大小,代表数据满了,那么进行扩容。Newcapacity的赋值通过三目操作符实现:如果capacity为初始值0,Newcapacity赋值为4,否则赋值为2*capacity。如果realloc申请失败,打印错误信息并返回。若申请成功,压入数据。

        ——如果数据没有满,直接在数组中插入数据,由于插入的数据不一定与原数据成堆,所以要进行向上调整

        调整需要交换,于是提前给出交换的功能接口:

 交换

  1. //传址交换
  2. void Swap(HPDatatype* p1, HPDatatype* p2)
  3. {
  4. HPDatatype tem = *p1;
  5. *p1 = *p2;
  6. *p2 = tem;
  7. }

 

 什么是向上调整?

         堆在逻辑上是二叉树,在物理上实际上是数组,数组的下标如下:

         在堆中,有一个规律:

  •         父节点下标 = (子节点下标 - 1) / 2;
  •         左子节点下标 = (父结点下标  * 2) + 1;
  •         右子节点下标 = (父结点下标  * 2) + 2;

        于是,我们可以通过一个节点的下标,找到他的父和子的下标 ;

本文以实现小堆为例 

向上调整

        将新插入的节点与其父节点比较,如果新节点小于父结点,两节点交换值,继续迭代进行;

         直到新节点的值大于等于父结点,或者已经比到了根节点才停止;

  1. //push实现小堆的向上调整
  2. void AdgustUP(HPDatatype* a,int child)
  3. {
  4. int parent = (child - 1) / 2;
  5. while (child > 0)
  6. {
  7. if (a[child] < a[parent])
  8. {
  9. Swap(&a[child],&a[parent]);
  10. child = parent;
  11. parent = (child - 1) / 2;
  12. }
  13. else
  14. {
  15. break;
  16. }
  17. }
  18. }

 

 插入数据

 

  1. //插入数据
  2. void HPpush(HP* php, HPDatatype x)
  3. {
  4. assert(php);
  5. //数据满,realloc扩容,得到新的大容量顺序表
  6. if (php->capacity == php->size)
  7. {
  8. int Newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  9. HPDatatype* tem = (HPDatatype*)realloc(php->a, Newcapacity * sizeof(HPDatatype));
  10. if (tem == NULL)
  11. {
  12. perror("realloc fail!");
  13. return;
  14. }
  15. php->a = tem;
  16. php->capacity = Newcapacity;
  17. }
  18. //数据不满,直接插入
  19. php->a[php->size] = x;
  20. php->size++;
  21. //向上调整
  22. AdgustUP(php->a,php->size-1);
  23. }

 

(4)删除堆顶的数据        

删除数据

        删除数据规定的是删除堆顶的数据;

        函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        如何删除?

        -直接删除,由于二叉树的兄弟节点是没有大小关系的,如果直接删除根节点,那么所有节点的下标减一,这意味着原来的的二叉树的结构就被完全破坏了,接下来只能重新建堆,代价太大。

        -先交换堆顶与最后一个数据,然后删除最后一个数据(其实就是原堆顶数据),然后进行向下调整;

        这样既没有完全破换二叉树的结构,只有一个数据需要调整位置,又操作简便只需size--即可。

向下调整

        思路与向上调整基本一致;

  1. //小堆向下调整——找小
  2. void AdgustDown(HPDatatype* a, int n, int parent)
  3. {
  4. //假设左孩子小
  5. int child = parent * 2 + 1;
  6. while (child < n)
  7. {
  8. //如果右孩子小,假设不成立
  9. if (child + 1 < n && a[child] > a[child + 1])
  10. {
  11. child++;
  12. }
  13. //此时,child表示较小的孩子
  14. if (a[child] < a[parent])
  15. {
  16. Swap(&a[child], &a[parent]);
  17. parent = child;
  18. child = parent * 2 + 1;
  19. }
  20. else
  21. {
  22. break;
  23. }
  24. }
  25. }

删除数据

  1. //删除数据,规定删除堆顶的数据
  2. void HPpop(HP* php)
  3. {
  4. assert(php);
  5. //交换堆顶与最后一个数据
  6. Swap(&php->a[0], &php->a[php->size - 1]);
  7. //删除堆顶的数据
  8. php->size--;
  9. //向下调整
  10. AdgustDown(php->a, php->size,0);
  11. }

(5)得到堆顶的数据

          首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        其次,堆不为空,通过assert断言实现;

        直接返回堆顶的数据即可;

  1. //得到堆顶数据
  2. HPDatatype HPtop(HP* php)
  3. {
  4. assert(php);
  5. assert(!HPempty(php));
  6. return php->a[0];
  7. }

 

(6)判断堆是否为空

         首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

         直接返回判断表达式的值即可;(若为空,返回真(1);否则返回假(0))

  1. //判空
  2. bool HPempty(HP* php)
  3. {
  4. assert(php);
  5. return php->size == 0;
  6. }

 

(7)得到堆内数据个数

          首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

        直接返回堆内数据个数即可;

  1. //数据个数
  2. int HPsize(HP* php)
  3. {
  4. assert(php);
  5. return php->size;
  6. }


完~

未经作者同意禁止转载 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号