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

【数据结构初阶】--二叉树(三)

🔥个人主页:@草莓熊Lotso

🎬作者简介:C++研发方向学习者

📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。

前言: 在前面我们完成了堆这个数据结构的代码实现以及堆排序的思想和实现,那么在堆排序中我们采用了一种向下建堆的思想,但其实我们也是可以向上建堆的。在今天这篇博客中我也会给大家分享这种方法,再就是一个经典的TOP-K问题,我们来一起解决一下。


目录

一.向上调整算法建堆和向下调整算法建堆的时间复杂度计算

二.向上调整算法建堆和向下调整算法建堆的时间复杂度对比以及堆排序的时间复杂度

三.Top-k问题

基本思路: 

代码演示: (找前k个最大的,建小堆)

主要代码: 


一.向上调整算法建堆和向下调整算法建堆的时间复杂度计算

首先我们先来看一下向上调整算法建堆的代码吧:(向下调整的在上篇博客中有这里就不演示了)

//向上调整算法建堆
for (int i = 0; i < n; i++)
{AdjustUp(arr, i);
}

 --其本质就是从第一个节点开始,依次向下使用向上调整算法将当前的结构调整成一个堆。

既然向上调整算法和向下调整算法都可以建堆,那是不是他们的时间复杂度都是一样的呢?

-------------先说结论,不是的。

--我们来看一下向上调整算法和向下调整算法的时间复杂度

如图所示我们可知向上调整算法和向下调整算法的时间复杂度都是O(logn) 

--那使用它们两个建堆的时间复杂度也相同吗,我们也来一起看看

向上调整算法建堆:O(n*logn)--证明过程如下图

向下调整算法建堆:O(n)--证明过程如下图

 --通过计算我们会发现,使用向下调整算法建堆要比使用向下调整算法建堆更优


二.向上调整算法建堆和向下调整算法建堆的时间复杂度对比以及堆排序的时间复杂度

--那为什么向上调整算法和向下调整算法时间复杂度一样,但是他们建堆的时间复杂度会有区别呢?

我们从图中可以看出,建堆的复杂度由节点调整次数和节点个数决定,向上调整算法建堆越往下节点个数越多,调整次数也越多,自然时间复杂度是没有向下调整算法低的。 

堆排序的时间复杂度: O(n*logn)

 --堆排序的时间复杂度为n*logn,这个时间复杂度算是比较优秀的一种排序算法了。


三.Top-k问题

Top-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名,世界500强,富豪榜,游戏前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据不能一下全部加载到内存中)。最佳的方式就是用堆来解决。

具体思考和解决过程如下图所示:

基本思路: 

1.用数据集合中前K个元素来建堆:

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆
2.用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素:
  • 将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素

代码演示: (找前k个最大的,建小堆)

--先给大家回顾一下向上调整算法和向下调整算法处理小堆的实现代码

//向上调整
void AdjustUp(int* 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 AdjustDown(int* arr, int parent, int n)
{assert(arr);int child = 2 * parent + 1;while (child < n){//child+1也小于n//后面的小堆就是取小的,大堆取大的孩子if (child + 1 < n && arr[child] > arr[child + 1]){child++;}//大堆:>   小堆:<if (arr[child] < arr[parent]){swap(&arr[child], &arr[parent]);parent = child;child = 2 * parent + 1;}else {break;}}
}

主要代码: 

void CreateNDate()
{// 造数据int n = 100000;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) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}void topk()
{printf("请输入k:");int k=0;scanf("%d", &k);const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fout fail!");exit(1);}//申请空间大小为k的空间int* MinHeap = (int*)malloc(k * sizeof(int));if (MinHeap == NULL){perror("malloc fail!");exit(2);}//读取前k个数据for (int i = 0; i < k; i++){fscanf(fout, "%d", &MinHeap[i]);}//数组调整建堆--向下调整建堆//找最大的前K个数,建小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(MinHeap, i, k);}//遍历剩下的n-k个数,跟堆顶比较,谁大谁入堆int data = 0;while (fscanf(fout, "%d", &data) != EOF){if (data > MinHeap[0]){MinHeap[0] = data;//换了之后记得向下调整回小堆AdjustDown(MinHeap, 0, k);}}//打印堆里的数据for (int i = 0; i < k; i++){printf("%d ", MinHeap[i]);}printf("\n");fclose(fout);
}int main()
{//CreateNDate();topk();return 0;
}

--我们光看这个也不能确定我们得出来的是不是前10个最大的啊,所以我们需要去验证一下:

1.打开data.txt文件,手动添加10个大于1000000的数

2.如果打印出的是这10个数,并且最后是个小堆那就证明没有问题 

--时间复杂度计算: O(n)=k+(n-k)logk


往期回顾: 

【数据结构初阶】--栈和队列(一)

【数据结构初阶】--栈和队列(二)

【数据结构初阶】--树和二叉树先导篇

【数据结构初阶】--二叉树(二)

结语:本篇博客就到此结束了,大家后续也可以自己去实现和完成一下博客中的证明和题目,加深印象。如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。

http://www.xdnf.cn/news/16546.html

相关文章:

  • 使用signal信号机制 + backtrace函数打印出程序崩溃后的堆栈信息
  • Flutter在购物场景中BLoC的应用
  • MySQL面试题及详细答案 155道(001-020)
  • 无人机气动设计模块解析
  • 微信小程序点击输入框时,顶部导航栏被遮挡问题如何解决?
  • 秩为1的矩阵的特征和性质
  • 【数据库】时序数据库选型指南:从大数据视角看IoTDB的核心优势
  • <PLC><西门子><modbusTCP>在西门子S7-1200系列PLC中,如何设置modbusTCP通讯?
  • 语音识别指标计算 WER
  • Java-泛型类的定义与使用
  • 24. 了解过 webp 吗
  • 如何进行DAP-seq的数据挖掘,筛选验证位点
  • Django 视图详解(View):处理请求与返回响应的核心
  • CenterOS8.5三台机器配置互信
  • 图解MySQL-小林code笔记
  • 排水管网实时监测筑牢城市安全防线
  • 本地大语言模型部署指南
  • Dify 工作流深度解析与实战指南
  • 重复文件清理工具,附免费链接
  • RWA 正当红,是 DeFi 的终点、拐点,还是新起点?
  • 常用设计模式系列(十四)—模板方法模式
  • HTML响应式SEO公司网站源码
  • 电脑开机不显示网卡的原因
  • 微算法科技(NASDAQ:MLGO)利用基于区块链的机器学习模型进行交易分类,实现交易数据的匿名化
  • Python 列表内存存储本质:存储差异原因与优化建议
  • ubnutu网络
  • Excel常用函数大全,非常实用
  • 旋变转换电路
  • Vue组件通信的终极指南
  • 【数据库】使用Sql Server将分组后指定字段的行数据转为一个字段显示,并且以逗号隔开每个值,收藏不迷路