[C语言实现]数据结构堆之《害怕二叉树所以天赋全点到堆上了》

news2025/2/1 10:36:25


🥰作者: FlashRider

🌏专栏: 数据结构

🍖知识概要:详解的概念、小根堆与大根堆的区别、以及代码实现。

目录

什么是堆?

如何实现堆?

代码实现堆(小根堆)

定义堆以及堆的初始化和销毁。

堆的插入

堆的删除

获取堆的元素长度和获取堆顶元素

代码测试

TopK问题


什么是堆?

我们先来一点二叉树的知识。首先我们需要知道,树的一个节点如果含有子节点,那么这个节点可以称为父节点,一个节点含有的子树个数称为该节点的,而度都为2的树,则称为二叉树。
完全二叉树则是最后一排子节点可以不全都是2个的二叉树 ( 满二叉树也是完全二叉树 )。
还有满二叉树,也就是每一个父结点的子节点都有2个的二叉树。

 

 

一个堆(Heap)通常可以看作一个完全二叉树,准确来说任何一个顺序表都可以看作一个完全二叉树。但是根在完全二叉树的基础上有一些特性。

小根堆:任何一个父节点都比子节点的值要小。

大根堆:任何一个父节点都比子节点的值要大。

如何实现堆?

我们知道,堆可以看作一个完全二叉树,且满足堆本身的特性(小根堆大根堆),而顺序表也可以看作一个完全二叉树。比如SeqList a = {1, 2, 3, 4, 5};

而图中1 < 2 && 1 < 3;   2 < 4 && 2 < 5 所有的父节点的大于子节点,这个恰好满足小根堆特性,如果倒过来也可以满足大根堆特性,所以可以用顺序表来实现堆。
我们用下标为0的地方当作根节点,然后根的左子节点,根的右子节点,以此类推。

[0]为父节点 [1] [2]为子节点  [1]为父节点 [3][4]为子节点 [2]没有子节点 所以后面为空。
我们还可以发现一个下标的规律:左子节点 = 父节点 * 2 + 1   右子节点 = 父节点 * 2 + 2
                                                      父节点 = (子节点 - 1) / 2
(整除,向下取整)
因此用顺序表存储可以让我们轻易的通过子节点找到父节点,父节点找到子节点。

代码实现堆(小根堆)

定义堆以及堆的初始化和销毁。

因为我们使用顺序表来实现堆,所以按照顺序表的定义初始化方法来写。

typedef int HPDataType;//元素类型
typedef struct Heap
{
    HPDataType* a;//首元素地址
    int size;//当前长度
    int capacity;//最大长度
}HP;
//初始化堆
void HeapInit(HP* root);
//销毁堆
void HeapDestroy(HP* root);
void HeapInit(HP* root)
{
    assert(root);//断言
    root->a = NULL;
    root->size = root->capacity = 0;
}
void HeapDestroy(HP* root)
{
    assert(root);
    free(root->a);//释放开辟的内存空间
    root->a = NULL;
    root->size = root->capacity  = 0;
}

堆的插入

如果当前数据是一个小根堆,我们如果要在后面插入一个数据(比如插入一个比父节点更小的数据),并不能和保证它还是堆,所以我们在尾部插入数据之后,需要将这个数进行向上调整,以保证满足小根堆的特性。

void HeapPush(HP* root, HPDataType x);
void AdjustUp(HPDataType*a, int child);

void HeapPush(HP* root, HPDataType x)
{
    assert(root);
    //判满
    if(root->size == root->capacity)
    {
        int newcapacity = root->capacity == 0 ? 4 : root->capacity * 2;//增容
        //内存开辟扩容
        HPDataType* tmp = (HPDataType*)realloc(root->a, sizeof(HPDataType)*newcapacity);
        if(tmp == NULL)//内存开辟是否成功
        {
            printf("realloc fail\n");
            exit(-1);
        }
        root->capacity = newcapacity;
        root->a = tmp;
    }
    root->a[root->size] = x;
    root->size++;
    //将尾部插入的数据向上调整
    AdjustUp(root->a, root->size - 1);
}
void AdjustUp(HPDataType*a, int child)
{
    assert(a);
    while(child > 0)
    {
        int parent = (child - 1) / 2; //之间总结的公式算出父节点
        if(a[parent] > a[child]) //如果父节点更大 就不满足小根堆 需要交换
        {
            int tmp = a[parent];
            a[paretn] = a[child];
            a[child] = tmp;
            child = parent;//交换后更新子节点
        }
        else break; //如果父节点更小 满足小根堆 不需要再调整
    }
}

