深入理解C++中的堆:从基本概念到实际应用

一、什么是堆?

堆是一种具有树形结构的数据结构,它遵循堆属性,即:每个节点必须小于其每个子节点。

“堆”这个名字可能来自于这样一个事实:如果堆放一堆东西并希望它能保持平衡,人类更愿意把大的东西放在底部,小的东西放在顶部:
在这里插入图片描述

请注意,这与包含动态分配对象的内存区域中的堆完全无关(与栈相对,栈也是一种数据结构的名称)。

堆的一个最重要的特性是,其最小元素位于其根部,易于访问。

在堆中,每个节点理论上可以有任意数量的子节点。但在STL中,堆的节点有两个子节点,因此在本文中将用堆来指代二叉堆。

二、最大堆

堆属性,即每个节点必须小于其子节点,可以推广到另一个比“小于”更通用的比较,如operator<。可以使用对于堆中的数据类型更有意义的某种关系。例如,集合的堆可以使用词典顺序关系。

也可以在堆属性中使用关系“大于”(仍然可以通过反转堆属性并确保子节点小于其父节点来使用operator<来实现)。

这样的堆称为最大堆,这是STL所具有的堆的类型。因此,在本文中,堆指的是二叉最大堆。

在最大堆中,最大的元素位于根部。下面是一个堆的示例:
在这里插入图片描述

可以看到每个节点都小于其父节点,而最大的节点(9)位于根部。

三、实现堆

要表示诸如堆之类的二叉树,一种实现方法是为每个节点进行动态分配,其中2个指针指向其子节点。

但是有一个更高效(更优雅)的实现方法:将它表示为数组的形式,通过对堆进行层次遍历来实现。即数组从根节点开始,然后是该根节点的子节点,然后是这些子节点的所有子节点,然后是这些子节点的所有子节点。等等…。

这样,最大元素位于数组的第一个位置。
在这里插入图片描述
这就是STL表示堆的方式:堆可以存储在std::vector中,例如,元素像上面那样排列在一起。

与为每个节点指向对方的节点相比,此表示方法更有效的原因有几个:

  • 只有一个动态分配用于整个堆,而不是每个节点都有一个动态分配;
  • 没有指向子节点的指针,因此不需要为它们分配空间;
  • 结构的连续布局使其更加友好地缓存。

这一切都很好,但好像不能再遍历树的节点了,因为没有指向子节点(或父节点)的指针。事实上,将二叉树表示为数组的一个好处是,要获取某个索引i处节点的左子节点,只需跳到索引(i + 1)* 2 - 1即可到达左子节点,而右子节点的索引是(i + 1) * 2
在这里插入图片描述
在这里插入图片描述
看看堆如何表示为数组,索引从1开始:将其与其初始树状表示进行比较。注意到位置i的节点的两个子节点分别位于位置i * 2i * 2 + 1吗?当索引从1开始时,这是正确的。

但由于在std::vector中,索引从0开始,因此节点在索引位置的左子节点位于以下位置:

size_t leftChild(size_t index)
{
    return (index + 1) * 2 - 1;
}

节点在索引位置的右子节点的位置为:

size_t rightChild(size_t index)
{
    return (index + 1) * 2;
}

三、使用STL创建和检查堆

现在已经清楚了堆作为数组的表示,接下来看看STL提供的一些算法,这些算法用于在数组内操作堆。

3.1、使用std::make_heap创建堆

首先,使用STL中的std::make_heap算法来构建堆。

如果有一系列可相互比较的对象,可以使用std::make_heap将这个范围重新排列成一个最大堆。示例代码:

std::vector<int> numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

std::make_heap(begin(numbers), end(numbers));

for (int number : numbers)
{
    std::cout << number << ' ';
}

这段代码输出了重新排列后的数字序列:

9 8 6 7 4 5 2 0 3 1

看起来熟悉吗?这就是作为数组实现的堆!

在这里插入图片描述

3.2、检查堆属性

接下来,可以使用std::is_heap来检查给定的集合是否以数组的形式实现了最大堆。

std::is_heap(begin(numbers), end(numbers))

如果集合是最大堆,则返回true,否则返回false。例如,在调用std::make_heap之前,对于前面的示例,它会返回false,在之后会返回true

