[算法(algorithms)、容器(containers)、函数(functions)、迭代器(iterators)] - STL |
使用using namespace std;声明命名空间; 也可手动注明std命名空间,std::; |
标准库指的是C++标准委员会定义的一整套标准化的类、函数和对象,旨在提供一致性和可移植性。 C++标准库通常分为两个主要部分:标准库和STL。 |
注:C++ 标准库则是在 C 标准库的基础上进行了扩展和改进,包含了 C 标准库的所有内容,引入了命名空间和模板等特性,提供了更丰富功能。 |
一、容器(containers)
1、array(数组)
头文件 | #include <array> | |
定义 | array<int,5> myarray = {1,2,3,4,5}; array<int,5> otherarray = myarray; | |
初始化 | std::array<double, 10> values {}; std::array<double, 10> values {0.5,1.0,1.5,,2.0}; | |
访问 | 可通过下标运算符[]对元素进行操作,还可以通过at/front/back进行操作 for (int i = 0; i < 5; i++) { cout << setw(10) << << myarray.at(i) << endl; } | |
遍历 | 可以通过正向和反向迭代器对元素进行遍历 for (auto it = myarray.begin(); it != myarray.end();++it) { cout << *it << endl; } | |
方法函数 | begin() | 返回指向容器中第一个元素的随机访问迭代器 |
end() | 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用 | |
rbegin() | 返回指向最后一个元素的随机访问迭代器 | |
rend() | 返回指向第一个元素之前一个位置的随机访问迭代器 | |
crbegin() | rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素 | |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素 | |
size() | 返回容器中当前元素的数量,其值始终等于初始化 array类的第二个模板参数 N | |
max_size() | 返回容器可容纳元素的最大数量,其值始终等于初始化 array类的第二个模板参数 N | |
empty() | 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快 | |
at(n) | 返回容器中n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出out_of_range 异常 | |
front() | 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器 | |
back() | 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器 | |
data() | 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能 | |
fill(val) | 将val 这个值赋值给容器中的每个元素 | |
array1.swap(array2) | 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型 |
2、vector(动态数组)
vector为可变长数组(动态数组),定义的vector数组可以随时添加数值和删除元素; | ||
头文件 | #include <vector> | |
定义 | // 方式一:一维可变长数组 vector<int>num; // 定义一个名为num的存int数据的一维数组 vector<double>num; // 定义一个名为num的存double数据的一维数组 vector<node>num; // node是结构体类型 // 方式二:二维可变长数组 vector<int>num[5]; // 定义可变长二维数组 // 注意:行是不可变的(只有5行),而列可变可以在指定行添加元素 // 第一维固定长度为5,第二维长度可以改变 // 定义一个行和列均可变的二维数组 vector<vectot<int>>num; | |
初始化 | // 1.附加长度 vector<int >v(n); // 定义一个长度为n的数组,动态定义 // 2.附加长度和初始值 vector<int>v(n,0); // 所有的元素均为0 | |
访问 | // 方式一:单个访问,假设num数组中已经有了5个元素 cout<<num[4]<<"\n"; // 输出第五个数据 // 一二维可变数组和普通数组的访问方法一样 // 方式二:遍历 for(int i = 0;i < num.size();i++) cout<<num[i]<<" "; // 下标范围在[0,num.size()),前开后闭 // 方式三:智能指针 for(auto i : num) cout<<i<<" "; | |
方法函数 | 相关方法函数如下:c指定为数组名称 | |
c.begin() | 返回首元素的迭代器(通俗来说就是地址) | |
c.end() | 返回最后一个元素后一个位置的迭代器(地址) | |
c.front() | 返回第一个数据 | |
c.back() | 返回最后一个数据 | |
c.push_back(element) | 在尾部加一个数据 O(1) | |
c.pop_back() | 删除最后一个数据 O(1) | |
emplace() | 在指定的位置直接生成一个元素 | |
emplace_back() | 在序列尾部生成一个元素,通过完美转发传递给元素构造函数的参数来直接构造元素,不需要创建临时对象,因此可以避免一次拷贝或移动操作,通常在性能上优于 push_back() | |
c.empty() | 判断是否为空,为空返回真,反之返回假 | |
c.size() | 返回实际数据个数(unsigned类型) O(1) | |
c.capacity () | 返回容器存储数据的个数 | |
c.reserve (n) | 设置capacity大小,减少内存浪费; reserve函数能够节省空间,且能够减少内存开辟次数,但是需要提前知道开辟空间大小; | |
c.resize(n,v) | 改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0 | |
c.erase(first,last) | 删除[first,last)的所有元素 删除指定索引元素: vec.erase(vec.begin() + index); | |
c.clear() | 清除元素个数 O(N),N为元素个数; size将变成0,但是容量capacity并未发生改变,clear只是删除数据,并未释放vector的内存 如果想要清空vector的元素,使用clear,如果想要释放vector的容量,可以使用swap: vector<A>().swap(vec); vec.swap(vector<A>()); | |
c.insert(it,x) | 向任意迭代器it插入一个元素x O(N), 例:c.insert(c.begin()+2,-1) 将-1插入c[2]的位置 | |
array1.swap(array2) | 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型 |
3、stack(栈)
栈为数据结构的一种,是STL中实现的一个先进后出,后进先出的容器; 就像火车进入没有出口的隧道一样,隧道是stack栈容器,火车车厢是入栈元素,火车头先进去,火车尾最后进隧道,当火车倒出来时,火车尾最先出来,火车头最后出来,所有的元素满足先进后出的规则; 注意:栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中; | ||
头文件 | #include <stack> | |
定义 | stack<int>sta; stack<string>sta; stack<node>sta; // node是结构体类型 | |
方法函数 | push() | 压栈,增加元素 O(1) |
pop() | 移除栈顶元素 O(1) | |
top() | 取得栈顶元素(但不删除)O(1) | |
empty | 检测栈内是否为空,空为真 O(1) | |
size() | 返回stack内元素的个数 O(1) |
4、queue(队列)
队列是一种先进先出的数据结构; 比喻性的描述可为 一条两端通透的隧道,火车车厢先进就先出,后进就后出; | ||
头文件 | #include <queue> | |
定义 | queue<int>q; | |
方法函数 | front() | 返回队首元素 O(1) |
back() | 返回队尾元素 O(1) | |
push() | 尾部添加一个元素副本 进队O(1) | |
pop() | 删除第一个元素 出队 O(1) | |
size() | 返回队列中元素个数,返回值类型unsigned int O(1) | |
empty() | 判断是否为空,队列为空,返回true O(1) |
5、deque(双端队列)
首尾都可插入和删除的队列为双端队列; 注意:deque可以进行排序,只能进行从小到大的排序; | ||
头文件 | #include <deque> | |
定义 | deque<int>dq; | |
方法函数 | back()/front() | 访问(不删除)后/前端元素 |
push_back(x)/push_front(x) | 把x压入后/前端 | |
pop_back() /pop_front() | 删除后/前端元素 | |
erase(iterator it) | 删除双端队列中的某一个元素 | |
erase(iterator first,iterator last) | 删除双端队列中[first,last)中的元素 | |
empty() | 判断deque是否空 | |
size() | 返回deque的元素数量 | |
clear() | 清空deque |
6、list(链表)
头文件 | #include <list> | |
定义 | list<int> a; // list的构造函数 list<int>a{1,2,3} // 声明一个n个元素的列表,每个元素都是0 list<int>a(n) // 声明一个n个元素的列表,每个元素都是m list<int>a(n, m) // 声明一个列表,其元素初值来源于由区间所指定序列中元素,first和last是迭代器 list<int>a(first, last) | |
方法函数 | begin()/end() | 通过调用list容器的成员函数begin()得到一个指向容器起始位置的iterator,可以调用list容器的end()函数来得到list末端下一位置 |
push_back(x)/push_front(x) | 使用list的成员函数push_back和push_front插入一个元素到list中。其中push_back()是从list的末端插入,而push_front()是从list的头部插入 | |
pop_back() /pop_front() | 删除后/前端元素 | |
empty() | 判断list是否为空 | |
erase(iterator first,iterator last) | 删除双端队列中[first,last)中的元素 | |
empty() | 判断deque是否空 | |
resize() | 调用resize(n)将list的长度改为只容纳n个元素,超出的元素将被删除。如果n比list原来的长度长,那么默认超出的部分元素置为0。也可以用resize(n, m)的方式将超出的部分赋值为m | |
clear() | 清空list中的所有元素 | |
front()/back() | 通过front()可以获得list容器中的头部元素,通过back()可以获得list容器的最后一个元素。注意:当list元素为空时,这时候调用front()和back()不会报错。因此在编写程序时,最好先调用empty()函数判断list是否为空,再调用front()/back()函数 | |
pop_back()/pop_front() | 使用pop_back()可以删掉尾部第一个元素,pop_front()可以删掉头部第一个元素。注意:list必须不为空,如果当list为空的时候调用pop_back()和pop_front()会使程序崩掉 | |
assign() | assign(n, val):将a中的所有元素替换成n个val元素 a.assign(b.begin(), b.end()) list<int>a{6,7,8,9}; list<int>b{1,2,3,4,5}; b.assign(a.begin(),a.end()); // b中的元素变为6,7,8,9 | |
swap() | 交换两个链表。a.swap(b)和swap(a, b),都可以完成a链表和b链表的交换 | |
reverse() | 可以实现list的逆置 | |
merge() | a.merge(b) 调用结束后b变为空,a中元素包含原来a和b的元素 | |
insert() | 在指定位置插入一个或多个元素 | |
erase() | 删除一个元素或一个区域的元素 | |
remove() | 从list中删除元素 |
7、pair(双元素模板结构)
pair只含有两个元素,可以看作是只有两个元素的结构体; 应用: 代替二元结构体; 作为map键值对进行插入; | |
头文件 | #include <utility> |
定义 | // 带初始值 pair<string,int>p("wangyaqi",1); // 不带初始值 pair<string,int>p; p = {"wang",18}; |
访问 | pair<int,int>p[20]; for(int i=0;i<20;i++) { // 和结构体类似,first代表第一个元素,second代表第二个元素 cout<<p[i].first<<" "<<p[i].second; } |
8、map/unordered_map(关联映射)
template < class Key, // unordered_map::key_type class T, // unordered_map::mapped_type class Hash = hash<Key>, // unordered_map::hasher class Pred = equal_to<Key>, // unordered_map::key_equal class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type > class unordered_map; map:按照键的顺序从小到大自动排序,内部实现为红黑树; unordered_map:内部实现为哈希表,以便通过键值快速访问元素。 | ||
头文件 | #include <map> | |
定义 | map<string,string>mp; map<string,int>mp; map<int,node>mp; // node是结构体类型 | |
方法函数 | mp.find(key) | 返回键为key的映射的迭代器 O(logN) 注意:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器 使用示例: if (monitoredMap.find(path) == monitoredMap.end()) |
mp.erase(it) | 删除迭代器对应的键和值O(1) | |
mp.erase(key) | 根据映射的键删除键和值 O(logN) | |
mp.erase(first,last) | 删除左闭右开区间迭代器对应的键和值 O(last-first) | |
mp.size() | 返回映射的对数 O(1) | |
mp.clear() | 清空map中的所有元素 O(N) | |
mp.insert() | 插入元素,插入时要构造键值对 | |
mp.empty() | 如果map为空,返回true,否则返回false | |
mp.begin() | 返回指向map第一个元素的迭代器(地址) | |
mp.end() | 返回指向map尾部的迭代器(最后一个元素的下一个地址) | |
mp.rbegin() | 返回指向map最后一个元素的迭代器(地址) | |
mp.rend() | 返回指向map第一个元素的迭代器(地址) | |
添加元素 | 方式一: mp["学习"] = "看书"; mp["玩耍"] = "打游戏"; 方式二(插入元素构造键值对): mp.insert(make_pair("vegetable","蔬菜")); 方式三: mp.insert(pair<string,string>("fruit","水果")); 方式四: mp.insert({"hahaha","wawawa"}); | |
访问元素 | mp["菜哇菜"] = "强哇强"; cout<<mp["菜哇菜"]<<"\n"; // ---------------------------------------遍历访问 方式一(迭代器访问): map<string,string>::iterator it; for(it=mp.begin();it!=mp.end();it++) { // 键 值 // it是结构体指针访问所以要用 -> 访问 cout<<it->first<<" "<<it->second<<"\n"; // *it是结构体变量 访问要用 . 访问 // cout<<(*it).first<<" "<<(*it).second; } 方式二(智能指针访问): for(auto i:mp) cout<<i.first<<" "<<i.second<<endl; // 键,值 方式三(对指定单个元素访问): map<char,int>::iterator it = mp.find('a'); cout<<it -> first <<" "<< it->second<<"\n"; |
9、multimap(关联映射-键对应多值)
multimap容器和map容器的区别在于,multimap容器中可以同时存储多个键相同的键值对; template < class Key, // 指定键(key)的类型 class T, // 指定值(value)的类型 class Compare = less<Key>, // 指定排序规则 class Alloc = allocator<pair<const Key,T> > // 指定分配器对象的类型 > class multimap; 和map容器一样,其中后2个参数都设有默认值,大多数时候只需设置前2个参数的值,有时候会用到第3个参数,最后一个参数几乎不会用到; | ||
头文件 | #include <map> | |
定义 | multimap<string, int> mymultimap; // 在创建multimap容器的同时,也可以进行初始化 multimap<string, int> mymultimap{{"penny",1},{"leonard",2},{"sheldon",3}}; // 借助pair类模板的构造函数生成各个pair类型的键值对 multimap<string,int> mymultimap{ pair<string,int>{"penny",1}, pair<string,int>{"leonard",2}, pair<string,int>{"sheldon",3} }; // 调用make_pair()函数,生成键值对元素;然后创建并初始化multimap容器 multimap<string,int> mymultimap{ make_pair("penny", 1), make_pair("leonard",2), make_pair("sheldon",3) }; // 拷贝(复制)构造函数,也可以初始化新的multimap容器 multimap<string,int> newmultimap(mymultimap); // 升序(默认) multimap<string,int> mymultimap{{"penny",1},{"leonard",2}}; multimap<string,int,less<string>> mymultimap{{"penny",1},{"leonard",2}}; // 降序 multimap<string,int,greater<string>> mymultimap{{"penny",1},{"leonard",2}}; | |
方法函数 | empty() | 若容器为空,则返回true,否则返回false |
size() | 返回当前multimap容器中键值对的个数 | |
max_size() | 返回multimap容器所能容纳的键值对的最大个数,不同的操作系统,其返回值亦不同 | |
count(key) | 在当前multimap容器中,查找键为key的键值对的个数并返回 | |
begin() | 返回指向容器中第一个(已排好序的第一个)键值对的双向迭代器 | |
end() | 返回指向容器中最后一个元素(已排好序的最后一个)所在位置的后一个位置的双向迭代器 | |
rbegin() | 返回指向容器中最后一个(已排好序的最后一个)元素的反向双向迭代器 | |
rend() | 返回指向容器中第一个(已排好序的第一个)元素所在位置的前一个位置的反向双向迭代器 | |
cbegin() | 和begin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对 | |
cend() | 和end()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对 | |
crbegin() | 和rbegin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对 | |
crend() | 和rend()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对 | |
find(key) | 在map容器中查找键为key的键值对,若成功找到,则返回指向该键值对的双向迭代器;若未找到,则返回和end()方法一样的迭代器 | |
lower_bound(key) | 返回一个指向当前map容器中第一个大于或等于key的键值对的双向迭代器 | |
upper_bound(key) | 返回一个指向当前map容器中第一个大于key的键值对的双向迭代器 | |
equal_range(key) | 返回一个pair对象(包含2个双向迭代器),其中pair.first和lower_bound()方法的返回值等价,pair.second和upper_bound()方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为key的键值对(map容器键值对唯一,因此该返回最多包含一个键值对) | |
insert() | 向multimap容器中插入键值对 | |
emplace() | 在当前multimap容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高 | |
emplace_hint() | 在本质上和emplace()在multimap容器中构造新键值对的方式是一样的,不同之处在于,必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数 | |
erase() | 删除multimap容器指定位置、指定键(key)值或者指定区域内的键值对 | |
clear() | 清空multimap容器中的所有键值对 | |
swap() | 交换2个multimap容器中存储的键值对,操作的2个键值对的类型必须相同 |
二、迭代器(iterators)
迭代器提供顺序访问一个聚合对象中各个元素方法, 而又不需暴露该对象的内部表示; |
迭代器实际上是一个指针,每种容器都有自己的迭代器,但是所有的迭代器的接口都是一样的,即所有的迭代器的操作方法都是一样的; |
1、迭代器类型
Input Iterator:只能单步向前迭代元素,不允许修改由该类迭代器引用的元素; |
Output Iterator:该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权力; |
Forward Iterator:该类迭代器可以在一个正确的区间中进行读写操作,拥有Input Iterator的所有特性,和Output Iterator的部分特性,以及单步向前迭代元素的能力; |
Bidirectional Iterator:该类迭代器是在Forward Iterator的基础上提供了单步向后迭代元素的能力; |
Random Access Iterator:该类迭代器能完成上面所有迭代器的工作,它自己独有的特性就是可以像指针那样进行算术计算,而不是仅仅只有单步向前或向后迭代; |
2、迭代器操作[begin,end)
所有迭代器 | p++:后置自增迭代器 |
++p:前置自增迭代器 | |
输入迭代器 | *p:复引用迭代器,作为右值 |
p=p1:将一个迭代器赋给另一个迭代器 | |
p==p1:比较迭代器的相等性 | |
p!=p1:比较迭代器的不等性 | |
输出迭代器 | *p:复引用迭代器,作为左值 |
p=p1:将一个迭代器赋给另一个迭代器 | |
正向迭代器 | 提供输入输出迭代器的所有功能 |
双向迭代器 | --p:前置自减迭代器 |
p--:后置自减迭代器 | |
随机迭代器 | p+=i:将迭代器递增i位 |
p-=i:将迭代器递减i位 | |
p+i:在p位加i位后的迭代器 | |
p-i:在p位减i位后的迭代器 | |
p[i]:返回p位元素偏离i位的元素引用 | |
p<p1:如果迭代器p的位置在p1前,返回true,否则返回false | |
p<=p1:p的位置在p1的前面或同一位置时返回true,否则返回false | |
p>p1:如果迭代器p的位置在p1后,返回true,否则返回false | |
p>=p1:p的位置在p1的后面或同一位置时返回true,否则返回false |
3、容器支持迭代器类别
vector | 随机访问 |
deque | 随机访问 |
list | 双向 |
set | 双向 |
multiset | 双向 |
map | 双向 |
multimap | 双向 |
stack | 不支持 |
queue | 不支持 |
priority_queue | 不支持 |
三、算法(algorithms)
STL算法是对容器进行处理的函数; |
算法主要是由头文件<algorithm、<functional>、<numeric>组成: <algorithm>是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等; <functional>定义了一些模板类,用以声明函数对象; <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数; |
1、查找类算法
find(beg,end val) | 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较,当匹配时,结束搜索,返回该元素迭代器 #include <iostream> #include <vector> #include <algorithm> // 包含 std::find int main() { // 示例驱动器字母向量 std::vector<char> drives = {'A', 'B', 'C', 'E', 'F', 'G'}; char targetDrive = 'D'; // 你想查找的驱动器字母 // 使用 std::find 查找目标驱动器 auto it = std::find(drives.begin(), drives.end(), targetDrive); if (it != drives.end()) { std::cout << "驱动器 " << targetDrive << " 存在于列表中。" << std::endl; } else { std::cout << "驱动器 " << targetDrive << " 不存在于列表中。" << std::endl; } return 0; } |
find_end(beg,end,beg2,end2) | 在迭代区间[beg,end) 内查找与区间[beg2, end2) 内任意元素匹配的元素,然后返回迭代器,指向最后一个匹配的元素,如果找不到元素,则返回第一个范围的end迭代器 |
find_first_of(beg,end,beg2,end2) | 与find_ first_ of类似,区别:查找最后一个匹配的元素 |
find_if(beg,end,val) | 与find类似查找的不是一个值还是达成条件的值 |
2、排序类算法
sort(beg,end) | 区间[beg,end) 内元素按字典次序排列 int arr[] = {5, 2, 9, 1, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); std::sort(arr, arr + n); for (int i = 0; i < n; ++i) { std::cout << arr[i] << " "; } |
stable_ sort(beg, end, func) | 同上,不过保存相等元素之间的顺序关系 sort(RectArr.begin(), RectArr.end(), comp); bool comp(RotatedRect a, RotatedRect b) { return a.center.x < b.center.x; } 或者 sort(contours.begin(), contours.end(), [](const vector<Point>& a, const vector<Point>& b) { return contourArea(a) > contourArea(b); }); |
partial_ sort (beg,mid, end) | 将最小值顺序放在[beg,mid)内 |
random_ shuffle (beg,end) | 区间内元素随意排序 |
reverse (beg,end) | 将区间内元素反转 |
rotate(beg,mid ,end) | 将区间[beg, mid) 和[mid, end)旋转,使mid为新的起点 |
merge(beg, end, beg2. end2,nbeg) | 将有序区间[beg, end)和[beg2, end2)合 并到一个新的序列nbeg中,并对其排序 |
3、可变按序列算法
copy(beg,end, beg2) | 将迭代区间[beg, end) 元素复制到以beg2 开始的区间 |
transform (beg, end, beg2, func) | 功能同上,只是每个元素需要经过函数func处理 |
repl ace(beg, end, v1, v2) | 将区间[beg,end)内等于 v1的元素替换为v2 |
fill (beg,end, v) | 区间内元素都写入v |
fill_ _n(beg, n, v) | 从位置beg开始的n个元素写入v |
generate (beg,n,rand) | 向从beg开始的n个位置随机填写数据 |
remove (beg,end) | 移除区间[beg,end)内的元素, 注意:并不真正删除 |
unique(beg,end) | 剔除相邻重复的元素,注意:并不真正删除 |
4、关系算法
equal (beg, end, beg2,end2) | 判断两个区间元素是否相等 |
includes (beg,end,beg2,end2) | 判断[beg,end) 序列是否被第二个序列[beg2, end2) 包含 |
max_ element (beg, en) | 返回序列最大元素的位置 |
min_ element (beg, end) | 返回序列最小元素的位置 |
mismatch (beg,end,beg2,end2) | 查找两个序列中第- -一个不匹配的元素,返回一对iterator,标记第一个不匹配元素的位置 |
5、堆算法
make_ heap (beg,end) | 以区间[beg,end)内元素建立堆 |
pop_ heap (beg,end) | 重新排序堆,使第-一个与最后- 一个交换,并不真正弹出最大值 |
push_ heap(beg, end) | 重新排序堆,把新元素放在最后一个位置 |
sort_ heap(beg, end) | 对序列重新排序 |