堆的删除

堆的删除是从堆顶删除元素,但是同样的道理,我们删除堆顶元素后,不能保证接下来的数据还是一个堆,并且挪动大量元素会很麻烦,所以我们把首元素和尾元素交换,直接删除尾元素就等于删除堆顶元素了,之后再让交换后的堆顶元素向下调整保证是堆即可。

void HeapPop(HP* root);
void AdjustDown(HPDataType* a, int n, int parent);
bool HeapEmpty(HP* root);//是否为空
void HeapPop(HP* root)
{
    assert(root);
    assert(!HeapEmpty(root));//如果堆为空就没元素可以pop了
    //交换首尾元素
    HPDataType tmp = root->a[root->size - 1];
    root->a[root->size - 1] = root->a[0];
    root->a[0] = tmp;
    //删除尾元素
    root->size--;
    //将堆顶向下调整 
    AdjustDown(root->a, root->size, 0);
}
void AdjustDown(HPDataType* a, int n, int parent)
{
    assert(a);
    int child = parent * 2 + 1;//算出左孩子
    while(child < n)//比到最后一个节点结束
    {
        if(child + 1 < n && a[child] > a[child+1])
            child++; //如果右子节点存在 且小于左子节点 则child变为右子节点。
        if(a[child] < a[parent])//保证满足小根堆
        {
            HPDataType tmp = a[child];
            a[child] = a[parent];
            a[parent] = tmp;
            parent = child;
            child = parent * 2 + 1;//更新子节点和父节点
        }
        else break;
    }
}
bool HeapEmpty(HP* root)
{
    assert(root);
    return root->size == 0; //为空返回真 不为空返回假
}

获取堆的元素长度和获取堆顶元素

void HeapSize(HP* root);
void HeapTop(HP* root);

void HeapSize(HP* root)
{
    assert(root);
    return root->size;
}
void HeapTop(HP* root)
{
    assert(root);
    assert(!HeapEmpty(root));
    return root->a[0];
}

代码测试

堆以及写的差不多了,我们来测试一下是否成功。

int main()
{
	HP hp;
	HeapInit(&hp);
	int a[] = {65, 100, 70, 32, 50, 60};
	for(int i = 0; i < 6; i++)
		HeapPush(&hp, a[i]);
	while(!HeapEmpty(&hp))
	{
		int top = HeapTop(&hp);
		printf("%d\n", top);
		HeapPop(&hp);
	}
	return 0;
}

测试结果:

测试没问题,满足小根堆。


TopK问题

给一组长度为n的数据,要求在这n个数中找出最大/最小的K个数据。

如果我们暴力直接遍历的话,时间复杂度非常高,数据量一旦比较大就会TLE。

但是堆可以解决这种问题。

找最大: 建小堆。

找最小: 建大堆。

如果给你一万个数,要求找最大的10个,我们只需要把前10个建成小堆。然后把剩下的数据和堆顶元素比较(小堆的堆顶元素最小)如果比堆顶还小直接排除,如果比堆顶大则变成新的堆顶并向下调整。最后就能得到最大的10个数。

void PrintTopK(int k, int* a, int n)
{
	Heap hp;
    HeapInit(&hp);
    for(int i = 0; i < k; i++)
        HeapPush(&hp, a[i]);
    for(int i = k; i < n; i++)
    {
        if(a[i] > HeapTop(&hp))
        {
            hp.a[0] = a[i];
            AdjustDown(hp.a, k, 0);
        }
    }
    for(int i = 0; i < k; i++)
    {
    	printf("%d ", HeapTop(&hp));
    	HeapPop(&hp);
	}
}
int main()
{
    int arr[20] = {1,2,3,4,5,10,12,15,20,6,7,8,9,11,13,14,18,19,16,17};
    PrintTopK(5, arr, 20);
    return 0;
}

