1. 概述

堆(也叫优先队列),是一棵完全二叉树,它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶堆和小顶堆)。它常用于管理算法执行过程中的信息,应用场景包括堆排序,优先队列等

2. 堆的基本操作

A:用于表示堆的数组,下标从1开始,一直到n

PARENT(t):节点t的父节点,即floor(t/2)
RIGHT(t):节点t的左孩子节点,即:2*t
LEFT(t):节点t的右孩子节点,即:2*t+1
HEAP_SIZE(A):堆A当前的元素数目

3.  堆的应用

3.1  堆排序
堆的最常见应用是堆排序,时间复杂度为O(N lg N)。如果是从小到大排序,用大顶堆;从大到小排序,用小顶堆。

4. 总结

堆是一种非常基础但很实用的数据结构,很多复杂算法或者数据结构的基础就是堆,因而,了解和掌握堆这种数据结构显得尤为重要。

代码,具体看下面介绍:

#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;void print(int x){cout << x << ' ';
}int main(void){vector<int> v;v.push_back(3);v.push_back(9);v.push_back(6);v.push_back(3);v.push_back(12);v.push_back(17);v.push_back(20);for_each(v.begin(), v.end(), print); cout << endl;//建立堆make_heap(v.begin(), v.end());cout<<"创建堆,未排序之前"<<endl; for_each(v.begin(),v.end(),print);cout<<endl;//堆排序cout << "进行堆排序" << endl;sort_heap(v.begin(), v.end());for_each(v.begin(), v.end(), print); cout << endl;return 0;
}
#include<iostream>
#include<algorithm>
using namespace std;
void print(int x)
{cout<<x<<" ";
}
int main()
{int iArray[]={1,2,3,4,5};const int len=sizeof(iArray)/sizeof(int);cout<<"创建堆"<<endl;make_heap(iArray,iArray+len);for_each(iArray,iArray+len,print);//5 4 3 2 1cout<<endl;cout<<"出堆一次"<<endl;pop_heap(iArray,iArray+len);for_each(iArray,iArray+len,print);//4 3 2 1 5return 0;
}

默认创建的是大根堆。

-----------------------------------------------------------------------------------------------------------------------------

1. make_heap() :

对随机访问迭代器指定的一段元素重新排列,生成大顶堆。

vector<double>numbers{2.5,10.0,3.5,6.5,8.0,12.0,1.5,6.0};
make_heap(begin(numbers), end(numbers));//{12 10 3.5 6.5 8 2.5 1.5 6}

根节点是 12,10 和 3.5 是它的子节点。10 的子节点是 6.5 和 8,3.5 的子节点是 2.5 和 1.5。6.5 只有一个叶节点 6。

图 2 所示堆中的前 3 个元素是顺序递减的,但第 4 个元素却大于第 3 个元素。

priority_queue 可以提供堆没有的优势,它可以自动保持元素的顺序;但我们不能打乱 priority_queue 的有序状态,因为除了第一个元素,我们无法直接访问它的其他元素。

使用 make_heap() 创建的堆可以提供一些 priority_queue 没有的优势:

  1. 可以访问堆中的任意元素,而不限于最大的元素,因为元素被存储在一个容器中,就像是我们自己的 vector。这也提供了偶然破坏元素顺序的可能,但是总可以调用 make_heap() 来还原堆。
  2. 可以在任何提供随机访问迭代器的序列容器中创建堆。这些序列容器包括普通数组、string 对象、自定义容器。这意味着无论什么时候需要,都可以用这些序列容器的元素创建堆,必要时,可以反复创建。甚至还可以为元素的子集创建堆。

make_heap(),它有第 3 个参数来生成一个小顶堆。

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers), end(numbers), greater<>()); //{1.5 6 2.5 6.5 8 12 3.5 10}

2. push_back();

堆不是容器,而是组织容器元素的一种特别方式。

push_heap() 创建堆的方式可能会觉得有些奇怪。为了向堆中添加元素,首先可以用任何方法将元素附加到序列中。然后调用 push_heap() 来插入最后一个元素,为了保持堆的结构,这个元素会被重新排列到一个适当的位置。

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers),end(numbers));//{12 10 3.5 6.5 8 2.5 1.5 6}
numbers.push_back(11); // {12 10 3.5 6.5 8 2.5 1.5 6 11}
push_heap(begin(numbers), end(numbers));//{12 11 3.5 10 8 2.5 1.5 6 6.5}

