堆的相关操作实现-基于C语言
首先,堆是一个完全二叉树,它分为大堆和小堆。
大堆:根节点比左右子树的值都大,整个堆的堆顶元素一定是最大值,比如:
小堆:
根节点比左右子树的值都小,整个堆的堆顶元素一定是最小值,比如:
1. 堆的结构定义
因为堆分为大堆和小堆,所以我们在堆的结构里放一个函数指针,用来控制当前的堆是大堆还是小堆
#define MAXSIZE 1024 typedef char HeapType; typedef int(*Compare) (HeapType a, HeapType b);//定义函数指针,用来控制当前堆是大堆还是小堆 typedef struct Head { HeapType data[MAXSIZE]; size_t size;//标志当前堆 Compare cmp;//控制大小堆 }Heap;
2. 堆的初始化
//1.初始化,决定是大堆还是小堆 int Greater(HeapType a, HeapType b)//大堆控制函数 { return a > b ? 1 : 0; } int Less(HeapType a, HeapType b)//小堆控制函数 { return a < b ? 1 : 0; } void HeapInit(Heap* heap, Compare cmp)//初始化 { if(heap == NULL)//非法输入 return; heap->size = 0;//不用初始化data,因为size控制着data是否有效 heap->cmp = cmp; return; }
3. 堆的销毁
//2.销毁堆 void HeapDestroy(Heap* heap)//销毁 { if(heap == NULL) return; heap->size = 0; heap->cmp = NULL; return; }
4. 堆的打印函数
因为我们拿数组实现的堆,所以我们只要按照下标遍历打印即可。
//3.打印函数 void HeapPrint(Heap heap) { size_t i = 0; for(; i<heap.size; ++i) { printf("[%c|%p] ",heap.data[i]); } printf("\n"); return; }
5.向堆里插入一个元素
这里的插入不能随意指定位置插入,因为要保证插入之后,它依旧满足堆的定义。所以我们直接尾插,然后根据情况调整堆,使其满足堆的定义。具体实现方法如下:
//4.向堆内插入一个元素,要注意插入元素后继续保持堆的定义 void Swap(HeapType* a, HeapType* b)//交换函数 { HeapType tmp = *a; *a = *b; *b = tmp; return; } void AdjustUp(Heap* heap, size_t index)//上浮式调整函数 { if(heap == NULL) return; size_t child = index;//尽量保证每个变量只做一件事 size_t parent = (child-1)/2; //调整的最大限度就是将传入的调整元素调整至整个堆的根结点,即下标为0 while(child > 0) { //不满足堆的定义 if(!heap->cmp(heap->data[parent], heap->data[child])) { Swap(&heap->data[parent], &heap->data[child]); } //若发现某一位置,child与parent已满足堆的定义,就停止调整,因为上面的必定满足堆的定义 else break; child = parent; parent = (parent-1)/2; } return; } void HeapInsert(Heap* heap, HeapType to_insert)//给定一个值,插入堆中 {//这里不能指定位置插入,因为要保证插入后它依旧是一个堆 if(heap == NULL)//非法输入 return; if(heap->size >= MAXSIZE)//堆已满 return; //插入堆中 heap->data[heap->size++] = to_insert; //对该堆进行上浮式的调整,调整的起始位置为size-1 AdjustUp(heap, heap->size-1); return; }
6. 取堆顶元素
下标元素为0的元素即堆顶元素。
//5.取堆顶元素 int HeapRoot(Heap heap, HeapType* root)//取到返回1,且元素在root中,取不到返回0 { if(heap.size == 0)//空堆 return 0; *root = heap.data[0];//下标为0的元素即堆顶元素 return 1; }
7. 删除堆顶元素
这里依旧要注意删除之后,依旧满足堆的定义,所以要根据情况进行下浮式调整
//6.删除堆顶元素,要注意保证删除后依旧满足堆的定义 void AdjustDown(Heap* heap, size_t index)//下沉式调整堆 { if(heap == NULL) return; size_t parent = index; size_t child = parent*2 + 1; while(child < heap->size) { //若右子树存在,且比左子树更符合堆的要求 //则让child指向右子树 if((child+1 < heap->size) && (heap->cmp(heap->data[child+1], heap->data[child]))) { child = child + 1; } //不满足堆的定义时,继续下沉 if(heap->cmp(heap->data[child], heap->data[parent])) { Swap(&heap->data[child], & heap->data[parent]); } //在某点时已满足堆的定义了,即可停止调整 else break; parent = child; child = child*2 + 1; } return; } void HeapEarseRoot(Heap* heap)//删除堆顶元素 { if(heap == NULL)//非法输入 return; if(heap->size == 0)//空堆 return; //将堆顶元素与最后一个元素交换 Swap(&heap->data[0], &heap->data[heap->size-1]); //再让size--即实现对堆顶元素的删除 --heap->size; //再下浮式调整堆,使其依旧满足堆的定义,调整起始位置为0 AdjustDown(heap,0); }
8. 给定一个数组,依照数组内容创建堆
直接遍历数组,调用之前写的插入函数即可
//7.给定一个数组内容,依此创建堆 void HeapCreate(Heap* heap, HeapType arr[], size_t size)//给定数组,依据内容创建堆 { if(heap == NULL) return; //遍历数组,调用之前的插入函数即可 size_t i = 0; for(; i<size; ++i) { HeapInsert(heap, arr[i]); } return; }
9. 给堆排序
我们依照所给数组,先创建堆。然后一直删除堆顶元素,因为堆顶元素一定是当前堆中最大或最小的,直至删到堆为空。此时数据一定是有序的,我们再拷贝回数组。
//8.对堆进行排序 void HeapSort(HeapType arr[], size_t size) { //将数组构成堆 Heap heap; HeapInit(&heap, Greater); HeapCreate(&heap, arr, size); ////这里打印一下建立好的堆 //HeapPrint(heap); //循环对堆进行删除堆顶元素操作,直到删完,排序就完成 //虽然删完之后堆为空,但是数据还在,只是置为无效了而已 while(heap.size > 0) { HeapEarseRoot(&heap); } //再将排序好的数据拷贝回数组 memcpy(arr, heap.data, size*sizeof(HeapType)); return; }
10. 以下为以上函数的测试代码
void TestInit() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater);//这里我们实现大堆 printf("size: expected is 0,actual is %d\n", heap.size); printf("cmp is %p\n",heap.cmp); } void TestDestroy() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); printf("size: expected is 0,actual is %d\n", heap.size); printf("cmp is %p\n",heap.cmp); HeapDestroy(&heap); printf("size: expected is 0,actual is %d\n", heap.size); printf("cmp: expected is nil,actual is %p\n",heap.cmp); } void TestInsert() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); HeapInsert(&heap, 's'); HeapInsert(&heap, 'v'); HeapInsert(&heap, 'b'); HeapInsert(&heap, 'n'); HeapInsert(&heap, 'a'); HeapInsert(&heap, 'l'); HeapPrint(heap); } void TestRoot() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); HeapInsert(&heap, 's'); HeapInsert(&heap, 'v'); HeapInsert(&heap, 'b'); HeapInsert(&heap, 'n'); HeapInsert(&heap, 'a'); HeapInsert(&heap, 'l'); HeapPrint(heap); HeapType root; int ret = HeapRoot(heap, &root); printf("expected is v, actual is %c\n", root); } void TestEarse() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); HeapInsert(&heap, 's'); HeapInsert(&heap, 'v'); HeapInsert(&heap, 'b'); HeapInsert(&heap, 'n'); HeapInsert(&heap, 'a'); HeapInsert(&heap, 'l'); HeapPrint(heap); HeapEarseRoot(&heap); HeapPrint(heap); } void TestCreate() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); HeapType arr[] = "augondlr"; printf("%s\n", arr); size_t size = sizeof(arr) - 1; HeapCreate(&heap, arr, size); HeapPrint(heap); } void TestSort() { SHOW_NAME; Heap heap; HeapInit(&heap,Greater); HeapType arr[] = "augondlr"; printf("%s\n", arr); size_t size = sizeof(arr) - 1; HeapSort(arr, size); printf("%s\n", arr); }