有可能集合的开始部分结构化为堆。在这种情况下,std::is_heap_until将返回指向不满足堆属性的集合的第一个位置的迭代器。

auto heapUntil = std::is_heap_until(begin(numbers), end(numbers))

例如,如果集合是堆,std::is_heap_until返回集合的结束。如果第一个元素小于第二个元素,它将返回自堆属性从一开始就被破坏以来的第一个位置。

四、总结

通过本文的阅读,可以系统地了解了C++中堆的基础知识及其在STL中的应用。从堆的概念和最大堆的特性开始,深入探讨了堆的实现方式,介绍了将堆表示为数组的方法,并解释了这种表示方法的优势。随后,介绍了使用STL中的std::make_heap算法来创建堆的方法,以及使用std::is_heapstd::is_heap_until算法来检查堆属性的方式。通过具体的示例代码和图示,读者可以更直观地理解堆的构建和检查过程。
在这里插入图片描述

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: C11是C语言的最新标准,相对于以前的版本增加了许多新功能。《深入理解C 11》是一本介绍C11标准的书,帮助读者更好地理解C11语言的新特性和用法。这本书不仅适合想要学习C11的开发者,还适合已经掌握C语言基础,想要扩展自己技能的编程人员。 《深入理解C 11》一书包含了丰富的例子和解释,涵盖了C11标准的许多新功能,如多线程和原子操作、内存模型、泛型和灵活数组、_Generic宏、静态断言、noreturn等。这些新特性提高了C语言的灵活性、可读性和可维护性。 本书还讨论了内存管理、文件操作、指针、函数指针、结构体和联合体、枚举类型等C语言的常用部分,并对C11新增的相关特性给予了讲解,让读者在学习过程更好地感受到新特性的优越性。 综上所述,《深入理解C 11》是一本非常有价值的书,可以帮助读者在C语言开发更好地应用C11的新特性,提高代码的质量和效率。同时,这本书也是一本值得收藏的参考书,极大地丰富了读者关于C语言的知识储备。 ### 回答2: PDF文档《深入理解C11》是一本关于C11语言的详细介绍与讲解,它包含了C11语言的基础知识、高级特性、编程技巧等方面的内容,涵盖了C语言从入门到精通的全过程。 在《深入理解C11》这本书,首先介绍了C语言的基础知识,如变量、常量、运算符、表达式、控制结构等。这些内容对于初学者来说非常重要,因为它们是C语言编程的基础。 接着,该书详细介绍了C11语言的高级特性,如泛型、可变参数、内存管理、并发编程等。这些特性为程序员编写高效、灵活、可扩展的代码提供了强大的支持,能够帮助程序员更加高效地解决实际问题。 最后,该书还介绍了一些实用的编程技巧和工具,如调试技巧、优化技巧、代码审查、版本控制等。这些内容可以让程序员更加高效地编写、调试和维护代码,提高自己的编程水平。 总之,深入理解C11 PDF文档是对于C语言学习和应用的一本权威指南,在这本书,读者可以学习到C语言的重要概念、高级特性以及实用的编程技巧,能够帮助程序员更好地理解C语言应用实际的编程工作。 ### 回答3: C++ 11 是C++编程语言的更新版本,它是一个重要的改进和扩展,旨在使C++更加现代,更加高效和更加易用。深入理解C++ 11 PDF是一本非常好的C++ 11 学习和参考资料,非常详细地介绍了这种新版本的各种特性和优点。这本书从C++ 11 新特性的基础开始,涵盖了所有C++11新语言、标准库和STL组件。 这本书探讨了许多改进和扩展的细节,比如多线程,类别枚举,lambda表达式、常量表达式、右值引用、类型推断及其它特性。它为读者提供了一种深入了解C++ 11 标准的方式,并通过一些实例和演示帮助读者理解各个特性和技术的实际应用。此外,这本书还包括了一些关于C++ 11语言规范和标准库的细节。 深入理解C++ 11 PDF对于 C++ 编程的学习者,特别是那些希望利用C++ 11新特性来提高代码性能和可靠性的专业人员来说是一个宝贵的资料。掌握 C++ 11 的新语言特性、标准库和STL组件可以让您编写更简洁,更可读性和更高效的代码,并增强您的编程技能和能力。总之,这本书是C++11的权威指南,值得一读。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值