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

这篇具有很好参考价值的文章主要介绍了[C语言实现]数据结构堆之《害怕二叉树所以天赋全点到堆上了》。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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


🥰作者: FlashRider

🌏专栏: 数据结构

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

目录

什么是堆?

如何实现堆?

代码实现堆(小根堆)

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

堆的插入

堆的删除

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

代码测试

TopK问题


什么是堆?

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

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

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

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

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

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

如何实现堆?

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

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

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

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

[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;
}

测试结果:

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

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


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;
}

运行结果:

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

没有任何问题。文章来源地址https://www.toymoban.com/news/detail-485052.html

到了这里,关于[C语言实现]数据结构堆之《害怕二叉树所以天赋全点到堆上了》的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】二叉树OJ题(C语言实现)

    ✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ 🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿 🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟 🌟🌟 追风赶月莫停留 🌟🌟 🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀 🌟🌟 平芜尽处是春山

    2024年03月17日
    浏览(34)
  • 【数据结构初阶】八、非线性表里的二叉树(二叉树的实现 -- C语言链式结构)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】七、非线性表里的二叉树(堆的实现 -- C语言顺序结构)-CSDN博客  ==========

    2024年02月08日
    浏览(38)
  • C语言数据结构初阶(10)----二叉树的实现

    · CSDN的uu们,大家好。这里是C语言数据结构的第十讲。 · 目标:前路坎坷,披荆斩棘,扶摇直上。 · 博客主页: @姬如祎 · 收录专栏: 数据结构与算法     目录 1. 函数接口一览 2. 函数接口的实现 2.1 BTNode* BuyNode(BTDataType x) 的实现 2.2 BTNode* CreateTree() 的实现  2.3 void

    2023年04月08日
    浏览(31)
  • 二叉树的基本操作-C语言实现-数据结构作业

    目录  (1)二叉树的创建; (2)二叉树的先序、中序和后序遍历输出; (3)输出二叉树的叶子节点和度为2的节点的数量; (4)输出二叉树的深度; (5)将二叉树所有节点的左右子树互换(左子树变右子树,右子树变左子树); (6)参考书上,二叉树按层次输出(一行输出一层); (7)删除二

    2024年02月04日
    浏览(34)
  • 数据结构初阶之基础二叉树(C语言实现)

    📃 博客主页: 小镇敲码人 💞 热门专栏:数据结构与算法 🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎 ❤️ 什么

    2024年03月19日
    浏览(32)
  • 数据结构(C语言实现)——二叉树的概念及二叉树顺序结构和链式结构的实现(堆排序+TOP-K问题+链式二叉树相关操作)

    前面学习了数据结构中线性结构的几种结构,顺序表,链表,栈和队列等,今天我们来学习一种非线性的数据结构——树。由于二叉树是数据结构中的一个重点和难点,所以本文着重介绍二叉树的相关概念和性质,以及二叉树的应用。 树是一种非线性的数据结构,它是由n(

    2023年04月21日
    浏览(35)
  • 数据结构入门(C语言版)二叉树的顺序结构及堆的概念及结构实现应用

    普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用 顺序结构的数组来存储 ,需要注意的是 这里的堆和操作系统虚拟进程地址空间中的堆是两回事 ,一个是 数据结构 ,一

    2023年04月19日
    浏览(39)
  • 【数据结构】线索二叉树(适用场景+图文推导过程+C语言代码实现)

    普通二叉树(如下图): 空间浪费 :存在大量“∧”,该空间未利用。 时间效率 :查找一次结点的前驱、后继就需要遍历一次,时间效率低。         在实际问题中,如果所用的 二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表

    2024年02月04日
    浏览(27)
  • 【数据结构初阶】七、非线性表里的二叉树(堆的实现 -- C语言顺序结构)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】六、线性表中的队列(链式结构实现队列)-CSDN博客  ===========================

    2024年02月08日
    浏览(35)
  • 数据结构---二叉树(C语言)

    空树 非空:根节点,根节点的左子树、根节点的右子树组成的。 从二叉树的定义来看,二叉树是递归定义的,因此我们可以用递归的形式来遍历二叉树。 1.1.1二叉树前中后序遍历(递归版) 访问根结点的顺序不同。 1.1.2 层序遍历 层序遍历是按照二叉树的高度,一层一层遍

    2024年02月06日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包