当前位置: 首页 > news >正文

C学习(数据结构)--> 实现顺序结构二叉树

目录

一、堆的概念与结构

 性质

二叉树的性质

二、堆的实现

1、结构

2、初始化与销毁

3、入堆与出堆(小堆)

1)Swap

2)入堆

1 数据的向上调整

2 入堆

3)出堆

1 数据的向下调整

 2 出堆

三、其他

1、入堆与出堆(大堆)

2、堆排序

1)排降序

 2)排升序

3、Top-K问题

四、总代码

Heap.h

Heap.cpp

main.cpp


一般堆使用顺序结构的数组来存储数据,堆是一种特殊的二叉树。具有二叉树的特性的同时,还具有其它的特性。

一、堆的概念与结构

如果有一个集合,把它所有的元素按完全二叉树的顺序存储方式存储,在一维数组中,并且每一个父节点小于它的孩子结点,则称这个完全二叉树为小堆,而称每一个父节点大于它的孩子结点的完全二叉树为大堆。

  • 小堆

  • 大堆 

 性质

根据以上可得,堆总是一棵完全二叉树,且堆中某个结点的值总是不大于或不小于父结点的值。

二叉树的性质

对于一个完全二叉树,按照从上至下从左至右的数组顺序对所有结点从0开始编号,那么对于序号为 i 的结点(i - 1)/ 2 对应的结点是它的父结点,2*i + 1 对应的结点是它的左孩子结点,2*i + 2 对应的结点是它的父结点

二、堆的实现

1、结构

typedef int HPDataType;
typedef struct Heap
{HPDataType* arr;int size;//有效数据个数int capacity;//空间大小
}Heap;

2、初始化与销毁

//堆的初始化
void HPInit(Heap* php)
{assert(php);php->arr = NULL;php->capacity = php->size = NULL;
}//堆的销毁
void HPDestroy(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->capacity = php->size = NULL;
}

3、入堆与出堆(小堆)

1)Swap

通过Swap函数交换两个数据

//两个数据的交换
void Swap(HPDataType* x, HPDataType* y)
{assert(x || y);HPDataType cpy = *x;*x = *y;*y = cpy;
}

2)入堆

1 数据的向上调整

//数据的向上调整(小堆)
void AdjustUpSmall(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;//求该子节点的父结点while (child > 0){if (arr[child] < arr[parent])//当孩子结点小于父节点时,两个数据交换{Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
2 入堆
//入堆(小堆)
void HPPushSmall(Heap* php, HPDataType x)
{assert(php);//判断空间是否足够if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fali!!!");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//因为数据还没有向上调整,所以size不能++//向上调整AdjustUpSmall(php->arr, php->size++);
}

3)出堆

1 数据的向下调整

