堆的C语言实现

在C++中,可以通过std::priority_queue来使用堆。

堆的C语言实现:

heap.c

  1 /** @file heap.c
  2 * @brief 堆,默认为小根堆,即堆顶为最小.
  3 */
  4 #include <stdlib.h> /* for malloc() */
  5 #include <string.h> /* for memcpy() */
  6 typedef int heap_elem_t; // 元素的类型
  7
  8 /**
  9 * @struct
 10 * @brief 堆的结构体
 11 */
 12 typedef struct heap_t
 13 {
 14   int size;  /** 实际元素个数 */
 15   int capacity;   /** 容量,以元素为单位 */
 16   heap_elem_t *elems;  /** 堆的数组 */
 17   int (*cmp)(const heap_elem_t*, const heap_elem_t*);
 18 }heap_t;
 19
 20 /** 元素的比较函数 */
 21 /** 基本类型(如 int, long, float, double)的比较函数 */
 22 int cmp_int(const int *x, const int *y)
 23 {
 24   const int sub = *x - *y;
 25   if(sub > 0)
 26   {
 27     return 1;
 28   }
 29   else if(sub < 0)
 30   {
 31     return -1;
 32   }
 33   else
 34   {
 35     return 0;
 36   }
 37 }
 38
 39 /**
 40 * @brief 堆的初始化.
 41 * @param[out] h 堆对象的指针
 42 * @param[out] capacity 初始容量
 43 * @param[in] cmp cmp 比较函数,小于返回-1,等于返回 0
 44 * 大于返回 1,反过来则是大根堆
 45 * @return 成功返回 0,失败返回错误码
 46 */
 47 int heap_init(heap_t *h, const int capacity, int (*cmp)(const heap_elem_t*, const heap_elem_t*))
 48 {
 49   h->size = 0;
 50   h->capacity = capacity;
 51   h->elems = (heap_elem_t*)malloc(capacity * sizeof(heap_elem_t));
 52   h->cmp = cmp;
 53   return 0;
 54 }
 55
 56 /**
 57 * @brief 释放堆.
 58 * @param[inout] h 堆对象的指针
 59 * @return 成功返回 0,失败返回错误码
 60 */
 61 int heap_uninit(heap_t *h)
 62 {
 63   h->size = 0;
 64   h->capacity = 0;
 65   free(h->elems);
 66   h->elems = NULL;
 67   h->cmp = NULL;
 68   return 0;
 69 }
 70
 71 /**
 72 * @brief 判断堆是否为空.
 73 * @param[in] h 堆对象的指针
 74 * @return 是空,返回 1,否则返回 0
 75 */
 76 int heap_empty(const heap_t *h)
 77 {
 78   return h->size == 0;
 79 }
 80
 81 /**
 82 * @brief 获取元素个数.
 83 * @param[in] s 堆对象的指针
 84 * @return 元素个数
 85 */
 86 int heap_size(const heap_t *h)
 87 {
 88   return h->size;
 89 }
 90
 91 /*
 92 * @brief 小根堆的自上向下筛选算法.
 93 * @param[in] h 堆对象的指针
 94 * @param[in] start 开始结点
 95 * @return 无
 96 */
 97 void heap_sift_down(const heap_t *h, const int start)
 98 {
 99   int i = start;
100   int j;
101   const heap_elem_t tmp = h->elems[start];
102   for(j = 2 * i + 1; j < h->size; j = 2 * j + 1)
103   {
104     if(j < (h->size - 1) && h->cmp(&(h->elems[j]), &(h->elems[j + 1])) > 0)
105     {
106       j++; /* j 指向两子女中小者 */
107     }
108     // tmp <= h->data[j]
109     if(h->cmp(&tmp, &(h->elems[j])) <= 0)
110     {
111       break;
112     }
113     else
114     {
115       h->elems[i] = h->elems[j];
116       i = j;
117     }
118   }
119   h->elems[i] = tmp;
120 }
121
122 /*
123 * @brief 小根堆的自下向上筛选算法.
124 * @param[in] h 堆对象的指针
125 * @param[in] start 开始结点
126 * @return 无
127 */
128 void heap_sift_up(const heap_t *h, const int start)
129 {
130   int j = start;
131   int i= (j - 1) / 2;
132   const heap_elem_t tmp = h->elems[start];
133   
134   while(j > 0)
135   {
136     // h->data[i] <= tmp
137     if(h->cmp(&(h->elems[i]), &tmp) <= 0)
138     {
139       break;
140     }
141     else
142     {
143       h->elems[j] = h->elems[i];
144       j = i;
145       i = (i - 1) / 2;
146     }
147   }
148   h->elems[j] = tmp;
149 }
150
151 /**
152 * @brief 添加一个元素.
153 * @param[in] h 堆对象的指针
154 * @param[in] x 要添加的元素
155 * @return 无
156 */
157 void heap_push(heap_t *h, const heap_elem_t x)
158 {
159   if(h->size == h->capacity)
160   {
161     /* 已满,重新分配内存 */
162     heap_elem_t* tmp = (heap_elem_t*)realloc(h->elems, h->capacity * 2 * sizeof(heap_elem_t));
163     h->elems = tmp;
164     h->capacity *= 2;
165   }
166   h->elems[h->size] = x;
167   h->size++;
168   heap_sift_up(h, h->size - 1);
169 }
170
171 /**
172 * @brief 弹出堆顶元素.
173 * @param[in] h 堆对象的指针
174 * @return 无
175 */
176 void heap_pop(heap_t *h)
177 {
178   h->elems[0] = h->elems[h->size - 1];
179   h->size --;
180   heap_sift_down(h, 0);
181 }
182
183 /**
184 * @brief 获取堆顶元素.
185 * @param[in] h 堆对象的指针
186 * @return 堆顶元素
187 */
188 heap_elem_t heap_top(const heap_t *h)
189 {
190   return h->elems[0];
191 }
时间: 2024-08-30 11:29:13

