堆的向下调整算法和TOPK问题

目录

1.什么是堆?

1.1 向下调整建堆的时间复杂度计算

1.2 堆的结构体设计

2.堆的功能实现:

2.1 堆的插入:

2.2 堆的删除:

2.3 堆排序:

2.4 向下调整建堆:

2.5 TOPK问题:

2.6 向上调整算法:

2.7 向下调整算法:

2.8 堆的初始化/返回堆顶元素/返回堆内有效元素个数/堆的判空/堆的销毁


1.什么是堆?

首先

堆是一种完全二叉树,它一定满足所有的根结点都大于或小于它的左右子树

如果是大堆,那么堆顶的数就是堆中最大的数

如果是小堆,那么堆顶的数就是堆中最小的数

堆常常用来解决排序和TOPK问题

对于完全二叉树而已,若将结点从根结点开始,从0开始编号,那么

父节点 = (i-1)/2 (i-1)/2>=0

左孩子结点=(i*2)+1 (i*2)+1<n

右孩子结点=(i*2)+2 (i*2)+2<n

 

1.1 向下调整建堆的时间复杂度计算

首先需要知道二叉树的几个性质:

  1. 若规定二叉树的根结点的层数为1,那么二叉树的第i层有2^(i-1)棵结点
  2. 若规定二叉树的根结点的层数为1,那么一颗二叉树最多有2^(h)-1
  3. 对任意一颗二叉树,如果度为0为n0,度为2为n2,那么n0 = N2+1
  4. 若规定二叉树的根节点的层数为1, 具有n个结点的二叉树,其高度h为log(n+1)

根据性质去推算

从最后一个父节点开始,需要向下调整的次数:

T(h) = 2^0*(h-1) + 2^1*(h-2) + 2^2*(h-3) +.......+ 2^(h-3)*2 + 2^(h-2) *1

2*T(h) = 2^1*(h-1) + 2^2*(h-2) + 2^3*(h-3) +.......+ 2^(h-2)*2  +2^(h-1) *1

错位相减:2^1 + 2^2 + 2^3 ...... 2^(h-2) + 2^(h-1) - h +1

T(h) = 2^0+2^1 + 2^2 + 2^3 ...... 2^(h-2) + 2^(h-1) - h

T(h) = 2^h - 1 -h = N - Log(n+1)

采用大O的渐进表示法,建堆的时间复杂度就是O(N)

1.2 堆的结构体设计

#pragma once#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HeapNodeDataType;
typedef struct Heap
{HeapNodeDataType* _a;int _size;int _capacity;
}Heap;
//堆向上调整
void AdjustUp(HeapNodeDataType* array, int n);
//堆向下调整
void AdjustDown(HeapNodeDataType* array, int n, int size);
//堆的初始化
void HeapInit(Heap* php);
//堆的销毁
void HeapDestory(Heap* php);
//堆的插入一个结点
void HeapPush(Heap* php, HeapNodeDataType data);
//堆的删除一个结点
void HeapPop(Heap* php);
//堆的出堆顶数据
HeapNodeDataType HeapTop(Heap* php);
//返回堆内有效个数
int HeapSize(Heap* php);
//交换函数
void Swap(HeapNodeDataType* a, HeapNodeDataType* b);
//判断堆是否为空
bool HeapEmpty(Heap* php);

2.堆的功能实现:

2.1 堆的插入:

在尾部插入,向上调整,跟父节点比较,若满足条件就交换它们

void HeapPush(Heap* php, HeapNodeDataType data)
{assert(php);if (php->_capacity == php->_size){int newCapacity = php->_capacity == 0 ? 4 : php->_capacity * 2;HeapNodeDataType* newArray = (HeapNodeDataType*)realloc(php->_a, sizeof(HeapNodeDataType) * newCapacity);php->_a = newArray;php->_capacity = newCapacity;}php->_a[php->_size] = data;int child = php->_size;php->_size++;AdjustUp(php->_a, child);
}

2.2 堆的删除:

将堆顶元素跟堆尾元素交换,然后从堆顶开始向下调整

void HeapPop(Heap* php)
{assert(php);assert(php->_a);Swap(&php->_a[0], &php->_a[php->_size - 1]);php->_size--;AdjustDown(php->_a, 0, php->_size);
}

