文章目录
前言
📚一、二叉树链式结构的接口补充
📔1.1 二叉树第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);//检测k是否小于0
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1)
+ BinaryTreeLevelKSize(root->right, k - 1);
}
📔1.2 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->val == x)
{
return root;
}
BTNode* ret = NULL;
ret = BinaryTreeFind(root->left, x);
if (ret)
{
return ret;
}
ret = BinaryTreeFind(root->right, x);
if (ret)
{
return ret;
}
return NULL;
}
📔1.3 判断一颗二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
//创建及初始化队列
Que q;
QueueInit(&q);
//把根不等于空(NULL)时入队列
if (root)
{
QueuePush(&q, root);
}
//思路:上一层出带下一层进
while (!QueueEmpty(&q))
{
BTNode* Front = QueueFront(&q);
//当节点等于空时,break跳出循环
if (Front == NULL)
{
break;
}
//NULL也入队列
QueuePush(&q, Front->left);
QueuePush(&q, Front->right);
QueuePop(&q);
}
//继续出队列,此时如果遇到不等于空(NULL)的节点
//那么这颗树就不是完全二叉树
while (!QueueEmpty(&q))
{
BTNode* Front = QueueFront(&q);
QueuePop(&q);
if (Front != NULL)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
//到这里时,已经遍历完整棵树了,此时这棵树就是完全二叉树
return true;
}
📚二、二叉树的顺序结构
📔2.1 二叉树顺序结构的概念
- 堆的结构
typedef int HPDataType;
typedef struct Heap
{
HPDataType* arr;
int size;
int capacity;
}HP;
📔2.2 堆实现
📕2.2.1 堆的初始化
- 第一种结构:
void HeapInit(HP* php)
{
assert(php);
php->arr = NULL;
php->capacity = 0;
php->size = 0;
}
- 第二种结构:
void HeapInitArray(HP* php, HPDataType* arr, int n)
{
assert(php);
assert(arr);
//开辟n个空间
php->arr = (HPDataType*)malloc(sizeof(HPDataType)*n);
if (php->arr == NULL)
{
perror("malloc fail");
exit(-1);
}
php->capacity = n;
php->size = n;
//把原数组的数据拷贝到在堆上开辟的数组里
memcpy(php->arr, arr, sizeof(HPDataType) * n);
//向上调整建堆
int i = 0;
for (i = 1; i < n; i++)
{
AdjustUp(php->arr, i);
}
}
📕2.2.2 堆的销毁
void HeapDestroy(HP* php)
{
assert(php);
free(php->arr);
php->arr = NULL;
php->capacity = 0;
php->size = 0;
}
📕2.2.3 堆的插入
//容量满了,扩容
if (php->size == php->capacity)
{
int newCapacity = php->capacity == 0 ?
INIT_SIZE : php->capacity * TIMES;
HPDataType* tmp=(HPDataType*)realloc(php->arr,
sizeof(HPDataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
php->arr = tmp;
php->capacity = newCapacity;
}
php->arr[php->size] = x;
php->size++;
AdjustUp(php->arr, php->size-1);
📃2.2.3.1 向上调整算法
void AdjustUp(int* arr, int child)
{
int parent = (child - 1) / 2;//计算父亲的位置
//child等于0时,为循环结束的条件
while (child > 0)
{
if (arr[child] < arr[parent])
{
Swap(&arr[child], &arr[parent]);//交换函数
child = parent;
parent = (child - 1) / 2;
}
else
{
//孩子大于父亲时跳出循环
break;
}
}
}
int main()
{
int arr[] = { 65,100,70,32,50,60 };
HP hp;
HeapInit(&hp);
int i = 0;
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
HeapPush(&hp, arr[i]);
}
HeapPrint(&hp);
HeapDestroy(&hp);
return 0;
}
📕2.2.4 堆的删除
void HeapPop(HP* php)
{
assert(php);
assert(php->size > 0);
//交换首尾的数据
Swap(&php->arr[0], &php->arr[php->size - 1]);
php->size--;
//然后向下调整
AdjustDown(php->arr, php->size, 0);
}
📃2.2.4.1 向下调整算法
void AdjustDown(int* arr, int n, int parent)
{
//默认选择左孩子
int child = parent * 2 + 1;
while (child < n)
{
//左孩子表示最小的
//那么改为右孩子
if (child+1 < n && arr[child + 1] < arr[child])
{
++child;
}
if (arr[child] < arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
//孩子大于父亲,就跳出循环
break;
}
}
}
📕2.2.5 获取堆顶元素
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->arr[0];
}
📕2.2.6 检测堆是否为空
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}
📔2.3 堆排序
void HeapSort(int* arr, int n)
{
//向上调整建堆O(N*logN)
int i = 0;
for (i = 1; i < n; i++)
{
AdjustUp(arr, i);
}
int end = n-1;
while (end > 0)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
end--;
}
}
void HeapSort(int* arr, int n)
{
//向下调整建堆O(N)
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arr, n, i);
}
int end = n-1;
while (end > 0)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
end--;
}
}
📔2.4 TOPK问题
void CreateNDate()
{
// 造数据
int n = 10000;
srand((unsigned int)time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{
perror("fopen error");
return;
}
for (int i = 0; i < n; ++i)
{
int x = (rand() + i) % 10000000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
int main()
{
//CreateNDate();
//传入文件名和要k的数值
PrintTopK("data.txt", 10);
return 0;
}
void PrintTopK(const char* filename, int k)
{
FILE* pf = fopen(filename, "r");
if (pf == NULL)
{
perror("fopen fail");
exit(-1);
}
int* heap = (int*)malloc(sizeof(int) * k);
if (heap == NULL)
{
perror("malloc fail");
exit(-1);
}
// 1、取出前k个数据建堆
int i = 0;
for (i = 0; i < k; i++)
{
fscanf(pf, "%d", &heap[i]);
}
//2.、前k个数向下调整,建堆
//k-1找到最后一个元素的下标
//(k-1-1)/2找到最后一个节点的父亲节点
for (i=(k-1-1)/2; i>=0; i--)
{
AdjustDown(heap, k, i);
}
fclose(pf);
free(heap);
pf = NULL;
heap = NULL;
}
void PrintTopK(const char* filename, int k)
{
FILE* pf = fopen(filename, "r");
if (pf == NULL)
{
perror("fopen fail");
exit(-1);
}
int* heap = (int*)malloc(sizeof(int) * k);
if (heap == NULL)
{
perror("malloc fail");
exit(-1);
}
// 1、取出前k个数据建堆
int i = 0;
for (i = 0; i < k; i++)
{
fscanf(pf, "%d", &heap[i]);
}
//2.、前k个数向下调整,建堆
for (i=(k-1-1)/2; i>=0; i--)
{
AdjustDown(heap, k, i);
}
// 读取剩下的数据依次跟堆顶数据比较,
//大于堆顶就替换进堆,然后再向下调整
int x = 0;
while (fscanf(pf, "%d", &x) != EOF)
{
//大于堆顶就替换它进堆
if (x > heap[0])
{
heap[0] = x;
//替换后,再向下调整
AdjustDown(heap, k, 0);
}
}
for (i = 0; i < k; i++)
{
printf("%d ", heap[i]);
}
printf("\n");
fclose(pf);
free(heap);
pf = NULL;
heap = NULL;
}
- 测试
- 测试结果:
📔2.5本篇章的代码
堆的实现代码