【数据结构】(堆)Top-k|堆排序

目录

概念:

堆的实现 

构建

初始化

销毁

插入元素

往上调整 

删除堆顶元素 

往下调整 

返回堆顶元素 

 返回有效个数

是否为空

 堆排序

 Top-k问题

​编辑 创建数据

堆top-k


概念:

堆是将数据按照完全二叉树存储方式存储到一维数组中;

堆分为大堆和小堆:

大堆:父结点大于等于孩子结点;

小堆:父结点小于等于孩子结点;

父结点与(左右)孩子结点关系:

1.父结点 = (孩子结点-1)/2;

2.左结点= (父结点*2)+1;

        右结点= (父结点*2)+2;

堆的实现 

堆的逻辑结构是完全二叉树,物理结构是一维数组存储;

而独特的结点关系,堆排序也是一种选择排序,

构建

typedef int HPDataType;
typedef struct Heap
{HPDataType* parr;int size;		//存储的有效数据个数int capacity;	//容量
}Heap;
//	用数组存储    

初始化

//堆的初始化
void HeapInit(Heap* php)
{assert(php);php->parr = NULL;php->size = 0;php->capacity = 0;
}

销毁


//堆的销毁
void HeapDestroy(Heap* php)
{assert(php);free(php->parr);php->parr = NULL;php->size = php->capacity = 0;free(php);php = NULL;
}

插入元素

因为堆分为两类,在数据插入时,需要选择适应的调整;

以小堆来说:当插入一个新元素时,插入到堆尾,与父结点比较,相应的往上调整

//堆的插入元素
void HeapPush(Heap* php, HPDataType x)
{assert(php);//检查容量if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* newparr = (HPDataType*)realloc(php->parr, sizeof(HPDataType) * newcapacity);if (newparr == NULL){perror("realloc fail");exit(-1);}php->capacity = newcapacity;php->parr = newparr;}php->parr[php->size] = x;php->size++;//小堆//向上调整AdjustUp(php->parr, php->size - 1);
}

往上调整 

当插入一个新元素,按照孩子和父结点之间的关系进行比较,交换两结点数据,直到满足堆的性质 

//向上调整
void AdjustUp(HPDataType* parr,int size)
{int child = size;int parent = (child - 1) / 2;//小堆=> 父结点<=孩子结点while (child>0){if (parr[child] < parr[parent]){//交换数据Swap(&parr[child], &parr[parent]);child = parent;        //更新结点位置parent = (child - 1) / 2;}else{break;}}
}

删除堆顶元素 

1.将堆顶元素和尾部元素互换位置;

2.将此刻不符合规定的堆顶元素往下调整至相应位置; 

// 删除堆顶(根节点)
void HeapPop(Heap* php)
{assert(php);//1.堆顶元素和尾部元素置换位置Swap(&php->parr[0], &php->parr[php->size - 1]);php->size--;	//删掉交换后的堆顶元素//2.将新站顶元素找到相应位置//向下调整AdjustDown(php->parr,php->size,0);
}

往下调整 

堆顶元素与其孩子结点比较,如何找到较大(较小)的孩子?

可以假设法:假设较大(较小)的孩子为左孩子,然后验证假设;

//向下调整
void AdjustDown(HPDataType* parr,int size,int parent)
{int child = (parent * 2) + 1;while (child<size)	//{//假设左孩子为较小值if (child+1<size && parr[child + 1] < parr[child])	//验证假设{++child;	//说明右孩子更小,更换孩子位置}//检查是否不符合堆排序结构 if (parr[parent] > parr[child]){Swap(&parr[parent], &parr[child]);//往下更新父结点 孩子结点位置parent = child;child = parent * 2 + 1;}else{break;}}
}

返回堆顶元素 

起始值为0;

//返回堆顶元素
HPDataType HeapTop(Heap* php)
{assert(php);assert(php->size > 0);return php->parr[0];
}

 返回有效个数

注意,构建堆的时候,size是最后一个元素的下一个;

//返回堆内有效数据个数
size_t HeapSize(Heap* php)
{assert(php);return php->size;	//数组下标0开始
}

是否为空

//判断堆是否为空
bool HeapEmpty(Heap* php)
{return php->size == 0;
}

 堆排序

以上是一些堆的简单功能实现;算不上真正的堆排序;

假设给定一串数字,4,6,2,1,5,8,2,9;如何将其排序?比如升序;

  1. 建立一个大堆;
  2. 将堆顶元素与堆尾元素互换,且将遍历堆的范围-1,保证其想要的值保持不动;
  3. 将此刻不符合规定的堆顶往下调整,找到次大的值;重复步骤2;

其实相当于第一个元素默认是堆,后面的进行遍历调整; 

//排序,升序
void HeapSort(int* parr, int n)
{//1.建立大堆for (int i = 1;i < n; i++){justUp(parr, i);}//2.堆顶元素与堆尾元素互换,然后将堆size-1(指只需要遍历到的位置)int end = n - 1;while (end>0){//堆顶和堆尾 元素呼唤Swap(&parr[0], &parr[end]);//往下调整justDown(parr,end,0);end--;}
}

也有其他思路;

我们从下面子树往上遍历,而内部调整时往下调整

 n-1是最后结点下标值,(结点-1)/2 可以得到该结点的父结点,从父结点往下调整;

for (int i = (n-1-1)/2; i >= 0; --i){AdjustDown(parr, n, i);}

 Top-k问题

在排序的基础上,如果这个数很大呢,比如一百万个数,要找到前k个较大值;

