目录
前言
一、堆的实现
1.1 头文件
1.2 堆的初始化及销毁
1.3 堆的插入
1.4 堆的删除
1.5 取堆顶数据和判空
前言
前面我们讲了二叉树有顺序结构和链式结构,今天就来讲一下顺序结构
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
一、堆的实现
堆的性质: 堆中某个结点的值总是不大于或不小于其父结点的值,堆总是一棵完全二叉树。
1.1 头文件
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}Heap;//堆的初始化
void HeapInit(Heap*hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);
写入我们需要用到的头文件以及堆的基础结构还有用到的堆的功能函数名。
1.2 堆的初始化及销毁
void HeapInit(Heap* hp)
{assert(hp);hp->a = NULL;hp->size = hp->capacity = 0;
}
void HeapDestory(Heap* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->size = hp->capacity = 0;
}
这个没什么还说的,就是把数据都置空了。
1.3 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* tem = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);hp->capacity = newcapacity;hp->a = tem;}hp->a[hp->size] = x;hp->size++;adjustup(hp->a, hp->size-1);
}
这里我们会用到向上调整算法
void swap(HPDataType* a, HPDataType* b)
{HPDataType tem = *a;*a = *b;*b = tem;
}
void adjustup(HPDataType* a, HPDataType child)
{int parent = (child - 1) / 2;while (child > 0){if (a[parent] > a[child]){swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
插入数据后要保持父节点比子节点小的性质,要把数据一步步向上调整。
1.4 堆的删除
void HeapPop(Heap* hp)
{assert(hp);swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;adjustdown(hp->a, hp->size,0);
}
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
void adjustdown(HPDataType* a, HPDataType n,HPDataType parent)
{HPDataType child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){child++;}if (a[child] < a[parent]){swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
这里给个例子:
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向下调整算法可以把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
int array[] = {27,15,19,18,28,34,65,49,25,37};
1.5 取堆顶数据和判空
HPDataType HeapTop(Heap* hp)
{return hp->a[0];
}
因为是基于数组写的,所以取堆顶就很简单了。
bool HeapEmpty(Heap* hp)
{return hp->size == 0;
}
如果数据位空了,那么堆就是空的。
二、完整源码
dui.h
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}Heap;void HeapInit(Heap*hp);void HeapDestory(Heap* hp);void HeapPush(Heap* hp, HPDataType x);void HeapPop(Heap* hp);HPDataType HeapTop(Heap* hp);int HeapSize(Heap* hp);bool HeapEmpty(Heap* hp);
dui.c
#include"dui.h"
void HeapInit(Heap* hp)
{assert(hp);hp->a = NULL;hp->size = hp->capacity = 0;
}
void HeapDestory(Heap* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->size = hp->capacity = 0;
}
void swap(HPDataType* a, HPDataType* b)
{HPDataType tem = *a;*a = *b;*b = tem;
}
void adjustup(HPDataType* a, HPDataType child)
{int parent = (child - 1) / 2;while (child > 0){if (a[parent] > a[child]){swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* tem = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);hp->capacity = newcapacity;hp->a = tem;}hp->a[hp->size] = x;hp->size++;adjustup(hp->a, hp->size-1);
}
void adjustdown(HPDataType* a, HPDataType n,HPDataType parent)
{HPDataType child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){child++;}if (a[child] < a[parent]){swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapPop(Heap* hp)
{assert(hp);swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;adjustdown(hp->a, hp->size,0);
}
HPDataType HeapTop(Heap* hp)
{return hp->a[0];
}
int HeapSize(Heap* hp)
{return hp->size;
}
bool HeapEmpty(Heap* hp)
{return hp->size == 0;
}
还是要自己动手写一边才能有印象,写完每个功能点最好一个一个的去测试,这样就不会那么麻烦,更容易知道错哪了,全部写写完再测试,没错误还好说,有错的话,找起来会很痛苦的。
本篇内容就到这里了,希望对各位有帮助,如果有错误欢迎指出。