//数据的向下调整(小堆)
void AdjustDownSmall(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;//求该父节点的左孩子while (child < n)//该左孩子在数组内{if (child + 1 < n && arr[child] > arr[child + 1])//比较左右孩子大小{child++;}//父节点和孩子结点交换if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
 2 出堆
//出堆(小堆)
void HPPopSmall(Heap* php)
{assert(php && php->size);//有效数据不能为零//头尾数据交换Swap(&php->arr[0], &php->arr[php->size - 1]);//出堆php->size--;//数据的向下调整AdjustDownSmall(php->arr, 0, php->size);
}

三、其他

1、入堆与出堆(大堆)

与小堆大同小异

//数据的向上调整(大堆)
void AdjustUpBig(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;//求该子节点的父结点while (child > 0){if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//入堆(大堆)
void HPPushBig(Heap* php, HPDataType x)
{assert(php);//判断空间是否足够if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fali!!!");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//因为数据还没有向上调整,所以size不能++//向上调整AdjustUpBig(php->arr, php->size++);
}//数据的向下调整(大堆)
void AdjustDownBig(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;//求该父节点的左孩子while (child < n)//该左孩子在数组内{if (child + 1 < n && arr[child] < arr[child + 1])//比较左右孩子大小{child++;}//父节点和孩子结点交换if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//出堆(大堆)
void HPPopBig(Heap* php)
{assert(php && php->size);//有效数据不能为零//头尾数据交换Swap(&php->arr[0], &php->arr[php->size - 1]);//出堆php->size--;//数据的向下调整AdjustDownBig(php->arr, 0, php->size);
}

2、堆排序

1)排降序

 以排降序为例,通过建小堆来实现堆排序

void HeapSortSmall(int* arr, int n)
{建小堆,向上排序法建堆//for (int i = 0; i < n; i++)//{//	//向上建堆//	AdjustUpSmall(arr, i);//}//向下排序,建小堆for (int i = (n - 1 - 1) / 2; i >= 0; i--)//(n - 1 - 1) / 2 求的是最底下子结点的父结点{AdjustDownSmall(arr, i, n);}//排降序int end = n - 1;while (end > 0)//出堆{Swap(&arr[0], &arr[end]);AdjustDownSmall(arr, 0, end);end--;}
}

 2)排升序

同理,通过建立大堆排升序

void HeapSortBig(int* arr, int n)
{//向下排序,建大堆for (int i = (n - 1 - 1) / 2; i >= 0; i--)//(n - 1 - 1) / 2 求的是最底下子结点的父结点{AdjustDownBig(arr, i, n);}//排升序int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDownBig(arr, 0, end);end--;}
}

3、Top-K问题

  • 问:有一个数据量特别大的集合,如何用一个最多包含 K 个元素的集合来求得最大或最小的前 K 个元素
  • 答:用前 K 个元素建大堆或小堆,在用剩余的N-K个元素依此与堆顶元素比较大小,如过该元素比堆顶元素大于或小于,则替换掉堆顶元素。
//造数据
void CreateNDate()
{int n = 100000;srand((unsigned int)time(NULL));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen fail!!!");return;}for (int i = 0; i < n; i++){int x = (rand() + i) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}//Top-K 问题
void TopK()
{int k = 10;const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail!!!");exit(1);}int minHeap[10] = { 0 };//从文件中读前K个数据for (int i = 0; i < k; i++){fscanf(fout, "&d\n", minHeap);}//建小堆for (int i = (k - 1 - 1) / 2; i > 0; i--){AdjustDownSmall(minHeap, 0, k);}//继续取文件的数据,将读取到的数据和堆顶进行比较int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minHeap[0]){minHeap[0] = x;AdjustDownSmall(minHeap, 0, k);}}//排升序printf("排升序:");HeapSortBig(minHeap, 10);for (int i = 0; i < 10; i++){printf("%d ", minHeap[i]);}printf("\n");fclose(fout);
}

四、总代码

Heap.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<time.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* arr;int size;//有效数据个数int capacity;//空间大小
}Heap;
//堆的初始化
void HPInit(Heap* php);
//堆的销毁
void HPDestroy(Heap* php);
//两个数据的交换
void Swap(HPDataType* x, HPDataType* y);
//数据的向上调整(小堆)
void AdjustUpSmall(HPDataType* arr, int child);
//入堆(小堆)
void HPPushSmall(Heap* php, HPDataType x);
//数据的向下调整(小堆)
void AdjustDownSmall(HPDataType* arr, int parent, int n);
//出堆(小堆)
void HPPopSmall(Heap* php);
//判空
bool HPEmpty(Heap* php);
//取堆顶数据
HPDataType HPTop(Heap* php);
//数据的向上调整(大堆)
void AdjustUpBig(HPDataType* arr, int child);
//入堆(大堆)
void HPPushBig(Heap* php, HPDataType x);
//数据的向下调整(大堆)
void AdjustDownBig(HPDataType* arr, int parent, int n);
//出堆(大堆)
void HPPopBig(Heap* php);
//堆排序
//排升序,建大堆
//排降序,建小堆
void HeapSortSmall(int* arr, int n);
void HeapSortBig(int* arr, int n);

Heap.cpp

#include"Heap.h"//堆的初始化
void HPInit(Heap* php)
{assert(php);php->arr = NULL;php->capacity = php->size = NULL;
}//堆的销毁
void HPDestroy(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->capacity = php->size = NULL;
}//两个数据的交换
void Swap(HPDataType* x, HPDataType* y)
{assert(x || y);HPDataType cpy = *x;*x = *y;*y = cpy;
}//数据的向上调整(小堆)
void AdjustUpSmall(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;//求该子节点的父结点while (child > 0){if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//入堆(小堆)
void HPPushSmall(Heap* php, HPDataType x)
{assert(php);//判断空间是否足够if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fali!!!");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//因为数据还没有向上调整,所以size不能++//向上调整AdjustUpSmall(php->arr, php->size++);
}//数据的向下调整(小堆)
void AdjustDownSmall(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;//求该父节点的左孩子while (child < n)//该左孩子在数组内{if (child + 1 < n && arr[child] > arr[child + 1])//比较左右孩子大小{child++;}//父节点和孩子结点交换if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//出堆(小堆)
void HPPopSmall(Heap* php)
{assert(php && php->size);//有效数据不能为零//头尾数据交换Swap(&php->arr[0], &php->arr[php->size - 1]);//出堆php->size--;//数据的向下调整AdjustDownSmall(php->arr, 0, php->size);
}//判空
bool HPEmpty(Heap* php)
{assert(php);return php->size == 0;
}//取堆顶数据
HPDataType HPTop(Heap* php)
{assert(php && php->size);return php->arr[0];
}//数据的向上调整(大堆)
void AdjustUpBig(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;//求该子节点的父结点while (child > 0){if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//入堆(大堆)
void HPPushBig(Heap* php, HPDataType x)
{assert(php);//判断空间是否足够if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fali!!!");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//因为数据还没有向上调整,所以size不能++//向上调整AdjustUpBig(php->arr, php->size++);
}//数据的向下调整(大堆)
void AdjustDownBig(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;//求该父节点的左孩子while (child < n)//该左孩子在数组内{if (child + 1 < n && arr[child] < arr[child + 1])//比较左右孩子大小{child++;}//父节点和孩子结点交换if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//出堆(大堆)
void HPPopBig(Heap* php)
{assert(php && php->size);//有效数据不能为零//头尾数据交换Swap(&php->arr[0], &php->arr[php->size - 1]);//出堆php->size--;//数据的向下调整AdjustDownBig(php->arr, 0, php->size);
}//堆排序
//排升序,建大堆
//排降序,建小堆void HeapSortSmall(int* arr, int n)
{建小堆,向上排序法建堆//for (int i = 0; i < n; i++)//{//	//向上建堆//	AdjustUpSmall(arr, i);//}//向下排序for (int i = (n - 1 - 1) / 2; i >= 0; i--)//(n - 1 - 1) / 2 求的是最底下子结点的父结点{AdjustDownSmall(arr, i, n);}//排降序int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDownSmall(arr, 0, end);end--;}
}
void HeapSortBig(int* arr, int n)
{//向下排序for (int i = (n - 1 - 1) / 2; i >= 0; i--)//(n - 1 - 1) / 2 求的是最底下子结点的父结点{AdjustDownBig(arr, i, n);}//排升序int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDownBig(arr, 0, end);end--;}
}

main.cpp

#include"Heap.h"//造数据
void CreateNDate()
{int n = 100000;srand((unsigned int)time(NULL));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen fail!!!");return;}for (int i = 0; i < n; i++){int x = (rand() + i) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}//Top-K 问题
void TopK()
{int k = 10;const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail!!!");exit(1);}int minHeap[10] = { 0 };//从文件中读前K个数据for (int i = 0; i < k; i++){fscanf(fout, "&d\n", minHeap);}//建小堆for (int i = (k - 1 - 1) / 2; i > 0; i--){AdjustDownSmall(minHeap, 0, k);}//继续取文件的数据,将读取到的数据和堆顶进行比较int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minHeap[0]){minHeap[0] = x;AdjustDownSmall(minHeap, 0, k);}}//排升序printf("排升序:");HeapSortBig(minHeap, 10);for (int i = 0; i < 10; i++){printf("%d ", minHeap[i]);}printf("\n");fclose(fout);
}void test01()
{Heap hpsmall;//堆的初始化HPInit(&hpsmall);//入堆(小堆)HPDataType arr[10] = { 32,11,55,223,86,123,867,3423,75,13 };for (int i = 0; i < 10; i++){HPPushSmall(&hpsmall, arr[i]);}//取堆顶数据printf("Heap: ");while (!HPEmpty(&hpsmall)){printf("%d ", HPTop(&hpsmall));//出堆(小堆)HPPopSmall(&hpsmall);}printf("\n");//堆的销毁HPDestroy(&hpsmall);//排降序printf("排降序:");HeapSortSmall(arr, 10);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}printf("\n");//排升序printf("排升序:");HeapSortBig(arr, 10);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}printf("\n");//CreateNDate();TopK();
}int main()
{test01();return 0;
}


http://www.mrgr.cn/news/5798.html

相关文章:

  • 在亚马逊云科技上提取视频内容并利用AI大模型开发视频内容问答服务
  • 海山数据库(He3DB)源码详解:CommitTransaction函数源码详解
  • Shell编程之条件语句
  • 开发者空间实践指导:基于华为云3大PaaS主流服务轻松实现文字转换语音
  • 【C++】STL简介
  • 社区流浪动物救助系统-计算机毕设Java|springboot实战项目
  • 探索TensorFlow:深度学习的未来
  • 手把手教你手写单例,六种实现方式一网打尽!
  • fpga图像处理实战-对数变换
  • Mybatis的分页,延迟加载和缓存
  • docker具体操作
  • Qt实现圆型控件的三种方法之子类化控件并重写paintEvent
  • webpack打包html
  • 使用docker-compose运行kafka及验证(无需zookpeer)
  • 【ORACLE】minus() 函数
  • vue3 侧边栏实现
  • 探索STM32平台中MK米客方德SD NAND的高效数据存储解决方案
  • 【每日刷题】Day105
  • 数据库系统 第22节 事务隔离级别案例分析
  • Parallels Desktop 19 for Mac破解版 附带parallels desktop 2024最新激活密钥