二叉堆的C语言实现

                                    二叉堆的C语言实现               
二叉堆的实现数据结构中如何使用,我任务主要是在操作系统中的任务优先级调度问题,当然也可以用于实现堆排序问题,比如找出数组中的第K个最小值问题,采用二叉堆能够快速的实现,今天我就采用C语言实现了一个简单的二叉堆操作,完成这些数据结构我并不知道能干什么,我就当自己在练习C语言的功底吧。逐步完成自己的代码,希望自己在知识的理解力上有一定的提高。
 
二叉堆是非常有特点的数据结构,可以采用简单的数组就能实现,当然链表的实现也是没有问题的,毕竟是一个二叉树问题,当然可以采用链表实现。采用数组实现时,可以找到两个特别明显的规律:
 
左儿子:L_Son = Parent * 2;
右儿子:R_Son = Parent * 2 + 1;
 
二叉堆是一颗完全填满的树,可能例外的是在底层,底层上的元素是从左到右填入,当然二叉堆可以是基于大值的排序,也可以是基于小值的排列形式,本文采用简单的基于小值的形式。主要完成的操作:1、最小值的删除操作,该操作会删除根节点,然后提升儿子节点来代替根节点,具体的实现过程中通过提升左右儿子中较小的作为父结点,依此提升直到到达最底层,这种实现方式叫做下虑法。2、数据的插入操作,插入操作可能会破坏二叉堆的结构,一般在最底层创建一个空穴,然后比较插入值与空穴父结点的值,如果大于父结点的值,那么直接插入到空穴中,如果小于父结点,则将父结点的值插入到刚创建的空穴中,在父结点所在位置上形成新的父结点,这时候再和父结点的父结点比较,具体操作如上所述,直到找到具体的插入地址。当结点个数为偶数时,在删除操作中需要注意节点是否有右儿子的情况。具体的可以参考代码中的说明。
 
具体的实现如下:
结构体:

点击(此处)折叠或打开

  1. #ifndef __BINARYHEAP_H_H_
  2. #define __BINARYHEAP_H_H_

  3. #include <stdlib.h>
  4. #include <assert.h>

  5. #define bool int
  6. #define true 1
  7. #define false 0

  8. /*打算采用数组的方式实现完全二叉堆*/
  9. typedef struct _binaryheap
  10. {
  11.         /*因为需要动态扩展,
  12.         *采用静态数组不方便*/
  13.         int * parray;
  14.         /*目前存在的结点*/
  15.         int currentSize;
  16.         /*树的实际容量*/
  17.         int capacity;
  18. }BinaryHeap_t, *BinaryHeap_handle_t;

  19. #ifdef __cplusplus
  20. extern "C"
  21. {
  22. #endif

  23. bool init_BinaryHeap(BinaryHeap_handle_t heap, int capacity);
  24. bool alloc_BinaryHeap(BinaryHeap_handle_t *heap, int capacity);
  25. void delete_BinaryHeap(BinaryHeap_handle_t heap);
  26. void free_BinaryHeap(BinaryHeap_handle_t *heap);

  27. bool insert(BinaryHeap_handle_t heap,int value);
  28. int deleteMin(BinaryHeap_handle_t heap);
  29. bool isEmpty(BinaryHeap_handle_t heap);

  30. #ifdef __cplusplus
  31. }
  32. #endif

  33. #endif
实现的接口函数如下:

点击(此处)折叠或打开

  1. #include "binaryheap.h"

  2. bool isEmpty(BinaryHeap_handle_t heap)
  3. {
  4.         assert(heap != NULL);
  5.         return heap->currentSize == 0;
  6. }

  7. bool init_BinaryHeap(BinaryHeap_handle_t heap, int capacity)
  8. {
  9.         int *parray = NULL;

  10.         if(heap == NULL)
  11.                 return false;
  12.     
  13.         parray = (int *)calloc(capacity+1,sizeof(int));
  14.         if(parray == NULL)
  15.                 return false; 
  16.     
  17.         heap->parray = parray;
  18.         heap->capacity = capacity;
  19.         heap->currentSize = 0;

  20.         return true;
  21. }

  22. void delete_BinaryHeap(BinaryHeap_handle_t heap)
  23. {
  24.         assert(heap != NULL && heap->parray != NULL);

  25.         heap->capacity = 0;
  26.         heap->currentSize = 0;

  27.         free(heap->parray);
  28.         heap->parray = NULL;
  29. }

  30. void free_BinaryHeap(BinaryHeap_handle_t *heap)
  31. {
  32.         assert(*heap != NULL);

  33.         (*heap)->capacity = 0;
  34.         (*heap)->currentSize = 0;

  35.         free((*heap)->parray);
  36.         (*heap)->parray = NULL;

  37.         free(*heap);
  38.         *heap = NULL;
  39. }

  40. bool alloc_BinaryHeap(BinaryHeap_handle_t *heap, int capacity)
  41. {
  42.         int *parray = NULL;

  43.         if(*heap != NULL)
  44.                 return false;

  45.         *heap = (int *)calloc(1, sizeof(BinaryHeap_t));
  46.         if(*heap == NULL)
  47.                 return false;

  48.         /*其中的1,主要是为了使得数组从下标1开始计算*/
  49.         parray =(int *)calloc(capacity + 1, sizeof(int));
  50.         if(parray == NULL)
  51.                 return false;

  52.         (*heap)->parray = parray;
  53.         (*heap)->capacity = capacity;
  54.         (*heap)->currentSize = 0;

  55.         return true;
  56. }

  57. /**************************************************
  58.  * 采用上虑法实现数据的插入操作
  59.  * 上虑法的实现方式比较简单,首先创建一个空节点
  60.  * 然后将需要插入的值与当前空穴的父结点进行比较
  61.  * 如果大于父结点,直接插入空穴中
  62.  * 如果小于父结点的值,则将父结点的值下拉到空穴中
  63.  * 之前父结点的位置就是空穴,接着与上层比较
  64.  * 直到找到父结点大于当前插入值的情况
  65.  **************************************************/
  66. bool insert(BinaryHeap_handle_t heap, int value)
  67. {
  68.         int index = 0;

  69.         if(heap == NULL || heap->parray == NULL)
  70.                 return false;

  71.         /*得到一个新的空穴下标*/
  72.         index = ++heap->currentSize;
  73.         /*条件是不是第一个下标和插入值比对应父结点小*/
  74.         while(index > 1 && value < heap->parray[index/2])
  75.         {
  76.                 /*将父结点保存到当前结点处*/
  77.                 heap->parray[index] = heap->parray[index/2];
  78.                 /*得到父结点的空穴位置*/
  79.                 index /= 2;
  80.         }
  81.         /*将插入的值保存到剩余的空穴中*/
  82.         heap->parray[index] = value;

  83.         return true;
  84. }

  85. /***********************************************************
  86.  * 下虑法实现数据的重排序操作
  87.  * 实现的方式,将子结点的两个儿子进行比较,将小的提升
  88.  * 需要注意的是如何让判断节点是否一定存在右儿子
  89.  * 实现的方式主要是利用了二叉堆的特性:
  90.  * 2*pare = L_child
  91.  * 2*pare + 1 = R_child; 
  92.  ***********************************************************/
  93. static void presort_BinaryHeap(BinaryHeap_handle_t heap,int hole)
  94. {
  95.         /*这是二叉堆的特性*/
  96.         int child = hole * 2;
  97.         /*保存当前数据操作*/
  98.         int tmp = 0;

  99.         assert(heap != NULL && heap->parray != NULL);

  100.         tmp = heap->parray[hole];
  101.         /*hold * 2 <= heap->currentSize 判断了当前结点是否为最低层*/
  102.         for(; hole * 2 <= heap->currentSize; hole = child)
  103.         {
  104.                 child = hole * 2;

  105.                 /*******************************
  106.                 *这句代码解决是否存在右结点的问题
  107.                 *并确定了那个子结点提升的问题
  108.                 *******************************/
  109.                 if((child != heap->currentSize)
  110.                         && (heap->parray[child + 1] < heap->parray[child]))
  111.                         child ++;

  112.                 if(heap->parray[child] < tmp)
  113.                 {
  114.                         /*将子结点提升为父结点*/
  115.                        heap->parray[hole] = heap->parray[child];
  116.                 }
  117.         }
  118.         /*到达了最低的层,也就是树叶*/
  119.         heap->parray[hole] = tmp;
  120. }

  121. /*实现数据的下虑法实现数据的删除操作*/
  122. int deleteMin(BinaryHeap_handle_t heap)
  123. {
  124.         int ret = 0;
  125.         int index = 0;

  126.         assert(!isEmpty(heap));
  127.         /*需要返回的值*/
  128.         ret = heap->parray[1];

  129.         /*将最后需要释放内存空间的值保存到第一个内存空间中*/
  130.         heap->parray[1] = heap->parray[heap->currentSize --];
  131.         /*从表头开始将新的二叉树进行重新排序*/
  132.         presort_BinaryHeap(heap, 1);

  133.         return ret;
  134. }
