c++ STL容器的使用

make_heap()等的使用

c++ STL容器中heap的使用

本文介绍如何使用STL里的heap(堆)算法。第一次接触heap这种数据结构是在大学的数据结构教材上,它是一棵完全二叉树。在STL中,heap是算法的形式提供给我们使用的。包括下面几个函数:

  • make_heap: 根据指定的迭代器区间以及一个可选的比较函数,来创建一个heap. O(N)
  • push_heap: 把指定区间的最后一个元素插入到heap中. O(logN)
  • pop_heap: 弹出heap顶元素, 将其放置于区间末尾. O(logN)
  • sort_heap:堆排序算法,通常通过反复调用pop_heap来实现. N*O(logN)

C++11加入了两个新成员:

  • is_heap: 判断给定区间是否是一个heap. O(N)
  • is_heap_until: 找出区间中第一个不满足heap条件的位置. O(N)

因为heap以算法的形式提供,所以要使用这几个api需要包含 #include <algorithm>

heap相关算法的使用

make_heap

STL中的通过make_heap创建的堆,默认是大顶堆(max heap),即每个非叶子节点元素的值均不”小于”(默认使用<作为比较准则)其左右孩子节点。要改变堆的建立准则,可以自己制定一个比较函数,如下第二个版本的make_heap声明:

// 1
template< class RandomIt >
void make_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void make_heap( RandomIt first, RandomIt last, Compare comp );

示例代码:

默认的make_heap

vector<int> vi{6, 1, 2, 5, 3, 4};
printContainer(vi, "vi: ");             // vi: 6 1 2 5 3 4

make_heap(vi.begin(), vi.end());
printContainer(vi, "vi: ");             // vi: 6 5 4 1 3 2

需要注意的是,make_heap需使用随机迭代器来创建heap。

自己指定比较函数的make_heap

vector<int> v2{6, 1, 2, 5, 3, 4};
printContainer(v2, "v2 before make_heap: ");
make_heap(v2.begin(), v2.end(), greater<int>());
printContainer(v2, "v2 after make_heap: ");

输出:

v2 before make_heap: 6 1 2 5 3 4
v2 after make_heap: 1 3 2 5 6 4

这里使用了greater<int>()来代替默认的less<int>()来创建int类型的heap。可以按层次遍历的顺序把这个heap画出来,可以看到它跟默认情况刚好相反,会是一个小顶堆。

push_heap

// 1
template< class RandomIt >
void push_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void push_heap( RandomIt first, RandomIt last, Compare comp );

make_heap类似,push_heap也有两个版本,其中有一个版本可以指定堆的比较函数,并且也是以一对迭代器指定的区间来进行操作。

示例代码:

vector<int> v1{6, 1, 2, 5, 3, 4};
make_heap(v1.begin(), v1.end()); 
v1.push_back(200);
printContainer(v1, "before push_heap: ");        // before push_heap: 6 5 4 1 3 2 200
push_heap(v1.begin(), v1.end());
printContainer(v1, "after push_heap: ");         // after push_heap: 200 5 6 1 3 2 4

先用make_heap来构造一个堆,然后在容器末尾追加元素之后,把新的迭代器区间传给push_heap,这样新尾部元素也被添加到堆中。

注意:使用push_heap(f, l)的话,调用者需要确保[f, l-1)已经是一个堆. push_heap(f, l)仅仅会把*(l-1)插入到[f, l-1)这个区间形成的堆中,时间复杂度是O(logN).

即, STL书中所述:the caller has to ensure, on entry, the elements in the range [begin, end) are a heap(according to the same sorting criterion).

如果一开始不用make_heap处理,直接push_heap会怎样?

vector<int> v2{6, 1, 2, 5, 3, 4};
v2.push_back(200);
printContainer(v2, "v2 before push_heap: ");
push_heap(v2.begin(), v2.end());
printContainer(v2, "v2 after push_heap: ");

输出:

v2 before push_heap: 6 1 2 5 3 4 200
v2 after push_heap: 200 1 6 5 3 4 2

可以看出直接调用push_heap的结果并不是一个heap. 下面要提到的pop_heap也有同样的要求。

pop_heap

// 1    
template< class RandomIt >
void pop_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void pop_heap( RandomIt first, RandomIt last, Compare comp );
Swaps the value in the position first and the value in the position last-1 and makes the subrange [first, last-1) into a max heap. This has the effect of removing the first (largest) element from the heap defined by the range [first, last).

它的作用是:交换*first*(last-1), 然后把[first, last-1)建成一个max heap. 也就是说把堆顶的最大元素交换到区间尾,然后把除了尾部的元素的剩余区间重新调整成堆。