运行结果:

没有任何问题。

 

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

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

相关文章

LeetCode·每日一题·1177. 构建回文串检测·前缀和

作者&#xff1a;小迅 链接&#xff1a;https://leetcode.cn/problems/can-make-palindrome-from-substring/solutions/2309940/qian-zhui-he-zhu-shi-chao-ji-xiang-xi-by-n3ps/ 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 著作权归作者所有。商业转载请联系作者获…

最新水文水动力模型在城市内涝、城市排水、海绵城市规划设计中深度应用

随着计算机的广泛应用和各类模型软件的发展&#xff0c;将排水系统模型作为城市洪灾评价与防治的技术手段已经成为防洪防灾的重要技术途径。本次培训将聚焦于综合利用GIS及CAD等工具高效地进行大规模城市排水系统水力模型的建立&#xff0c;利用SWMM实现排水系统水力模拟。讲解…

【RH850/U2A】:GreenHills编译配置

GreenHills编译配置 GreenHills语法.gpj文件.opt文件示例GreenHills编译器在编译我们的文件时涉及它需要哪些文件及相关配置呢?带着疑问我们开始来梳理。 我们还是以具体示例来展开(硬件平台:RH850 U2A8) GreenHills语法 一般我们是需要查看它的帮助文档的,文档在哪里呢?…

CSP第二轮/NOIP 比赛注意事项

一、在哪里写代码 主办方会提前在桌面已在 E 盘根目录下建立以考生准考证编号命名的文件夹,考生应检查该文件夹名称是否正确(包括编号及大小写字母),如有错误须立即上报监考人员,由监考人员进行更改。确认无误后,考生须为每道试题再单独建立一个子文件夹,子文件夹名与对应…

某互联网银行绿色金融背后的“安全秘诀”

​随着银保监会出台《银行业保险业绿色金融指引》、人民银行牵头制定《G20转型金融框架》的发布&#xff0c;金融行业正在持续加大对绿色金融支持力度。某互联网银行为了响应号召&#xff0c;采用数字化无纸化办公&#xff0c;线上零接触服务减少大量碳排放&#xff0c;成为国内…

oracle rac架构解读

一、oracle 数据库架构 单节点数据库&#xff0c;如果实例宕机了&#xff0c;如果一个业务链接在实例上面&#xff0c;那么这个业务就中断了。这个时候系统就不具有可用性了&#xff0c;那么这个时候单节点的可用性是很差的。 对于RAC来说&#xff0c;和单实例一样&#xff0c;…

新榜 | 小红书美妆用户趋势洞察报告

目前&#xff0c;小红书上聚集了大量年轻、高知的女性美妆用户&#xff0c;她们倾向于在小红书平台分享美妆护肤产品和使用经验&#xff0c;用户间的互动、分享氛围浓厚&#xff1b;而这些评论互动也传递了用户的真实诉求&#xff0c;理解用户的关注点对于企业和品牌来说将具有…

Vue中如何进行数据导入与Excel导入

Vue中如何进行数据导入与Excel导入 Vue是一款非常流行的JavaScript框架&#xff0c;它提供了一套用于构建用户界面的工具和库。在Vue中&#xff0c;我们可以使用多种方式来导入数据&#xff0c;包括从服务器获取数据、从本地存储获取数据、从文件中读取数据等等。其中&#xf…

如何提高职场沟通能力

如何提高职场沟通能力 在现代职场中&#xff0c;良好的沟通能力不仅有助于我们更好地完成工作任务&#xff0c;还能提高团队协作效率&#xff0c;降低矛盾和误解。本文将为你提供一些建议和技巧&#xff0c;帮助你提高职场沟通能力。 1. 倾听 倾听是沟通中最重要的技能之一…

社招准备和面试题

这次就整理下这次社招都做了哪些准备以及自己面试过的题目。 首先就是专业知识的准备&#xff0c;看了很多常用的机器学习算法&#xff0c;并对其算法做了推导。看了深度学习推荐系统这本书里面的模型&#xff0c;对自己简历中涉及到的模型重点掌握&#xff0c;比如DIN、DIEN、…