push_back() 会在序列末尾添加元素,然后使用 push_heap() 恢复堆的排序。push_heap() 会因此认为最后一个元素是新元素,为了保持堆结构,会重新排列序列。

push_heap()  也可以用第 3 个参数来生成一个小顶堆。

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers), end(numbers), greater<>());//{1.5 6 2.5 6.5 8 12 3.5 10}
numbers.push_back(1.2);//{1.5 6 2.5 6.5 8 12 3.5 10 1.2}
push_heap(begin(numbers), end(numbers),greater<>());//{1.2 1.5 2.5 6 8 12 3.5 10 6.5}

3. pop_heap(),pop_back()

删除最大元素,先pop_heap() 后pop_back() ---看程序注解就明白了

vector<double> numbers{2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers),end(numbers));//{12 10 3.5 6.5 8 2.5 1.5 6}
pop_heap(begin(numbers),end(numbers));//{10 8 3.5 6.5 6 2.5 1.5 12}
numbers.pop_back();//{10 8 3.5 6.5 6 2.5 1.5}

pop_heap() 函数将第一个元素移到最后,并保证剩下的元素仍然是一个堆,再vector 的成员函数 pop_back() 移除最后一个元素。

pop_heap() 也可以用第 3 个参数

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers),end(numbers),greater<>());//{1.5 6 2.5 6.5 8 12 3.5 10}
pop_heap(begin(numbers), end(numbers),greater<>());//{2.5 6 3.5 6.5 8 12 10 1.5}
numbers.pop_back();//{2.5 6 3.5 6.5 8 12 10}

STL 提供了一个检查序列是否仍然是堆的方法

if(is_heap(begin(numbers),end(numbers)))
cout << "Great! We still have a heap.\n";
else
cout << "oh bother! We messed up the heap.\n";

is_heap() 也可以用第 3 个参数

is_heap(begin(numbers),end(numbers),greater<>())
vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers),end(numbers),greater<>());// {1.5 6 2.5 6.5 8 12 3.5 10}
pop_heap (begin (numbers),end(numbers),greater<>());//{2.5 6 3.5 6.5 8 12 10 1.5}
auto iter = is_heap_until(begin(numbers),end(numbers),greater<>());
if(iter != end(numbers))//end(numbers)为10,因为1.5已经不算是heap里面的数了
cout << "numbers is a heap up to "<< *iter << endl;

is_heap_until() 函数返回一个迭代器,指向第一个不在堆内的元素。这个代码段会输出最后一个元素的值 1.5,因为在调用 pop_heap() 后,这个元素就不在堆内了。如果整段元素都是堆,函数会返回一个结束迭代器,因此if语句可以确保我们不会解引用一个结束迭代器。如果这段元素少于两个,也会返回一个结束迭代器。这里还有另一个版本的 is_heap_until(),它有两个参数,以 less<> 作为默认断言。

输出:numbers is a heap up to 1.5

STL 提供的最后一个操作是 sort_heap(),它会将元素段作为堆来排序。如果元素段不是堆,程序会在运行时崩溃。这个函数有以两个迭代器为参数的版本,迭代器指向一个假定的大顶堆(用 less<> 排列),然后将堆中的元素排成降序。结果当然不再是大顶堆。下面是一个使用它的示例:

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers), end(numbers));//{12 10 3.5 6.5 8 2.5 1.5 6}
sort_heap(begin(numbers), send(numbers));//{1.5 2.5 3.5 6 6.5 8 10 12}

排序操作的结果不是一个大顶堆,而是一个小顶堆。

如图所示,尽管堆并不是全部有序的,但任何全部有序的序列都是堆。

第 2 个版本的 sort_heap() 有第 3 个参数,可以指定一个用来创建堆的断言。如果用断言 greater() 来创建堆,会生成一个小顶堆,对它进行排序会生成一个降序序列。排序后的序列不是小顶堆。下面的代码对此做了展示:

vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
make_heap(begin(numbers),end(numbers), greater<>());// {1.5 6 2.5 6.5 8 12 3.5 10}
sort_heap(begin(numbers), end(numbers), greater<>());//{12 10 8 6.5 6 3.5 2.5 1.5}

如最后一行注释中显示的那样,对小顶堆执行 sort_heap() 后,会变成一个大顶堆。