2.3 堆排序:

利用向下调整算法建堆,然后将堆顶数据放到堆尾,堆内有效元素-1,再向下调整

void HeapSort(int* a, int n)
{int parent = (n - 1 - 1) / 2;while (parent >= 0){AdjustDown(a, parent, n);parent--;}for(int i = n - 1; i > 0; i--){Swap(&a[0], &a[i]);AdjustDown(a, 0, i);}
}

2.4 向下调整建堆:

最后一个父节点((end-1 )/2)开始向下调整建堆,到根结点向下调整建堆结束

void AdjustDown(HeapNodeDataType* array, int n, int size)
{assert(array);int parent = n;int child = n * 2 + 1;if (child + 1 < size && array[child] > array[child + 1]){child = n * 2 + 2;}while (child < size){if (array[child] < array[parent]){Swap(&array[child], &array[parent]);parent = child;child = parent * 2 + 1;if (child + 1 < size && array[child] > array[child + 1]){child = parent * 2 + 2;}}else{break;}}
}

2.5 TOPK问题:

首先取前K个元素向下调整建堆,然后遍历剩下的元素,若满足条件则进堆

如果是取前k个最大的数,那么就建小堆

如果是取前k个最小的数,那么就建大堆

void CreateNDate()//创建一个有100000个数的文件
{// int n = 100000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (size_t i = 0; i < n; ++i){int x = rand() % 100000;fprintf(fin, "%d\n", x);}fclose(fin);
}void PrintTopK(int k)
{int* arr = (int*)malloc(sizeof(int) * k);const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}for (int i = 0; i < k; i++){fscanf(fout,"%d", &arr[i]);}for (int i = (k - 1 - 1)/ 2; i >= 0; i--){AdjustDown(arr, i, k);}int x = 0;while (fscanf(fout, "%d", &x) > 0){if (x > arr[0]){arr[0] = x;AdjustDown(arr, 0, k);}}printf("ǰ%d", k);for (int i = 0; i < k; i++){printf("%d ", arr[i]);}printf("\n");
}

2.6 向上调整算法:

参数:需要数组的首元素地址,数组的有效元素个数

首先创建parent保存父亲结点

若父亲节点小于0则循环结束

判断父节点和子节点,若满足条件则交换,将父亲的值给孩子,继续求孩子结点的父节点

如果判断不需要交换则break

void AdjustUp(HeapNodeDataType* array, int n)
{assert(array);int child = n;while (child > 0){int parent = (child - 1) / 2;if (array[child] > array[parent]){Swap(&array[child], &array[parent]);child = parent;}else{break;}}
}

2.7 向下调整算法:

参数:数组的首元素地址,从什么下表开始向下调整,数组的有效元素个数

已知父亲结点,求左孩子和右孩子结点,判断左右孩子的大小,假设法找到符合条件的哪一个

当孩子结点小于或等于堆内有效元素-1的时候进入循环,孩子结点大于size-1的时候循环结束

比较父子结点,判断是否需要交换

需要交换则将孩子给给父亲,运用假设法继续求孩子的结点中符合条件的那个

不需要交换则break

注意:在比较左右孩子谁满足条件时,需要判断右孩子是否存在,也即右孩子的下表需要小于size

void AdjustDown(HeapNodeDataType* array, int n, int size)
{assert(array);int parent = n;int child = n * 2 + 1;if (child + 1 < size && array[child] > array[child + 1]){child = n * 2 + 2;}while (child < size){if (array[child] < array[parent]){Swap(&array[child], &array[parent]);parent = child;child = parent * 2 + 1;if (child + 1 < size && array[child] > array[child + 1]){child = parent * 2 + 2;}}else{break;}}
}

2.8 堆的初始化/返回堆顶元素/返回堆内有效元素个数/堆的判空/堆的销毁