创客匠人6月功能更新:服务商管理、直播、学员版APP全新上线

创客匠人6月功能更新&#xff0c;包括服务商管理、直播、学员版APP、圈子、商城、店铺等众多产品升级&#xff0c;我们一起来看看吧。 正式升级时间&#xff1a;6月20日 一、服务商管理 1.服务商模块排版优化&#xff1a;支持查看整个团队的用户信息和业绩明细。 2.支持记…

嵌套滚动实践:onInterceptTouchEvent与NestedScrolling【实用为准】

嵌套滚动&#xff1a;内外两层均可滚动&#xff0c;比如上半部分是一个有限的列表&#xff0c;下半部分是WebView&#xff0c;在内层上半部分展示到底的时候&#xff0c;外部父布局整体滚动内部View&#xff0c;将底部WevView拉起来&#xff0c;滚动到顶部之后再将滚动交给内部…

SQL Server 无备份情况下误操作数据恢复(3)

原文链接&#xff1a;https://blog.csdn.net/dba_huangzj/article/details/8491327 问题&#xff1a; 经常看到有人误删数据&#xff0c;或者误操作&#xff0c;特别是update和delete的时候没有加where&#xff0c;然后就喊爹喊娘了。人非圣贤孰能无过&#xff0c;做错可以理解…

Verilog 高级知识点---状态机

目录 状态机 1、Mealy 状态机 2、Moore 状态机 3、三段式状态机 状态机 Verilog 是硬件描述语言&#xff0c;硬件电路是并行执行的&#xff0c;当需要按照流程或者步骤来完成某个功能时&#xff0c;代码中通常会使用很多个 if 嵌套语句来实现&#xff0c;这样就增加了代码…

2DUI跟踪3D模型,更精准的嵌套与跟踪

实现的效果&#xff1a; 1、2DUI跟踪模型指定位置&#xff0c;跟随模型移动 2、2DUI时刻面向摄像机 首先准备一个模型。如下图&#xff1a; 在此模型层级下新建Canvas&#xff08;画布&#xff09; 改显示模式为世界空间 在canvas下创建Image&#xff08;图像&#xff09; 放…

包看包会Stable Diffusion原理,新手也能看明白

知道看文章的人怎么看&#xff0c;听我讲的人经常反应的就是听不明白。于是我又在网上找了一下&#xff0c;发现这篇文章讲的很好&#xff0c;算得上是深入浅出&#xff0c;可惜是英文的&#xff0c;就把它翻译了一下&#xff1a; https://stable-diffusion-art.com/how-stabl…

一次过!快速申领软件著作权

文章目录 一次过&#xff01;快速申领软件著作权1 软件著作权的定义2 申请流程2.1 准备申请材料2.2 登录软著局申请系统并进行填写2.3 审核2.4 补正和修改申请材料2.5 接受核准并领证 3 申请材料4 注意事项5 总结 一次过&#xff01;快速申领软件著作权 申领软件著作权是保护软…

【Nexus】Nexus创建Maven私服

目录 一、前言二、创建Blob Stores1、在创建Repository之前&#xff0c;设定一个文件存储目录Blob&#xff0c;方便后序管理2、选择创建的Blob类型为File&#xff0c;根据需要选择是否超出约束时进行报警&#xff0c;以及约束类型和约束限制3、成功创建好的页面 三、创建Reposi…

如何快速翻译ppt文档?分享几个实用的文档翻译方法

想必你也曾有过这样的困扰&#xff1a;在工作或学习中&#xff0c;需要阅读外语PPT的内容&#xff0c;但是却遇到了语言障碍&#xff0c;无法流利地理解其中的意思。这时&#xff0c;我们就需要翻译ppt的软件来帮助我们解决问题。那么&#xff0c;翻译ppt的软件哪个好呢&#x…

【LeetCode热题100】打卡第22天:编辑距离颜色分类

文章目录 【LeetCode热题100】打卡第22天&#xff1a;编辑距离&颜色分类⛅前言 编辑距离&#x1f512;题目&#x1f511;题解 颜色分类&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第22天&#xff1a;编辑距离&颜色分类 ⛅前言 大家好&#xff0c;我是…