堆的C语言实现的相关文章

轻松学习C语言编程的秘诀:总结+灵感

目前在准备一套C语言的学习教程,所以我这里就以C语言编程的学习来讲.注意,讲的是"轻松学习",那种不注重方法,拼命玩命的方式也有其效果,但不是我提倡的.我讲究的是在方式方法对头.适合你.减轻你学习负担和心里压力的前提下,才适当的抓紧时间. 因此,探索一种很好的学习方法就是我所研究的主要内容. 众所周知,学习C语言并非易事,要学好它更是难上加难.这和你期末考试背会几个题目的答案考上满分没多大关系,也就是说你考试满分也说明不了你学好.学精通了C语言.那么怎么才算学精通C语言?闭着眼睛对自己

嵌入式Linux C语言(三)——指针与函数

嵌入式Linux C语言(三)--指针与函数 指针对函数的功能有巨大的贡献,指针能够将数据传递给函数,并且允许函数对数据进行修改.指针对于函数的作用主要有两方面:将指针传递给函数和声明函数指针. 一.程序的栈和堆 程序的栈和堆是C语言程序运行的运行时元素. 1.程序栈 程序栈是支持函数执行的内存区域,通常和堆共享一块内存区域,通常程序栈占据内存区域的下部,堆用内存区域的上部.程序栈存放栈帧,栈帧存放函数参数和局部变量.调用函数时,函数的栈帧被推倒栈上,栈向上长出一个栈帧,当函数终止时,函数的栈帧

基本数据结构之堆

堆的定义 堆是一个完全二叉树或者近似的完全二叉树 堆的性质 父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 堆的存储 一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2.它的左右子结点下标分别为2 * i + 1和2 * i + 2 堆的特点 插入元素和弹出堆顶元素的时间复杂度lg(n) 判断序列是不是堆 根据性质判断 比如输入序列15,30,22,93,52,71,89,15是根节点,可以看出满足性