测试代码:

点击(此处)折叠或打开

  1. #include "binaryheap.h"
  2. #include <stdio.h>
  3. #include <time.h>

  4. void print_binaryheap(BinaryHeap_handle_t heap)
  5. {
  6.         int i = 0;

  7.         assert(heap != NULL && heap->parray != NULL);

  8.         for(= 1; i <= heap->currentSize; ++ i)
  9.         {
  10.                 if(%6)
  11.                         printf("%d\t",heap->parray[i]);
  12.                 else
  13.                         printf("\n%d\t",heap->parray[i]);
  14.         }
  15.         printf("\n");
  16. }

  17. int main()
  18. {
  19.         int i = 0;
  20.         int value = 0;

  21.         srand((int)time(0));
  22.         printf("********Test Binaryheap**************\n");

  23.         BinaryHeap_t bheap;
  24.         BinaryHeap_handle_t *pheap = NULL;

  25.         printf("init and alloc test:\n");
  26.         if(init_BinaryHeap(&bheap,10))
  27.        {
  28.                 printf("init_BinaryHeap() successed!\n");
  29.         }
  30.         if (alloc_BinaryHeap(&pheap,15));
  31.         {
  32.                 printf("alloc_BInaryHeap() successed!\n");
  33.         }

  34.         printf("***insert test*****\n");
  35.         for(; i < 10; ++ i)
  36.         {
  37.                 if(!insert(&bheap,* i - rand()%20))
  38.                 {
  39.                         printf("i = %d:insert failed !!\n",i);
  40.                 }
  41.         }
  42.         for(= 0; i < 15; ++ i)
  43.         {
  44.                 if(!insert(pheap,* 8 - rand()%20))
  45.                 {
  46.                         printf("i = %d:insert failed!!\n",i);
  47.                 }
  48.         }

  49.         print_binaryheap(&bheap);
  50.         print_binaryheap(pheap);

  51.         printf("****deleteMin test****\n");
  52.         for(= 0; i < 5; ++ i)
  53.         {
  54.                 value = deleteMin(&bheap);
  55.                 printf("bheap deleted:%d\n",value);
  56.                 value = deleteMin(pheap);
  57.                 printf("pheap deleted:%d\n",value);
  58.         }
  59.         print_binaryheap(&bheap);
  60.         print_binaryheap(pheap);

  61.         printf("deleteMin test successed\n");

  62.         printf("****delete and free test:*******\n");
  63.         delete_BinaryHeap(&bheap);

  64.         printf("Is the bheap empty ? %s\n",
  65.                         isEmpty(&bheap)?"Yes":"No");

  66.         free_BinaryHeap(&pheap);

  67.         printf("*********Test successed!***********\n");
  68.         pheap = NULL;
  69.         return 0;
  70. }
测试结果:

点击(此处)折叠或打开

  1. [gong@Gong-Computer c_binaryheap]$ ./testbinaryheap
  2. ********Test Binaryheap**************
  3. init and alloc test:
  4. init_BinaryHeap() 
  5. alloc_BInaryHeap() 
  6. ***insert test*****
  7. -11    -9    -9    14    15    
  8. 10    21    23    40    26    
  9. -16    2    14    20    13    
  10. 21    33    49    61    67    76    
  11. 86    83    95    109    
  12. ****deleteMin test****
  13. bheap deleted:-11
  14. pheap deleted:-16
  15. bheap deleted:-9
  16. pheap deleted:2
  17. bheap deleted:-9
  18. pheap deleted:13
  19. bheap deleted:10
  20. pheap deleted:14
  21. bheap deleted:14
  22. pheap deleted:20
  23. 15    23    21    40    26    
  24. 21    49    21    61    67    
  25. 76    33    95    83    109    
  26. deleteMin test successed
  27. ****delete and free test:*******
  28. Is the bheap empty ? Yes
  29. *********Test

从上面的测试结果可知,基本上实现了二叉堆的基本插入、删除操作。代码的关键点在于删除中的下虑和插入过程中的上虑操作。以及如何判断代码是否存在右儿子,如何充分运用二叉堆的特性。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值