此刻建堆排序显然不合适;

1.读取前K个值,建立其小堆;

2.依次读取后面的值,与堆顶比较:如果比堆顶大,替换堆顶进堆,再往下调整;

 创建数据

//tok-k 问题
//创建一千万的数据
void CreateNode()
{// 造数据int n = 10000000;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;	//+i是 因为随机数产生只有三万个,加上i可以大大减少重复值fprintf(fin, "%d\n", x);}fclose(fin);
}

堆top-k

开辟K个数的空间(动态数组);

建立K个数的小堆;

读取文件中值,遍历与堆顶比较,

void HeapTok(const char* file,int k)
{FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}//开辟K个数的空间int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc error");return;}//建立K个数的小堆for (int i = 0; i < k; i++){//从文件流中 读取数据存到 开辟的空间中fscanf(fout,"%d", &minheap[i]);//往上调整AdjustUp(minheap, i);}//遍历文件数据 int x = 0;while (fscanf(fout, "%d", &x) != EOF)	{if (x > minheap[0]){minheap[0] = x;//往下调AdjustDown(minheap, k, 0);}}//打印tokfor (int i = 0; i < k; i++){printf("%d ", minheap[i]);}free(minheap);fclose(fout);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/283336.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言使用posix正则表达式库

在C语言中&#xff0c;你可以使用 POSIX 正则表达式库&#xff08;regex.h&#xff09;来进行正则表达式的模式匹配。POSIX 正则表达式库提供了一组函数来编译、执行和释放正则表达式。 下面是使用 POSIX 正则表达式库的基本步骤&#xff1a; 包含头文件 <regex.h>&…

Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现实时通信

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

【MySQL】数据库基础入门 安装MySQL

目录 介绍&#xff1a; 安装MySQL: 设置 root 账号密码 2.配置环境变量 2.找到 Path 系统变量, 点击 "编辑" 介绍&#xff1a; MySQL是一个开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是一种用于管理和存储数据的软件。 安装MySQL: …

前端登录界面网站设计模板--HTML+CSS

🎀登录表单 💖效果展示 💖HTML代码展示 <!DOCTYPE html> <html lang="en" > <head></

Linux驱动(中断、异步通知):红外对射,并在Qt StatusBus使用指示灯进行显示

本文工作&#xff1a; 1、Linux驱动与应用程序编写&#xff1a;使用了设备树、中断、异步通知知识点&#xff0c;实现了红外对射状态的异步信息提醒。 2、QT程序编写&#xff1a;自定义了一个“文本指示灯”类&#xff0c;并放置在QMainWidget的StatusBus中。 3、C与C混合编程与…

[Verilog] Verilog 数据类型

主页&#xff1a; 元存储博客 文章目录 前言1. bit 类型2. reg 类型3 wire类型4 integer类型5 real类型6 parameter类型7 enum类型8 array 类型9 向量类型10 time 类型11 string 类型 前言 在 Verilog 中&#xff0c;有几种不同的数据类型可以用于声明和操作变量。 在 Verilo…

Missing artifact org.wltea.analyzer:ik-analyzer:jar:5.0

没有找到【org.wltea.analyzer】 找到了【org.wltea.ik-analyzer】 https://github.com/wks/ik-analyzer https://github.com/wks/ik-analyzer.git https://code.google.com/archive/p/ik-analyzer/downloads?page2 C:\Users\Administrator\Desktop\ik-analyzer-master>m…

Docker使用3-Share the application

写在前面 本文主题是Share the application&#xff0c;这里是链接。本文主要学习如何将镜像image上传到Docker Hub 创建仓库 创建并登录Docker Hub登录后点击Create Repository按钮仓库名填写getting-started&#xff0c;确保仓库权限为公开的点击Create按钮 推送镜像 在…

ArcMap自定义脚本工具箱迁移至ArcGIS pro

本文记录了将ArcMap10.7创建的自定义脚本工具箱&#xff08;.tbx&#xff09;迁移至ArcGIS pro的过程 ArcGIS Pro使用的是python版本与ArcMap不同&#xff0c;前者为python3&#xff0c;后者为python2。由于python3 和 python2 的部分语法不兼容&#xff0c;以及一些地理处理工…

正点原子驱动开发BUG(一)--SPI无法正常通信

目录 一、问题描述二、讲该问题的解决方案三、imx6ull的spi适配器驱动程序控制片选分析3.1 设备icm20608的驱动程序分析3.2 imx的spi适配器的驱动程序分析 四、BUG修复测试五、其他问题 一、问题描述 使用正点的im6ull开发板进行spi通信驱动开发实验的时候&#xff0c;主机无法…

链表基础知识(二、双向链表头插、尾插、头删、尾删、查找、删除、插入)

目录 一、双向链表的概念 二、 双向链表的优缺点分析​与对比 2.1双向链表特点&#xff1a; 2.2双链表的优劣&#xff1a; 2.3循环链表的优劣 2.4 顺序表和双向链表的优缺点分析​ 三、带头双向循环链表增删改查实现 3.1SList.c 3.2创建一个新节点、头节点 3.3头插 3.…

智慧校园2.0物联网管理平台建设方案:PPT全文22页,附下载

关键词&#xff1a;物联网解决方案&#xff0c;智慧校园解决方案&#xff0c;物联网平台建设方案&#xff0c;物联网应用技术 一、智慧校园2.0物联网管理平台建设背景 1、教育现代化和强国建设的需要&#xff1a;近年来&#xff0c;国家为了加快推进教育现代化、教育强国建设…