STL(标准模板库)--容器(一)

1. 序列容器

1.1 array

  • array< T,N >:数组容器,是一个长度固定的序列,有 N 个 T 类型的对象,不能增加或删除元素。
    初始化:
    std::array<int,100> data;
    std::array<int, 100> data {};
    std::array<double, 10> values {0.5, 1.0, 1.5, 2.0};
    //通过调用数组对象的成员函数 fill(),可以将所有元素设成给定值。
    values.fill(3.1415926);
    
    访问元素
    values[4] = values[3] + 2.O*values[1];
    //当传给 at() 的索引是一个越界值时,这时会抛出 std::out_of_rang 异常。
    values.at(4) = values.at(3) + 2.O*values.at(1);
    
    迭代器用法
    unsigned int h {min_ht};
    auto first = height_ins.begin();
    auto last = height_ins.end() ;
    while (first != last)
    {
     	*first++ = h;
     	h  +=  ht_step;
    }
    

1.2 vector

  • vector< T >:向量容器。是一个长度可变的序列,用来存放T类型的对象。必要时,可以自动增加容量,但只能在序列的末尾高效地增加或删除元素。
    初始化:

    //初始化为20个0
    std::vector<double> values(20,0);
    //初始化为1个20
    std::vector<double> values{20};
    //words_copy 被 words 数组容器中的元素初始化
    std::array<std :: string, 5> words {"one", "two","three", "four", "five"};
    std::vector<std::string> words_copy {std::begin(words) , std::end(words)};
    

    容量和大小

    访问元素

    std::vector<double> v;
    v.reserve(20);
    std::vector<double> values (20);
    values[0] = 3.14159;
    values[1] = 5.0;
    values.at(2) = 2.0*values[0]*values[1];
    //vector 的成员函数 front() 和 back() 分別返回序列中第一个和最后一个元素的引用
    std::cout << values.front () << std::endl; // Outputs 3.14159
    //因为成员函数 front() 和 back() 返回的是引用,所以它们可以出现在赋值运算符的左边。
    values.front() = 2.71828;
    //成员函数 data() 返回一个指向数组的指针,它在内部被用来存储元素。
    auto pData = values.data();
    

    迭代器用法
    vector 容器的迭代器是随机访问迭代器。当然,也可以通过全局函数获取它们。vector 有成员函数 push_back(),所以能够通过使用 back_insert_iterator 来添加新的值。

    从前面章节了解到,可以通过调用全同的 back_inserter() 函数来获取一个后向插入迭代器。无法在 vector 奔器中使用 front_insert_iterator,这需要 vector 有成员函数 push_front(),但是 vector 容器并没有定义这个函数。

    可以通过演示如何用 copy() 算法来添加元素,向你展示怎样在 vector 中使用后向插入迭代器。copy() 的头两个参数是两个迭代器,指定了复制元素的范围,第三个参数指定了这些元素存放的位置。头两个参数要求是输入迭代器,所以可以接受任何其他类别的迭代器;显然第三个参数必须是一个输出迭代器。这里有一个示例:

    std::vector<double> data {32.5, 30.1, 36.3, 40.0, 39.2};
    std::cout << "Enter additional data values separated by spaces or Ctrl+Z to end:" << std::endl;
    std::copy(std::istream_iterator<double>(std::cin),std::istream_iterator<double>(), std::back_inserter(data));
    std::copy(std::begin(data), std::end(data),std::ostream_iterator<double> (std:: cout," "))
    

    添加元素

    //push_back()
    std::vector<double> values;
    values.push_back(3.1415926);
    //empalce_back()
    std::vector<std::string> words;
    words.push_back (std:: string ("facetious") ) ; // Calls string constructor & moves the string object 
    words.emplace_back("abstemious";// Calls string constructor to create element in place
    std::string str {"alleged"};
    words.emplace_back(str, 2, 3);
    // Create string object corresponding to "leg" in place
    

    插入元素
    通过使用成员函数 emplace(),可以在 vector 序列中插入新的元素。emplace() 的第一个参数是一个迭代器,它确定了对象生成的位置。对象会被插入到迭代器所指定元素的后面。第一个参数后的参数,都作为插入元素的构造函数的参数传入。
    成员函数 insert() 可以在 vector 中插入一个或多个元素。第一个参数总是一个指向插入点的 const 或 non-const 迭代器。元素会被迅速插入到第一个参数所指向元素的前面,如果第一个参数是一个反向迭代器,元素会被插入到迭代器所指向元素的后面。
    删除元素
    vector成员函数clear()删除所有元素,pop_back()来删除容器尾部元素。erase()传入两个元素,用来删除指定元素的范围。remove()在头两个参数指定的元素范围内,移除了所有匹配 remove() 的第三个参数 string(“none”) 的元素。

1.3 deque

  • deque < T >:双端队列容器,完成了标准 C++ 数据结构中栈的所有功能。是一个长度可变的、可以自动增长的序列,在序列的两端都不能高效地增加或删除元素。
    //初始化
    std::deque<int> my_deque(10,1); 
    //容量大小size()
    //访问元素 front() and back()
    //添加删除元素
    my_deque.push_front(11);
    my_deque.push_back(2);
    my_deque.pop_front();
    //emplace_back(),emplace_front(),emplace(),insert(),erase(),clear()这些成员函数与vector容器的使用方法相同。
    //修改(替换)元素assign()
    

1.4 list

  • list < T >:双向链表容器,完成了标准 C++ 数据结构中链表的所有功能。是一个长度可变的、由 T 类型对象组成的序列,它以双向链表的形式组织元素,在这个序列的任何地方都可以高效地增加或删除元素。

    //初始化
    std::list<std::string> words;
    std::list<double> values(50, 3.14159265);
    //获取begin(),end(),front(),back()
    //增加插入元素
    std::list<std::string> names { "Jane", "Jim", "Jules", "Janet"};
    names.push_front("Ian"); // Add string ("Ian") to the front of the list
    names.push_back("Kitty"); // Append string ("Kitty") to the end of the list
    names.emplace_front("Ian");//Create string ("Ian") in place at the front of the list
    names.emplace_back("Kitty");// Create string ("Kitty") in place at the end of the list
    std::list<int> data(10, 55); // List of 10 elements with value 55
    data.insert(++begin(data), 66); // Insert 66 as the second element
    auto iter = begin(data);
    std::advance(iter, 9); // Increase iter by 9
    data.insert(iter, 3, 88);// Insert 3 copies of 88 starting at the 10th
    //删除元素
    std::list<int> numbers { 2, 5, 2, 3, 6, 7, 8, 2,};
    numbers.remove(2); // List is now 5 3 6 7 8 9
    numbers.remove_if([](int n){return n%2 == 0;});// Remove even numbers. Result 5 3 7 9
    //排序及元素合并
    std::list<std::string> my_words { "three","six", "eight"};
    std::list<std::string> your_words { "seven", "four", "nine"};
    auto comp_str = [](const std::strings s1, const std::strings s2){ return s1[0]<s2[0];};
    my_words.sort (comp_str); //"eight" "six" "three"
    your_words.sort (comp_str) ;  //"four" "nine" "seven"
    my_words.merge (your_words, comp_str) ; // "eight" "four" "nine" "six" "seven" "three"
    //splice() 的第一个参数是指向目的容器的迭代器。第二个参数是元素的来源。第三个参数是一个指向源list容器中被粘接元素的迭代器,它会被插入到第一个参数所指向位置之前。
    std::list<std::string> my_words {"three", "six", "eight"};
    std::list<std::string> your_words {"seven", "four", "nine"};
    my_words.splice(++std::begin(my_words), your_words, ++std::begin(your_words));
    //out: your_words: "seven", "nine"
    //out: my_words : "three", "four", "six", "eight"
    
  • forward list< T >:正向链表容器。是一个长度可变的、由 T 类型对象组成的序列,它以单链表的形式组织元素,是一类比链表容器快、更节省内存的容器,但是它内部的元素只能从第一个元素开始访问。
    fdrward_list 和 list 最主要的区别是:它不能反向遍历元素;只能从头到尾遍历。
    forward_list 的单向链接性也意味着它会有一些其他的特性:
    无法使用反向迭代器。只能从它得到const或non-const前向迭代器,这些迭代器都不能解引用,只能自增;
    没有可以返回最后一个元素引用的成员函数back();只有成员函数front();
    因为只能通过自增前面元素的迭代器来到达序列的终点,所以push_back()、pop_back()、emplace_back()也无法使用。

    forward_list 的操作比 list 容器还要快,而且占用的内存更少,尽管它在使用上有很多限制,但仅这一点也足以让我们满意了。

    forward_list 容器的构造函数的使用方式和 list 容器相同。forward_list 的迭代器都是前向迭代器。它没有成员函数 size(),因此不能用一个前向迭代器减去另一个前向迭代器,但是可以通过使用定义在头文件 iterator 中的 distance() 函数来得到元素的个数。例如:

    std::forward_list<std::string> my_words {"three", "six", "eight"};
    auto count = std::distance(std::begin(my_words),std::end(my_words));
    // Result is 3
    

    distance() 的第一个参数是一个开始迭代器,第二个参数是一个结束迭代器,它们指定了元素范围。当需要将前向迭代器移动多个位置时,advance() 就派上了用场。例如:

    std::forward_list<int> data {10, 21, 43, 87, 175, 351};
    auto iter = std::begin(data);
    size_t n {3};
    std::advance(iter, n);
    std::cout << "The " << n+1 << "th element is n << *iter << std::endl;
    // Outputs 87
    

    forward_list 和 list —样都有成员函数 sort() 和 merge(),它们也都有 remove()、remove_if() 和unique(),所有这些函数的用法都和 list 相同。

1.5 容器中常见的函数成员

表 1 array、vector 和 deque 容器的函数成员

函数成员函数功能array< T,N >vector< T >deque< T >
begin()返回指向容器中第一个元素的迭代器。
end()返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。
rbegin()返回指向最后一个元素的迭代器。
rend()返回指向第一个元素所在位置前一个位置的迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
assign()用新元素替换原有内容。-
operator=()复制同类型容器的元素,或者用初始化列表替换现有内容。
size()返回实际元素个数。
max_size()返回元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
capacity()返回当前容量。--
empty()判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
resize()改变实际元素的个数。-
shrink _to_fit()将内存减少到等于当前元素实际所使用的大小。-
front()返回第一个元素的引用。
back()返回最后一个元素的引用。
operator使用索引访问元素。
at()使用经过边界检査的索引访问元素。
push_back()在序列的尾部添加一个元素。-
insert()在指定的位置插入一个或多个元素。-
emplace()在指定的位置直接生成一个元素。-
emplace_back()在序列尾部生成一个元素。-
pop_back()移出序列尾部的元素。-
erase()移出一个元素或一段元素。-
clear()移出所有的元素,容器大小变为 0。-
swap()交换两个容器的所有元素。
data()返回指向容器中第一个元素的指针。-

表 2 list 和 forward_list 的函数成员

函数成员函数功能listforward_list
begin()返回指向容器中第一个元素的迭代器。
end()返回指向容器最后一个元素所在位置后一个位置的迭代器。
rbegin()返回指向最后一个元素的迭代器。-
rend()返回指向第一个元素所在位置前一个位置的迭代器。-
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
before_begin()返回指向第一个元素前一个位置的迭代器。-
cbefore_begin()和 before_begin() 功能相同,只不过在其基础上,增加了 const 属性,即不能用该指针修改元素的值。-
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。-
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。-
assign()用新元素替换原有内容。
operator=()复制同类型容器的元素,或者用初始化列表替换现有内容。
size()返回实际元素个数。-
max_size()返回元素个数的最大值,这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
resize()改变实际元素的个数。
empty()判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
front()返回容器中第一个元素的引用。
back()返回容器中最后一个元素的引用。-
push_back()在序列的尾部添加一个元素。-
push_front()在序列的起始位置添加一个元素。
emplace()在指定位置直接生成一个元素。-
emplace_after()在指定位置的后面直接生成一个元素。-
emplace_back()在序列尾部生成一个元素。-
cmplacc_front()在序列的起始位生成一个元索。
insert()在指定的位置插入一个或多个元素。-
insert_after()在指定位置的后面插入一个或多个元素。-
pop_back()移除序列尾部的元素。-
pop_front()移除序列头部的元素。
reverse()反转容器中某一段的元素。
erase()移除指定位置的一个元素或一段元素。-
erase_after()移除指定位置后面的一个元素或一段元素。-
remove()移除所有和参数匹配的元素。
remove_if()移除满足一元函数条件的所有元素。
unique()除所有连续重复的元素。
clear()移除所有的元素,容器大小变为 0。
swap()交换两个容器的所有元素。
sort()对元素进行排序。
merge()合并两个有序容器。
splice()移动指定位置前面的所有元素到另一个同类型的 list 中。-
splice_after()移动指定位置后面的所有元素到另一个同类型的 list 中。-

2. 容器适配器

2.1 stack

  • stack< T >:是一个封装了 deque 容器的适配器类模板,默认实现的是一个后入先出(Last-In-First-Out,LIFO)的压入栈。stack 模板定义在头文件 stack 中。

    stack 容器适配器的模板有两个参数。第一个参数是存储对象的类型,第二个参数是底层容器的类型。
    stack 的底层容器默认是 deque 容器,因此模板类型其实是 stack<typename T, typename Container=deque>。通过指定第二个模板类型参数,可以使用任意类型的底层容器,只要它们支持 back()、push_back()、pop_back()、empty()、size() 这些操作。

    堆栈操作
    和其他序列容器相比,stack 是一类存储机制简单、所提供操作较少的容器。下面是 stack 容器可以提供的一套完整操作:

    • top():返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。
    • push(const T& obj):可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
    • push(T&& obj):以移动对象的方式将对象压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
    • pop():弹出栈顶元素。
    • size():返回栈中元素的个数。
    • empty():在栈中没有元素的情况下返回 true。
    • emplace():用传入的参数调用构造函数,在栈顶生成对象。
    • swap(stack & other_stack):将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同。对于 stack 对象有一个特例化的全局函数 swap() 可以使用。

2.2 queue

  • queue< T >:是一个封装了 deque 容器的适配器类模板,默认实现的是一个先入先出(First-In-First-Out,LIFO)的队列。可以为它指定一个符合确定条件的基础容器。queue 模板定义在头文件 queue 中。

    queue 和 stack 有一些成员函数相似,但在一些情况下,工作方式有些不同:
    • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
    • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
    • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
    • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
    • pop():删除 queue 中的第一个元素。
    • size():返回 queue 中元素的个数。
    • empty():如果 queue 中没有元素的话,返回 true。
    • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
    • swap(queue &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

2.3 priority_queue

  • priority_queue< T >:是一个封装了 vector 容器的适配器类模板,默认实现的是一个会对元素排序,从而保证最大元素总在队列最前面的队列。priority_queue 模板定义在头文件 queue 中。

    priority_queue 模板有 3 个参数,其中两个有默认的参数;第一个参数是存储对象的类型,第二个参数是存储元素的底层容器,第三个参数是函数对象,它定义了一个用来决定元素顺序的断言。因此模板类型是:
    template <typename T, typename Container=std::vector<T>, typename Compare=std::less<T>> class priority_queue
    
    对 priority_queue 进行操作有一些限制:
    • push(const T& obj):将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
    • push(T&& obj):将obj放到容器的适当位置,这通常会包含一个排序操作。
    • emplace(T constructor a rgs…):通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
    • top():返回优先级队列中第一个元素的引用。
    • pop():移除第一个元素。
    • size():返回队列中元素的个数。
    • empty():如果队列为空的话,返回true。
    • swap(priority_queue< T >& other):和参数的元素进行交换,所包含对象的类型必须相同。

3. 堆用法

堆(heaps) 是一种特殊的数据组织方式,STL 中的 priority_queue 容器适配器底层就是采用堆来组织数据存储的。
是分层排列的元素或节点。每个节点有一个键,它是节点中所保存的对象,就如同链表中的节点。父节点是有一个或两个子节点的节点。一般父节点可以有任意个数的子节点,树中的父节点不需要有相同个数的子节点。没有子节点的节点叫作叶节点。一般父节点的键与其子节点有一些关系。树都有一个根节点,它是树的基础,从根节点可以到达所有的子节点。
创建堆
用来创建堆的函数定义在头文件 中。max_heap() 对随机访问迭代器指定的一段元素重新排列,生成一个堆。默认使用的是 < 运算符,可以生成一个大顶堆。例如:

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

调用 make_heap() 后,vector 中的元素如注释所示,这也说明了图 2 所展示的结构。

priority_queue 是一个堆。在底层,一个 priority_queue 实例创建了一个堆。在堆中,所有成对的连续元素不需要有相同的比较关系。图 2 所示堆中的前 3 个元素是顺序递减的,但第 4 个元素却大于第 3 个元素。既然如此,为什么 STL 有 priority_queue (它是一个堆),却还需要创建堆,特别是还需要将堆作为优先级队列?

这是因为 priority_queue 可以提供堆没有的优势,它可以自动保持元素的顺序;但我们不能打乱 priority_queue 的有序状态,因为除了第一个元素,我们无法直接访问它的其他元素。如果需要的是一个优先级队列,这一点非常有用。

从另一方面来说,使用 make_heap() 创建的堆可以提供一些 priority_queue 没有的优势:

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

如果使用保持堆顺序的函数,那么可以将堆当作优先级队列使用。
堆操作
堆不是容器,而是组织容器元素的一种特别方式。为了向堆中添加元素,首先可以用任何方法将元素附加到序列中。然后调用 push_heap() 来插入最后一个元素,为了保持堆的结构,这个元素会被重新排列到一个适当的位置。

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::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}
std::push_heap(std::begin(numbers), std::end(numbers));//{12 11 3.5 10 8 2.5 1.5 6 6.5}

push_back() 会在序列末尾添加元素,然后使用 push_heap() 恢复堆的排序。通过调用 push_heap(),释放了一个信号,指出我们向堆中添加了一个元素,这可能会导致堆排序的混乱。push_heap() 会因此认为最后一个元素是新元素,为了保持堆结构,会重新排列序列。
当然,也可以用自己的比较函数来创建堆,但是必须和 push_heap() 使用相同的比较函数:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), std::end(numbers), std::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}
std::push_heap(std::begin(numbers), std::end(numbers),std::greater<>());//{1.2 1.5 2.5 6 8 12 3.5 10 6.5}