void HeapInit(Heap* php)
{assert(php);php->_a = NULL;php->_capacity = php->_size = 0;
}
void HeapDestory(Heap* php)
{assert(php);assert(php->_a);free(php->_a);php->_capacity = php->_size = 0;
}
HeapNodeDataType HeapTop(Heap* php)
{assert(php);assert(php->_a);return php->_a[0];
}
int HeapSize(Heap* php)
{assert(php);return php->_size;
}
void Swap(HeapNodeDataType* a, HeapNodeDataType* b)
{HeapNodeDataType tmp = *b;*b = *a;*a = tmp;
}
bool HeapEmpty(Heap* php)
{return php->_size == 0;assert(php);
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity踩坑】UI Image的fillAmount不起作用

在游戏场景中&#xff0c;我们经常在界面上展示进度条&#xff0c;当然有各种形状的&#xff0c;线性的&#xff0c;长方形的&#xff0c;圆形&#xff0c;环形等等。 Unity中实现这种效果的话&#xff0c;最基本的方法说是改变Image的fillAmout属性。 如果你是初次使用UI Ima…

ubuntu安装SFML库+QT使用SFML库播放声音

(1)ubuntu安装SFML库 sudo apt-get install libsfml-dev (2)QT使用SFML库播放声音 在.pro文件中添加头文件路径和库文件路径 INCLUDEPATH /usr/include/SFML LIBS /usr/lib/x86_64-linux-gnu/libsfml*.so UI界面中创建一个pushbutton按钮&#xff0c;并且创建槽函数 加载…

国外大带宽服务器怎么连接

随着互联网技术的发展&#xff0c;企业和个人用户越来越依赖于高速的数据传输服务。国外的大带宽服务器因其高速度、稳定性及较低延迟等优势&#xff0c;成为了许多跨国公司、网站托管商以及数据密集型应用的选择。以下是连接国外大带宽服务器的一些常见方法及其注意事项。 选择…

STL-常用算法 遍历/查找/排序/拷贝和替换/算数生成/集合算法

STL常用算法 常用的遍历算法 for_each #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; #include<vector> #include<algorithm>void myPrint(int v) {cout << v << " "; }class MyPrint { public:void op…

切换到WDDM模式,Tesla M4可以用于本地显示输出了!

正文共&#xff1a;1333 字 21 图&#xff0c;预估阅读时间&#xff1a;2 分钟 上次安装完Tesla M4显卡之后&#xff08;HPE服务器通过显卡直通安装Tesla M4&#xff0c;这算亮机成功了吗&#xff1f;&#xff09;&#xff0c;系统识别正常&#xff0c;但是不能用于显示&#x…

C语言的文件基础知识

一、文件存在的意义 ① 文件的定义是什么&#xff1f; 文件是以单个名称在计算机上存储的信息集合。文件可以是文本文档、图片、程序等等。文件通常具有三个字母的文件扩展名&#xff0c;用于指示文件类型&#xff08;例如&#xff0c;图片文件常常以 JPEG 格式保存并且文件扩…

Hive企业级调优[4]——HQL语法优化之分组聚合优化

HQL语法优化之分组聚合优化 优化说明 在 Hive 中&#xff0c;未经优化的分组聚合通常通过一个 MapReduce Job 实现。Map 端负责读取数据&#xff0c;并按分组字段进行分区&#xff0c;通过 Shuffle 将数据发送至 Reduce 端&#xff0c;在 Reduce 端完成最终的聚合运算。 Hiv…

网页交互模拟:模拟用户输入、点击、选择、滚动等交互操作

目录 一、理论基础 1.1 网页交互模拟的重要性 1.2 网页交互的基本原理 二、常用工具介绍 2.1 Selenium 2.2 Puppeteer 2.3 Cypress 2.4 TestCafe 三、实战案例 3.1 模拟用户输入 3.2 模拟用户点击 3.3 模拟用户选择 3.4 模拟滚动操作 四、最佳实践与优化 4.1 代…

用 Pygame 实现一个乒乓球游戏

用 Pygame 实现一个乒乓球游戏 伸手需要一瞬间&#xff0c;牵手却要很多年&#xff0c;无论你遇见谁&#xff0c;他都是你生命该出现的人&#xff0c;绝非偶然。若无相欠&#xff0c;怎会相见。 引言 在这篇文章中&#xff0c;我将带领大家使用 Pygame 库开发一个简单的乒乓球…

系统优化工具 | Windows Manager v2.0.5 便携版

Windows Manager 是一款专为Microsoft Windows 10/11设计的系统优化和管理软件。它集成了多种实用程序&#xff0c;旨在帮助用户更好地管理和优化Windows操作系统。该软件的功能包括系统清理、系统优化、系统修复、硬件信息查看和系统设置调整等。 系统清理&#xff1a;Window…

Qt Creator项目模板介绍

在Qt Creator中创建项目时&#xff0c;用户可以从多个模板类别中进行选择&#xff0c;以满足不同的开发需求。 Application(Qt) 在Application(Qt)类别下&#xff0c;Qt Creator提供了多种用于创建不同类型Qt应用程序的模板。这些模板主要包括&#xff1a; Qt Widgets Applic…

前缀和与差分(二维)

二维前缀和 下面是一个二维数组&#xff0c;我们要求&#xff08;1&#xff0c;1&#xff09;到&#xff08;2&#xff0c;2&#xff09;区间内的所有元素的和&#xff0c;最原始的方法就是遍历每个元素然后一个一个加起来&#xff0c;此时时间复杂度为O(n*m)。 我们之前学过…

【计算机网络篇】电路交换,报文交换,分组交换

本文主要介绍计算机网络中的电路交换&#xff0c;报文交换&#xff0c;分组交换&#xff0c;文中的内容是我认为的重点内容&#xff0c;并非所有。参考的教材是谢希仁老师编著的《计算机网络》第8版。跟学视频课为河南科技大学郑瑞娟老师所讲计网。 目录 &#x1f3af;一.划分…

【实战篇】MySQL是怎么保证主备一致的?

MySQL 主备的基本原理 如图 1 所示就是基本的主备切换流程。 在状态 1 中&#xff0c;客户端的读写都直接访问节点 A&#xff0c;而节点 B 是 A 的备库&#xff0c;只是将 A 的更新都同步过来&#xff0c;到本地执行。这样可以保持节点 B 和 A 的数据是相同的。 当需要切换的…

PostgreSQL JAVA与SQL集成之PL/Java

PostgreSQL pljava PL/Java 作为 PostgreSQL 的编程语言扩展之一&#xff0c;与 PL/pgSQL&#xff08;PostgreSQL 原生的存储过程语言&#xff09;相比&#xff0c;提供了 Java 语言特有的面向对象功能&#xff0c;并支持 Java 的标准库和第三方库。由于 Java 是一种跨平台的语…

企业搭建VR虚拟展厅,如何选择搭建平台?

选择虚拟展厅搭建平台时&#xff0c;需要综合考虑多个因素以确保平台能够满足您的具体需求并提供高质量的展示效果。以下是一些关键的选择标准&#xff1a; 1. 技术实力与创新能力 技术平台选择&#xff1a;确保平台支持虚拟现实&#xff08;VR&#xff09;、增强现实&#xf…

Qt clicked()、clicked(bool)、toggled(bool)信号的区别和联系

clicked() 信号 所属控件&#xff1a;clicked()信号是QAbstractButton类&#xff08;及其子类&#xff0c;如QPushButton、QRadioButton、QCheckBox等&#xff09;的一个信号。clicked信号可以说是许多控件&#xff08;特别是按钮类控件&#xff0c;如QPushButton&#xff09;…

基于lnmp搭建wordpress

一、案例目标 &#xff08;1&#xff09;了解LNMP环境的组成。 &#xff08;2&#xff09;了解LNMP环境的部署与安装。 &#xff08;2&#xff09;了解WordPress应用的部署与使用。 二、节点规划 IP 主机名 节点 192.168.200.20 lnmp lnmp服务节点 三、案例实施 LN…

C#基于SkiaSharp实现印章管理(8)

上一章虽然增加了按路径绘制文本&#xff0c;支持按矩形、圆形、椭圆等路径&#xff0c;但测试时发现通过调整尺寸、偏移量等方式不是很好控制文本的位置。相对而言&#xff0c;使用弧线路径&#xff0c;通过弧线起始角度及弧线角度控制文本位置更简单。同时基于路径绘制文本时…

2024 新手指南:轻松掌握 Win10 的录屏操作

之前为了节约成本我们公司都采用录制软件操作都方式来为异地的同事进行远程操作培训的。所以我们尝试了不少的录屏工具&#xff0c;这里我就分享下win10怎么录屏的操作过程。 1.福昕录屏大师 链接&#xff1a;www.foxitsoftware.cn/REC/ 这款录屏工具是初学者的理想之选&…