浅谈C#堆栈与托管堆的工作方式(转)

C#初学者经常被问的几道辨析题,值类型与引用类型,装箱与拆箱,堆栈,这几个概念组合之间区别,看完此篇应该可以解惑. 俗话说,用思想编程的是文艺程序猿,用经验编程的是普通程序猿,用复制粘贴编程的是2B程序猿,开个玩笑^_^. 相信有过C#面试经历的人,对下面这句话一定不陌生: 值类型直接存储其值,引用类型存储对值的引用,值类型存在堆栈上,引用类型存储在托管堆上,值类型转为引用类型叫做装箱,引用类型转为值类型叫拆箱. 但仅仅背过这句话是不够的. C#程序员不必手工管理内存,但要编写高效的代码,就仍需

C++自学笔记_数组和指针_《C++ Primer》

1.数据定义中的类型可以是内置数据类型或者类类型,除引用数据类型之外,数组元素的类型可是是任何数据类型.没有所有元素都是引用数据类型的数组. 2.数组的维数必须要用值大于等于1的表达式定义.此常量表达式只能包含整形字面值常量.枚举常量或者用常量表达式初始化的const对象,否则在编译阶段不知道数组的维数,对于一个非const对象,只有运行时才能获得它的值. const unsigned buf_size=512; int staff_size=27; char input_buffer[buf_

OC语法基本使用

首先来了解一下: 1.面向过程与面向对象 1.1面向过程编程 什么是面向过程? Procedure,流程: 主干流程和分支流程: 子模块,代码的复用: 特点:所有的工作,一个人干,不利于工程协作: 1.2 类和对象 1.2.1 从生活逻辑 ┏  类  ┓         ┏  对象   ┓ ┃ 人           ┃ 乔布斯          库克 ┃ 电脑         ┃ 你桌子上那台    我桌子上这台 类class(不占用内存) 一个类的实例 instance(占内存) 1.2.2

深入C#内存管理来分析值类型&amp;引用类型,装箱&amp;拆箱,堆栈几个概念组合之间的区别

C#初学者经常被问的几道辨析题,值类型与引用类型,装箱与拆箱,堆栈,这几个概念组合之间区别,看完此篇应该可以解惑. 俗话说,用思想编程的是文艺程序猿,用经验编程的是普通程序猿,用复制粘贴编程的是2B程序猿,开个玩笑^_^. 相信有过C#面试经历的人,对下面这句话一定不陌生: 值类型直接存储其值,引用类型存储对值的引用,值类型存在堆栈上,引用类型存储在托管堆上,值类型转为引用类型叫做装箱,引用类型转为值类型叫拆箱. 但仅仅背过这句话是不够的. C#程序员不必手工管理内存,但要编写高效的代码,就仍需

今日刷题总结20

C++的内存布局 在C++中内存被分为5个部分,分别是堆.栈.自由存储区.全局/静态存储区以及常量存储区. 自由存储区与堆的区别 堆是C语言和操作系统里的术语,它是由操作系统维护的一块特殊的内存,当C程序调用malloc函数进行动态内存分配时就是从堆上获取内存的,而调用free函数可以交还分配的内存.自由存储区是C++基于new和delete操作符的一个抽象概念,凡是通过new来申请的内存区域都可称为自由存储区.几乎所有的C++编译器默认都使用堆来实现自由存储区,即缺省的全局库函数operato

【C++ Primer每日一刷之八】之九 创建动态数组

4.3.1. 创建动态数组 数组类型的变量有三个重要的限制:[数组长度固定不变],[在编译时必须知道其长度],[数组只在定义它的块语句内存在]. 实际的程序往往不能忍受这样的限制--它们需要在运行时动态地分配数组. 虽然数组长度是固定的,但动态分配的数组不必在编译时知道其长度,可以(通常也是)在运行时才确定数组长度.与数组变量不同,动态分配的数组将一直存在,直到程序显式释放它为止.每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆. C 语言程