我们知道可以用定义在 algorithm 头文件中的函数模板 sort() 来对堆排序,那么为什么还需要 sort_heap() 函数?sort_heap() 函数可以使用特殊的排序算法,巧合的是它被叫作堆排序。这个算法首先会创建一个堆,然后充分利用数据的局部有序性对数据进行排序。sort_heap 认为堆总是存在的,所以它只做上面的第二步操作。充分利用堆的局部有序性可以潜在地使排序变得更快,尽管这可能并不是一直有用。

例子:

// Using a heap as a priority queue
#include <iostream>                              // For standard streams
#include <iomanip>                               // For  stream manipulators
#include <algorithm>                             // For heap support functions
#include <string>                                // For string class
#include <deque>                                 // For deque containerusing namespace std;// List a deque of words
void show(const std::deque<string>& words, size_t count = 5)
{if(words.empty()) return;                     // Ensure deque has elements// Find length of longest stringauto max_len = max_element(std::begin(words), end(words),[](const string& s1, const string& s2){return s1.size() < s2.size(); })->size();size_t n {count};for(const auto& word : words){cout << setw(max_len + 1) << word << " ";if(--n) continue;cout << endl;n = count;}cout << endl;}int main()
{deque<string> words;string word;cout << "Enter words separated by spaces, enter Ctrl+Z on a separate line to end:\n";while (true){if ((cin >> word).eof()){cin.clear();break;}words.push_back(word);}cout << "The words in the list are:" << endl;show(words);make_heap(begin(words), end(words));cout << "\nAfter making a heap, the words in the list are:" << endl;show(words);cout << "\nYou entered " << words.size() << " words. Enter some more:" << endl;while (true){if ((std::cin >> word).eof()){cin.clear();break;}words.push_back(word);push_heap(begin(words), end(words));}cout << "\nThe words in the list are now:" << endl;show(words);
}

输出

Enter words separated by spaces, enter Ctrl+Z on a separate line to end:
one two three four five six seven
^Z
The words in the list are:one    two  three   four   fivesix  sevenAfter making a heap, the words in the list are:two    one  three   four   fivesix  sevenYou entered 7 words. Enter some more:
eight nine ten twelve fifteen ninety forty fifty-three
^ZThe words in the list are now:two       twelve        three         nine          tensix        seven        eight         four         fiveone      fifteen       ninety        forty  fifty-three

这个示例在一个 deque 容器中创建了一个堆,这和之前的示例不同;这里也可以使用 vector 容器。show() 函数可以列出 deque<string> 容器中的所有单词。为了能够整齐地输出,单词都以比最大单词长度长 1 的固定宽度输出。可以使用定义在 algorithm 头文件中的 max_element() 函数来计算单词最大长度。

通过使用提供的比较函数,max_element() 会返回一个指向最大元素的迭代器。前两个参数是指定序列范围的迭代器。第 3 个参数是一个用于比较运算的 lambda 表达式。

注意,max_dement() 函数需要定义小于而不是大于运算,用来查找最大元素。比较函数的形式如下:

  1. bool comp(const T1& a,const T2& b);

大多数情况下,第一个参数和第二个参数的类型相同,但有时类型也可以不同。唯一的要求是,这个范围内的元素需要可以隐式转换为 T1、T2 类型。参数不需要指定为 const,但最好这样做。在任何情况下,比较函数都不能改变传给它的参数值。

lambda 表达式可以返回字符串的 size() 值的比较结果。max_element() 返回的迭代器指向最长的字符串,因此可以调用它的成员函数 size() 来将它的长度记录到 max_len 中。

用我们之前见过的方式从 cin 中读取单词。这里调用 cin 的成员函数 clear() 来清除 EOF 状态,这个状态是在输入 Ctrl+Z 时设置的。如果不调用 clear(),EOF 状态会继续保留,这样后面就无法再从标准输入流获取输入了。

读入一些单词序列后,通过调用 make_heap() 函数将 deque 容器中的内容排成堆。然后读取一些单词,在将每个单词添加到容器时,需要调用 push_heap() 来保持堆序。push_heap() 希望新元素被添加在容器的尾部;如果使用 push_front(),程序会因此崩溃,因为这时候堆是无效的。输出表明所有代码按预期工作。

当然,如果每次输入单词后,都使用 push_heap(),就不需要调用 make_heap()。该例展示了如何使用我们控制的底层容器来访问全部元素,并且保留它们,而不需要像使用优先级队列那样在使用前不得不先备份它。

c++ heap (堆)相关推荐

  1. [Java]Stack栈和Heap堆的区别(终结篇)[转]

    首先分清楚Stack,Heap的中文翻译:Stack-栈,Heap-堆. 在中文里,Stack可以翻译为"堆栈",所以我直接查找了计算机术语里面堆和栈开头的词语: 堆存储: hea ...

  2. JAVA Stack栈和Heap堆的区别(转)

          首先分清楚Stack,Heap的中文翻译:Stack-栈,Heap-堆. 在中文里,Stack可以翻译为"堆栈",所以我直接查找了计算机术语里面堆和栈开头的词语:  堆 ...

  3. java内存stack heap_java内存解析-------stack(栈)和heap(堆)的理解

    学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. ...

  4. 如何给女朋友讲明白:Java中Stack(栈)与Heap(堆)

    背景 Java中Stack(栈)与Heap(堆)是面试中被经常问到的一个话题. 有没有对Java中Stack(栈)与Heap(堆)烂熟于心的童鞋,请举手!!!(怎么没人举手-) 这个时候蜗牛哥的对象弱 ...

  5. Heap(堆结构/优先队列)-Swift实现

    特性 堆结构很像二叉树,堆也是一个近似树形结构,堆的每个节点也最多有左.右两个孩子,但是堆实质是存储在数组中的结构,所以他和二叉树只是近似的有某些共同的特性. 第一特性,堆结构是存储在数组中的. 堆通 ...

  6. STL 源码剖析 heap堆

    heap不属于STL容器的组件,属于幕后角色,是priority_queue的助手 priority_queue 允许用户以任何次序将任何元素推入容器内,但是取出的时候需要从优先级最高(也就是数值最高 ...

  7. STL系列之四 heap 堆

    下面再介绍STL中与堆相关的4个函数--建立堆make_heap(),在堆中添加数据push_heap(),在堆中删除数据pop_heap()和堆排序sort_heap(): 头文件 #include ...

  8. Java Heap堆分析

    一.堆直方图 减少内存使用时一个重要目标,在堆分析上最简单的方法是利用堆直方图.通过堆直方图我们可以快速看到应用内的对象数目,同时不需要进行完整的堆转储(因为堆转储需要一段时间来分析,而且会消耗大量磁 ...

  9. js stack栈与heap堆的区别与含义

    作为前端了解栈与堆是非常必要的,如果不能充分理解那么js的深拷贝.浅拷贝就没办法正确使用. 当然如果你是大学计算机专业相信你因该了解很透彻了,如果文章有不足之处请多多指教 一.栈与堆概念 栈(stac ...

最新文章

  1. 制药行业SAP项目里的那些MES系统
  2. LINUX文件、目录权限及相关操作命令
  3. JavaScript大杂烩9 - 理解BOM
  4. 【转知乎】人工智能会是泡沫吗?
  5. erlang rebar 配置mysql_Erlang Rebar 使用指南之四:依赖管理
  6. Lambda项目:迈向多核及超越
  7. rocketmq原理_彻底看懂RocketMQ事务实现原理
  8. 小米10预计春节后见 售价超3500元没悬念
  9. 10_文件包含漏洞(属于任意代码执行)
  10. verilog之按键消抖的理解
  11. ps aux 中的状态说明
  12. 有了RK Easywork轻松在线组装标准工作台
  13. 遥感计算机的分类原理,遥感图像的计算机分类
  14. 两直线平行交叉相乘_初中数学几何公式、定理梳理,太全了!老师都转发了!...
  15. 非法使用爬虫,一互联网公司被端,警方上门,23人被带走…
  16. 机构投资者大举入场,促使BTC上涨? | 一周问答热议
  17. 数据湖三剑客Delt Lake、Hudi、Iceberg详解
  18. mbr+ghost装黑苹果OS X 10.13
  19. 计算机为什么要学16进制,为什么人们通常用十六进制而不是二进制写计算机
  20. 福昕阅读器文档无法高亮的问题

热门文章

  1. AVR 定时器快速PWM模式使用
  2. Laravel Database——查询构造器与语法编译器源码分析 (上)
  3. mysql唯一索引的关键字_mysql中唯一索引的关键字是什么
  4. 关于盒子模型之 水平居中和垂直居中的问题
  5. 【ROS入门学习01| ROS命令行工具的使用】
  6. 求解矩阵方程耗时比较(直接求逆,Qr分解,LU分解)
  7. 时下人间道的生存法则
  8. arcgis直方图导出地图_利用Arcgis地图工具自动输出报告地图图纸
  9. 国内主流应用商城及其开放平台地址
  10. 计算机音乐数字乐谱牵丝戏,天谕手游牵丝戏乐谱代码分享