二叉树(堆)
二叉树(堆)
简介
前文,我们大概的介绍了一下树的一些基本概念,然后这节就主要讲解一下二叉树的其中一种存储方式:用数组存储(顺序存储)。及其一些特殊结构,也就是堆。
注意一下就是,这种用数组存储二叉树的方式只适用于完全二叉树。非完全二叉树的话就更加适合链式存储,这个后面讲。
堆
堆是什么呢?
堆的插入
讲完基本的思路,那么就要开始写代码了。
堆的实现及其功能代码
头文件.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
//数据类型
typedef int HPDataType;
typedef struct Heap
{
HPDataType *a;//数组首元素地址
int size;//数组中有效元素的个数(数组大小)
int capacity;//数组的容量
}HP;
//向上调整
void AdjustUp(HPDataType* a,int child);
//向下调整
void AdjustDown(HPDataType* a,int n,int parent);
//交换函数
void Swap(HPDataType* p1,HPDataType* p2);
//打印函数
void HeapPrint(HP* pq);
//初始化函数
void HeapInit(HP* pq);
//第二种初始化函数
void HeapInitArray(HP* pq,int* a,int n);
//摧毁函数
void HeapDestroy(HP* pq);
//插入函数
void HeapPush(HP* pq,HPDataType x);
//删除函数
void HeapPop(HP* pq);
//获取堆顶元素
HPDataType HeapTop(HP* pq);
//判断堆是否为空
bool HeapEmpty(HP* pq);
实现文件.c
#include "Heap.h"
//初始化函数
void HeapInit(HP* pq)
{
assert(pq);
pq->a=NULL;
pq->size=pq->capacity=0;
}
//第二种初始化函数(把已经存在的数组,复制一份给我们的数组)
void HeapInitArray(HP* pq,int* a,int n)
{
assert(pq);
assert(a);
//这里直接开了一个大小为n的数组空间
pq->a=(HPDataType*) malloc(sizeof (HPDataType)*n);
if(pq->a==NULL)
{
perror("malloc failed");
exit(-1);
}
pq->capacity=n;
memcpy(pq->a,a,sizeof (HPDataType)*n);
pq->size=n;
//建堆
for(int i=1;i<n;i++)
{
AdjustUp(pq->a,i);
}
}
//摧毁函数
void HeapDestroy(HP* pq)
{
assert(pq);
free(pq->a);
pq->a=NULL;
pq->size=pq->capacity=0;
}
//交换函数
void Swap(HPDataType* p1,HPDataType* p2)
{
HPDataType tmp=*p1;
*p1=*p2;
*p2=tmp;
}
//向上调整(建小堆)
void AdjustUp(HPDataType* a,int child)
{
int parent=(child-1)/2;
while(child>0)
{
if(a[child]<a[parent])
{
Swap(&a[child],&a[parent]);
child=parent;
parent=(child-1)/2;//parent=(parent-1)/2;
}
else
{
break;
}
}
}
//向下调整(小堆)
void AdjustDown(HPDataType* a,int n,int parent)
{
int 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 HeapPush(HP* pq,HPDataType x)
{
assert(pq);
//判断容量是否足够
if(pq->size==pq->capacity)
{
int newCapacity=pq->capacity==0?4:pq->capacity*2;
HPDataType* tmp=(HPDataType*) realloc(pq->a,sizeof (HPDataType)*newCapacity);
if(tmp==NULL)
{
perror("reallocate failed");
exit(-1);
}
pq->a=tmp;
pq->capacity=newCapacity;
}
pq->a[pq->size]=x;
pq->size++;
AdjustUp(pq->a,pq->size-1);
}
//删除函数
void HeapPop(HP* pq)
{
assert(pq);
assert(pq->size>0);
Swap(&pq->a[0],&pq->a[pq->size-1]);
pq->size--;
AdjustDown(pq->a,pq->size,0);
}
//获取堆顶元素
HPDataType HeapTop(HP* pq)
{
assert(pq);
assert(pq->size>0);
return pq->a[0];
}
//判断堆是否为空
bool HeapEmpty(HP* pq)
{
assert(pq);
return pq->size==0;
}
//打印函数
void HeapPrint(HP* pq)
{
assert(pq);
for(int i=0;i<pq->size;i++)
{
printf("%d ",pq->a[i]);
}
printf("\n");
}
测试文件.c
#include "Heap.h"
/*
void HeapSort(int* a,int n)
{
for(int i=(n-1-1)/2;i>=0;i--)
{
AdjustDown(a,n,i);
}
int end=n-1;
while(end>0)
{
Swap(&a[0],&a[end]);
AdjustDown(a,end,0);
end--;
}
}
int main()
{
int a[]={65,100,70,32,50,60};
HeapSort(a,sizeof (a)/sizeof (int));
return 0;
}*/
void PrintTopK(const char* filename, int k)
{
// 1. 建堆--用a中前k个元素建堆
FILE* fout = fopen(filename, "r");
if (fout == NULL)
{
perror("fopen fail");
return;
}
int* minheap = (int*)malloc(sizeof(int) * k);
if (minheap == NULL)
{
perror("malloc fail");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &minheap[i]);
}
// 前k个数建小堆
for (int i = (k-2)/2; i >=0 ; --i)
{
AdjustDown(minheap, k, i);
}
// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
int x = 0;
while (fscanf(fout, "%d", &x) != EOF)
{
if (x > minheap[0])
{
// 替换你进堆
minheap[0] = x;
AdjustDown(minheap, k, 0);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", minheap[i]);
}
printf("\n");
free(minheap);
fclose(fout);
}
// fprintf fscanf
void CreateNDate()
{
// 造数据
int n = 1000;
srand(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();//要先使用这个函数创建一个含有N个数据的文本文件,然后注释掉,使用下面那个函数
PrintTopK("data.txt", 5);
return 0;
}
解析
-
结构
-
初始化函数和销毁函数
-
插入函数
- 向上调整函数
结合下图理解代码
- 删除函数
- 向下调整函数
建堆
建堆什么意思呢?顾名思义,就是建立起一个堆,可以是大堆,可以是小堆,这里我们以建立小堆给大家讲解。然后我们顺便用上删除操作,这样就可以得到一个简单的排序的手法。
优化