如果 push_heap() 和 make_heap() 的第 3 个参数不同,代码就无法正常执行。

删除最大元素和添加元素到堆的过程有些相似,但所做的事是相反的。首先调用 pop_heap(),然后从容器中移除最大的元素,例如:

std::vector<double> numbers{2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::end(numbers));//{12 10 3.5 6.5 8 2.5 1.5 6}
std::pop_heap(std::begin(numbers),std::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() 移除最后一个元素。如果 make_heap() 中用的是自己的比较函数,那么 pop_heap() 的第 3 个参数也需要是这个函数:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::end(numbers),std::greater<>());//{1.5 6 2.5 6.5 8 12 3.5 10}
std::pop_heap(std::begin(numbers), std::end(numbers),std:: 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}

同样,为了能够正确执行这个操作,pop_heap() 必须和 make_heap() 使用相同的比较函数。

因为可能会打乱容器中的堆,所以 STL 提供了一个检查序列是否仍然是堆的方法:

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

如果元素段是堆,那么 is_heap() 会返回 true。这里是用默认的比较断言 less<> 来检查元素顺序。如果这里使用的是用 greater<> 创建的堆,就会产生错误的结果。为了得到正确的结果,表达式需要写为:

std::is_heap(std::begin(numbers),std::end(numbers),std::greater<>())

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

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

排序操作的结果不是一个大顶堆,而是一个小顶堆。如图 4 所示,尽管堆并不是全部有序的,但任何全部有序的序列都是堆。

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

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

4. 智能指针

4.1 auto_ptr

4.2 unique_ptr(替换auto_ptr)

4.3 shared_ptr

3.4 weak_ptr

5. 关联容器

5.1 map

5.2 set

参考

http://c.biancheng.net/stl/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值