堆:
- 此处堆为二叉树的应用,不是计算机中用于管理动态内存的堆。
- 形状是完全二叉树。
- 堆分两种:最大堆,最小堆。
- 最大堆:每个节点比子树所有节点的数值都大,根节点为最大值。
- 最小堆:每个节点比子树所有节点的数值都小,根节点为最小值。
- 左右节点的数值没有顺序要求,但有左子节点才能有右子节点。
- 一般用数组实现堆。最小堆可以实现优先队列。
注:完全二叉树:每个节点最多2个节点,倒数第二层级(含)之前层级的所有节点都有元素,最后一层级从左到右依次有元素(中间不能断)。
子树:节点及其所有子节点形成子树。(即节点,左右子节点,左右子节点的左右子节点,。。。)
根节点:没有父节点。
兄弟节点:其父节点是同一个节点。
节点 | 索引号(根节点为0) |
---|---|
节点 | x |
父节点 | (x-1)// 2 |
左兄弟节点(右节点才有) | x - 1 |
右兄弟节点(左节点才有) | x + 1 |
左子节点 | 2x + 1 |
右子节点 | 2x + 2 |
C语言实现:(使用数组实现最小堆)
创建堆(结构体数据类型):记录数组地址和元素个数
// 堆(结构体数据类型)
typedef struct Heap
{int *p; // 记录数组地址int n; // 记录元素个数
} Heap; // 别名
(函数)堆初始化:
// 堆初始化
void init(Heap *heap, int length)
{heap->p = (int *)malloc(length * sizeof(int)); // 分配内存空间if(heap->p == NULL){perror("Memory allocation failed");exit(-1);}heap->n = 0; // 元素个数,初始化为0
}
创建堆变量,并初始化
Heap heap; // 创建堆变量
init(&heap, 8); // 设置数组最大元素个数为8
添加元素:
1、先在末尾添加元素。
2、(向上调整)与父节点比较大小,若小于父节点,与父节点交换位置,直到开头位置(根节点,数组第一个元素)。
(注:子节点索引号为x,父节点索引号为(x-1)//2)
void add(Heap *heap, int data) // add a element to the heap
{// 往末尾添加元素heap->p[heap->n] = data;// (向上调整) 与父节点比较,小于父节点的值,与父节点交换位置int cur = heap->n;while(cur > 0){int parent = ceil((cur - 1) / 2);if(heap->p[parent] > data){heap->p[cur] = heap->p[parent];heap->p[parent] = data;cur = parent;}else break;}// 每添加一个元素,元素个数+1heap->n++;
}
删除(根节点):
1、记录根节点(数组第一个元素)和末尾节点(数组最后一个元素)。
2、末尾节点的值换到根节点位置,元素个数-1。
3、(向下调整)从根节点开始,依次与左右子节点比较大小,数值小的是父节点,直到比对到末尾。
(注:父节点索引号为x,左子节点索引号为2x+1,右子节点索引号为2x+2)
int delete(Heap *heap) // delete root from the heap
{if(heap->n == 0) return -1; // 空树heap->n--; // 每删除一次根节点,元素个数-1int root = heap->p[0], enddata = heap->p[heap->n];heap->p[0] = enddata; // 末尾元素换到第一个位置// (向下调整) 与左右子节点比较,若子节点小,与较小子节点交换位置int cur = 0;while(cur <= heap->n){int left = cur * 2 + 1, right = cur * 2 + 2, minchild;if(left > heap->n) break; // 没有左子节点if(right > heap->n) minchild = left; // 没有右子节点,有左子节点else // 左右子节点都有的情况{// 左右子节点比较,找到较小子节点if(heap->p[right] < heap->p[left]) minchild = right;else minchild = left;// 父节点与较小子节点比较,若子节点小,与子节点交换位置if(heap->p[minchild] < enddata){heap->p[cur] = heap->p[minchild];heap->p[minchild] = enddata;cur = minchild;}else break;}}return root;
}
获取根节点:(数组第一个元素)
int getroot(Heap *heap) // get the root from the heap
{if(heap->n == 0){printf("Empty heap\n");exit(-1);}return heap->p[0];
}
遍历堆:(类似广度遍历二叉树)
每个节点比子树的所有节点都小,但左右节点没有顺序要求。
void traverse(Heap *heap) // show element one by one (like breadth traverse the tree)
{if(heap->n == 0) return ;printf("elements(%d): ", heap->n);for(int k = 0; k < heap->n; k++){printf("%d ", heap->p[k]);}printf("\n");
}
清空堆:
释放数组内存空间,指向数组的指针指向NULL,元素个数为0。
void clear(Heap *heap) // clear the heap (free memory space of the array)
{free(heap->p); // 释放数组内存空间heap->p = NULL; // 指针指向NULL,避免野指针heap->n = 0; // 元素个数为0
}
完整代码:(heap.c)
#include <stdio.h>
#include <math.h>
#include <stdlib.h>/* structure */
typedef struct Heap
{int *p; // memory address of the heap (array)int n; // the number of the heap
} Heap;/* function prototype */
void init(Heap *, int); // heap initialization
void add(Heap *, int); // add a element to the heap
int delete(Heap *); // delete root from the heap
int getroot(Heap *); // get the root from the heap
void traverse(Heap *); // show element one by one (likely breadth traverse the tree)
void clear(Heap *); // clear the heap (free memory space of the array)/* main function */
int main(void)
{Heap heap;init(&heap, 8);printf("length: %d\n", heap.n);add(&heap, 5); printf("root(the minimum): %d\n", getroot(&heap));traverse(&heap);add(&heap, 8); add(&heap, 3); add(&heap, 9); add(&heap, 2);printf("root(the minimum): %d\n", getroot(&heap));traverse(&heap);delete(&heap); printf("root(the minimum): %d\n", getroot(&heap));traverse(&heap);delete(&heap); printf("root(the minimum): %d\n", getroot(&heap));traverse(&heap);clear(&heap);printf("length: %d\n", heap.n);printf("root(the minimum): %d\n", getroot(&heap));return 0;
}/* subfunction */
void init(Heap *heap, int length) // heap initialization
{heap->p = (int *)malloc(length * sizeof(int));if(heap->p == NULL){perror("Memory allocation failed");exit(-1);}heap->n = 0;
}void add(Heap *heap, int data) // add a element to the heap
{// add a element to the end of the heapheap->p[heap->n] = data;// (adjust up) compair with parent,if smaller than parent, change the positionint cur = heap->n;while(cur > 0){int parent = ceil((cur - 1) / 2);if(heap->p[parent] > data){heap->p[cur] = heap->p[parent];heap->p[parent] = data;cur = parent;}else break;}heap->n++;
}int delete(Heap *heap) // delete root from the heap
{if(heap->n == 0) return -1;heap->n--;int root = heap->p[0], enddata = heap->p[heap->n];heap->p[0] = enddata; // put the last element to the first position// (adjust down) compair with left and right child, minimun is parentint cur = 0;while(cur <= heap->n){int left = cur * 2 + 1, right = cur * 2 + 2, minchild;if(left > heap->n) break; // no left childif(right > heap->n) minchild = left; // have left child, no right childelse // have left child and right child{// compair left child and right child, find smaller oneif(heap->p[right] < heap->p[left]) minchild = right;else minchild = left;// smaller child compair with parent,if child is the smallest, changeif(heap->p[minchild] < enddata){heap->p[cur] = heap->p[minchild];heap->p[minchild] = enddata;cur = minchild;}else break;}}return root;
}int getroot(Heap *heap) // get the root from the heap
{if(heap->n == 0){printf("Empty heap\n");exit(-1);}return heap->p[0];
}void traverse(Heap *heap) // show element one by one (like breadth traverse the tree)
{if(heap->n == 0) return ;printf("elements(%d): ", heap->n);for(int k = 0; k < heap->n; k++){printf("%d ", heap->p[k]);}printf("\n");
}void clear(Heap *heap) // clear the heap (free memory space of the array)
{free(heap->p);heap->p = NULL;heap->n = 0;
}
编译链接: gcc -o heap heap.c
执行可执行文件: ./heap