需要注意的是,调用者要保证,在调用pop_heap[first, last)已经是一个堆(使用相同的排序准则)。

vector<int> v1{6, 1, 2, 5, 3, 4};

make_heap(v1.begin(), v1.end());
printContainer(v1, "after make_heap: ");
pop_heap(v1.begin(), v1.end());
printContainer(v1, "after pop_heap: ");
auto largest = v1.back();
psln(largest);
v1.pop_back();
printContainer(v1, "delete largest: ");

输出:

after make_heap: 6 5 4 1 3 2
after pop_heap: 5 3 4 1 2 6
largest = 6
delete largest: 5 3 4 1 2

sort_heap

// 1
template< class RandomIt >
void sort_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void sort_heap( RandomIt first, RandomIt last, Compare comp );

sort_heap即经典的堆排序算法,通过每次弹出堆顶直到堆为空,依次被弹出的元素就组成了有序的序列了。STL中的priority_queue即使用heap的这个特性来实现。

使用sort_heap(f, l)处理过的区间因为已经有序,就不再是一个heap了。

vector<int> v1{6, 1, 2, 5, 3, 4};
printContainer(v1, "before sort_heap: ");       
make_heap(v1.begin(), v1.end());
sort_heap(v1.begin(), v1.end());
printContainer(v1, "after sort_heap: ");

输出:

before sort_heap: 6 1 2 5 3 4
after sort_heap: 1 2 3 4 5 6

注意:调用者仍需确保区间已经是一个堆。

is_heap

// (1)  (since C++11)
template< class RandomIt >
bool is_heap( RandomIt first, RandomIt last );
// (2)  (since C++11)
template< class RandomIt, class Compare >
bool is_heap( RandomIt first, RandomIt last, Compare comp );

示例:

vector<int> v1{6, 1, 2, 5, 3, 4};
psln(is_heap(v1.begin(), v1.end()));
pln("after make_heap");
make_heap(v1.begin(), v1.end());
psln(is_heap(v1.begin(), v1.end()));

输出:

is_heap(v1.begin(), v1.end()) = 0
after make_heap
is_heap(v1.begin(), v1.end()) = 1

is_heap_until

原型:

// (1)  (since C++11)
template< class RandomIt >
RandomIt is_heap_until( RandomIt first, RandomIt last );
// (2)  (since C++11)
template< class RandomIt, class Compare >
RandomIt is_heap_until( RandomIt first, RandomIt last, Compare comp );
vector<int> v1{6, 1, 2, 5, 3, 4};
auto iter = is_heap_until(v1.begin(), v1.end());
psln(*iter);        // *iter = 5    5 是第一个不满足heap条件的位置。
make_heap(v1.begin(), v1.end());
iter = is_heap_until(v1.begin(), v1.end());
ASSERT_TRUE(iter == v1.end());

总结

  1. 建堆,make_heap
  2. 堆操作:增加元素(push_heap),删除元素(pop_heap), 排序(sort_heap), 均要求区间已经是一个heap,并且是与当前操作使用相同的排序准则
  3. is_heap, is_heap_until当做辅助判断函数来用
  4. 所有的heap算法操作的区间都需要是随机迭代器组成

测试

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
// #include<functional>
using namespace std;

vector<int>heap;

template <class T>
bool myLess(T a,T b){
    return a<b;
}

template <class T>
struct Less{
    bool operator()(const T& a,const T& b)const{
        return a<b;
    }
};


