的代码就
很轻松出手了
//
// heap_insert - 堆插入数据
// h : 堆对象
// node : 操作对象
// return : void
//
inline void
heap_insert(heap_t h, void * node) {
heap_expand(h);
up(h->fcmp, node, h->data, h->len++);
}
//
// heap_top - 查看堆顶结点数据
// h : 堆对象
// return : 堆顶节点
//
inline void *
heap_top(heap_t h) {
return h->len <= 0 ? NULL : *h->data;
}
//
// heap_top - 摘掉堆顶结点数据
// h : 堆对象
// return : 返回堆顶节点
//
inline void *
heap_pop(heap_t h) {
void * node = heap_top(h);
if (node && --h->len > 0) {
// 尾巴节点一定比小堆顶节点大, 那么要下沉
h->data[0] = h->data[h->len];
down(h->fcmp, h->data, h->len, 0);
}
return node;
}
看完上面代码可以再回看下 down 和 up 代码布局. 是不是堆节点调整全部技巧已经了然于胸 ~
随后我们介绍堆删除任意节点大致算法思路
1' 循环遍历, 找到要删除节点
2' 如果删除后堆空, 或者删除的是最后节点, 那直接搞定
3' 最后节点复制到待删除节点位置处
4' 最后节点和待删除节点权值相等, 不调整节点关系
5' 最后节点比待删除节点权值大, 向下调整节点关系(基于小顶堆设计)
6' 最后节点比待删除节点权值小, 向上调整节点关系
从上可以看出堆删除节点算法复杂度是 O(n) + O(logn) = O(n). 请欣赏具体代码
//
// heap_remove - 堆删除数据
// h : 堆对象
// arg : 操作参数
// fcmp : 比较行为, 规则 fcmp() == 0
// return : 找到的堆节点
//
void *
heap_remove(heap_t h, void * arg, cmp_f fcmp) {
if (h == NULL || h->len <= 0)
return NULL;
// 开始查找这个节点
unsigned i = 0;
fcmp = fcmp ? fcmp : h->fcmp;
do {
void * node = h->data[i];
if (fcmp(arg, node) == 0) {
if (--h->len > 0 && h->len != i) {
// 尾巴节点和待删除节点比较
int ret = h->fcmp(h->data[h->len], node);
// 小顶堆, 新的值比老的值小, 那么上浮
if (ret < 0)
up(h->fcmp, h->data[h->len], h->data, i);
else if (ret > 0) {
// 小顶堆, 新的值比老的值大, 那么下沉
h->data[i] = h->data[h->len];
down(h->fcmp, h->data, h->len, i);
}
}
return node;
}
} while (++i < h->len);
return NULL;
}
到这堆数据结构基本代码都已经搞定. 开始写写测试用例跑跑
#include "heap.h"
#include <stdio.h>
struct node {
int value;
};
static inline int node_cmp(const struct node * l, const struct node * r) {
return l->value - r->value;
}
static void heap_print(heap_t h) {
struct heap {
cmp_f fcmp; // 比较行为
unsigned len; // heap 长度
unsigned cap; // heap 容量
void ** data; // 数据节点数组
} * x = (struct heap *)h;
// 数据打印for (unsigned i = 0; i < x->len; ++i) {
struct node * node = x->data[i];
printf("%d ", node->value);
}
putchar('\n');
}
int main() {
heap_t h = heap_create(node_cmp);
struct node a[] = { { 53 }, { 17 }, { 78 }, { 9 }, { 45 }, { 65 }, { 87 }, { 23} };
for (int i = 0; i < sizeof a / sizeof *a; ++i)
heap_insert(h, a + i);
heap_print(h);
// 数据打印
struct node * node;
while ((node = heap_pop(h))) {
printf("%d ", node->value);
}
putchar('\n');
// 重新插入数据
for (int i = 0; i < sizeof a / sizeof *a; ++i)
heap_insert(h, a + i);
// 删除操作 - 下沉
heap_remove(h, &(struct node){ 17 }, NULL);
heap_print(h);
// 插入操作
heap_insert(h, &(struct node){ 17 });
heap_print(h);
// 删除操作 - 上浮
heap_remove(h, &(struct node){ 78 }, NULL);
heap_print(h);
heap_delete(h, NULL);
return 0;
}
最终运行结果如下
后续堆相关代码变化, 可以参照 heap - https://github.com/wangzhione/structc/blob/master/structc/struct/heap.c
说到引用 github 想起一个 git 好用配置安利给大家 ~ 从此 git ll 就活了.
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global color.interactive auto
git config --global alias.ll "log --graph --all --pretty=format:'%C