void print_heap(vector<int>&heap){
    for(int i=0;i<heap.size();i++){
        cout<<heap[i]<<" ";
    }
    cout<<"\n";
}
int main(){
    vector<int> a({1,5,4,2,3});
    for(int i=0;i<a.size();i++){
        heap.push_back(a[i]);
    }
    // make_heap(heap.begin(),heap.end());// 默认最大堆
    {
        make_heap(heap.begin(),heap.end(),less<>());// 最大堆
        // print_heap(heap);
        // make_heap(heap.begin(),heap.end(),greater<>());// 最小堆

        // make_heap(heap.begin(),heap.end(),myLess<int>);// 最大堆
        // make_heap(heap.begin(),heap.end(),Less<int>());// 最大堆
    }
    heap[4]=10;// 直接更改里面的元素不会进行堆的调整
    // print_heap(heap);

    push_heap(heap.begin(),heap.end());     // push_heap()并不会去主动调整所有的元素,只会调整最后一个元素,自底向上的形成堆
    print_heap(heap);

    heap.push_back(11);
    push_heap(heap.begin(),heap.end());     
    print_heap(heap);

    pop_heap(heap.begin(),heap.end());      // pop出堆顶的第一个元素放到vector<>的末尾,需要保证[heap.begin(),heap.end())已经是一个堆
    pop_heap(heap.begin(),heap.end()-1);
    print_heap(heap);

    make_heap(heap.begin(),heap.end());// sort_heap之前需要保证是一个堆,最好make_heap()
    sort_heap(heap.begin(),heap.end());// 堆排序
    print_heap(heap);

    // is_heap()
    cout<<is_heap(heap.begin(),heap.end())<<endl;
    // is_heap_until(heap.begin(),heap.end()); // 返回迭代器,*iter是元素的值
    auto iter=is_heap_until(heap.begin(),heap.end());
    auto iter1=heap.begin();
    cout<<*iter1<<endl;
    cout<<*is_heap_until(heap.begin(),heap.end())<<endl;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: C++ STL容器中的栈(stack)是一种后进先出(LIFO)的数据结构。栈的基本操作包括入栈(push)、出栈(pop)、查看栈顶元素(top)和判断栈是否为空(empty)。栈可以用数组或链表实现,但使用STL容器可以更方便地实现栈的操作。在STL中,栈是由适配器(adapter)stack实现的,可以使用push、pop、top和empty等成员函数来操作栈。栈的应用场景包括函数调用、表达式求值、括号匹配等。 ### 回答2: 栈(stack)是C++ STL(标准模板库)中的一个容器,是一个后入先出(Last In, First Out,LIFO)的数据结构。堆栈的基本操作是退栈(Pop)和入栈(Push),即在栈顶插入和删除元素。除了这两个基本操作,堆栈还提供了访问栈顶元素的方法,即Top()。 堆栈可以通过STL中的std::stack<T>来使用,其中T是元素的类型。堆栈的定义非常简单,只需要使用一个std::stack<T>对象即可。在使用之前,需要包含头文件<stack>。 堆栈的主要特性是插入和删除元素的时间复杂度为常数时间O(1),因为栈只需要在栈顶进行操作。堆栈一般用于实现递归、表达式求值、内存分配等。例如,在递归深度优先搜索中,可以使用堆栈来存储遍历的路径。 堆栈的操作非常简单,以下是常用的操作列表: 1. push():将一个元素插入栈顶。 2. pop():删除栈顶元素。 3. top():返回栈顶元素。 4. empty():判断堆栈是否为空。 5. size():返回堆栈中元素的个数。 下面是一个简单的堆栈的例子,可以更好地理解堆栈的基本操作: #include <iostream> #include <stack> using namespace std; int main() { stack<int> s; // 定义一个int类型的栈 s.push(10); // 将10入栈 s.push(20); // 将20入栈 s.push(30); // 将30入栈 while (!s.empty()) { cout << s.top() << " "; // 输出栈顶元素 s.pop(); // 删除栈顶元素 } return 0; } 在上面的例子中,我们首先定义了一个堆栈s,然后在堆栈s中依次插入了三个元素10、20和30。接下来使用while循环,栈顶元素依次输出,同时删除栈顶元素,直到堆栈为空。由于堆栈是后进先出的,所以输出的顺序是30、20和10。 总之,堆栈是一个非常常用的数据结构,STL中的栈(stack)提供了非常方便的使用,可以减轻我们对堆栈数据结构进行操作的负担,提高代码的可读性和复用性。 ### 回答3: 栈(stack)是 C++ STL(Standard Template Library)中常见的一种容器数据结构,它可以在一端进行元素的插入和删除操作,遵循“后进先出”(LIFO,Last-In-First-Out)的原则。栈的操作不需要访问元素中间的部分,只需要在栈顶执行操作,保证了操作效率。栈可以用数组或链表等数据结构实现,但 C++ STL 提供了封装好的栈容器使用起来方便且安全。 C++ STL 中栈容器的定义方式为:`std::stack`。栈默认情况下使用双端队列(deque)实现,用户也可以指定其他底层容器,如 vector、list 等。可以使用`push()`向栈顶插入元素,使用`pop()`弹出栈顶元素,使用`top()`获取栈顶元素。栈的元素个数可以使用`size()`来获取,判断栈是否为空可以使用`empty()`,在栈容器中查找某个元素的功能则不支持。 在实际应用中,栈容器可以用来实现函数的递归调用、表达式求值、括号匹配等操作。例如,可以使用栈来判断一个字符串中的括号是否匹配,具体做法是将左括号入栈,遇到右括号时弹出栈顶元素检查是否为相应的左括号。如果不匹配或者栈已经为空,则括号不匹配;如果字符串中所有的括号都匹配,则最后栈为空。 总之,栈作为一种容器数据结构,在实际应用中有着广泛的应用场景,C++ STL 提供的封装好的栈容器,具有使用方便、效率高等特点,可以帮助我们更快更方便地实现各种数据处理和算法设计。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

youngsea8

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值