前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STL之序列式容器(deque和list)

STL之序列式容器(deque和list)

作者头像
用户9831583
发布2022-06-16 14:38:00
2830
发布2022-06-16 14:38:00
举报
文章被收录于专栏:码出名企路

deque

deque<T>以双端队列的形式组织元素,可以在容器的头部和尾部高效地添加或删除对象,这是它相对于 vector 容器的优势。

1.初始化,访问,插入

代码语言:javascript
复制
std::deque<int> a_deque;    // A deque container with no elements
std::deque<int> my_deque(10); // A deque container with 10 elements

std:: deque<std:: string> words { "one", "none", "some", "all","none","most", "many” };

std::deque<std::string> words_copy { words };// Makes a copy of the words c
std::deque<std::string> words_part { std::begin(words),std::begin(words) + 5 };

std::cout << words.at(2) << std::endl; // Output the third element in words

    std::deque<int> numbers {2, 3, 4};
    numbers.push_front(11); // numbers contains 11 2 3 4
    numbers.push_back(12);  // numbers contains 11 2 3 4 12
    numbers.pop_front();    // numbers contains 2 3 4 12

2.修改元素

代码语言:javascript
复制
std::deque<std::string> words {"one", "two", "three", "four"};
    auto init_list = {std::string{"seven"}, std::string{ "eight"}, std::string{"nine"}};
    words.assign(init_list);
    words.assign({"seven”,"eight","nine"});
    
    std::vector<std::string> wordset {"this","that","these","those"};
    words.assign(std::begin(wordset)+1, --std::end(wordset));
    //Assigns "that" and "these"

最后一条语句用 init_list 中的 string 对象替换掉了 words 中的元素。

3.综合举例

代码语言:javascript
复制
    // Using a deque container
    #include <iostream> // For standard streams
    #include <algorithm> // For copy()
    #include <deque> // For deque container
    #include <string> // For string classes
    #include <iterator> // For front_insert_iterator & stream iterators
    using std::string;
    int main()
    {
        std::deque<string> names;
        std::cout << "Enter first names separated by spaces. Enter Ctrl+Z on a new line to end:\n";
        std::copy(std::istream_iterator < string > {std::cin}, std::istream_iterator < string > {}, std::front_inserter(names));
        std::cout << "\nIn reverse order, the names you entered are:\n";
        std::copy(std::begin(names), std::end(names), std::ostream_iterator < string > {std::cout, "  "});
        std::cout << std::endl;
    }
list

list<T> 是 T 类型对象的双向链表。可以在序列已知的任何位置插入或删除元素。list 的缺点是无法通过位置来直接访问序列中的元素,也就是说,不能索引元素。

1.初始化

代码语言:javascript
复制
list 容器的构造函数的用法类似于 vector 或 deque 容器。
    std::list<std::string> words;

可以创建一个带有给定数量的默认元素的列表:
    std::list<std::string> sayings {20}; // A list of 20 empty strings

如何生成一个包含给定数量的相同元素的列表:
    std::list<double> values(50, 3.14159265);
不能使用初始化列表 {50,3.14159265},这样列表将仅仅包含两个元素。

list 容器有一个拷贝构造函数,因此可以生成一个现有 list 容器的副本:
    std::list<double> save_values {values}; // Duplicate of values

用另一个序列的开始和结束迭代器所指定的一段元素,来构造 list 容器的初始化:
    std::list<double> samples {++cbegin(values), --cend(values)};

2.增加元素

代码语言:javascript
复制
    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::vector<int> numbers(10, 5)/ // Vector of 10 elements with value 5
    data.insert(--(--end(data)), cbegin(numbers), cend(numbers));

     std::list<std:: string> names {"Jane", "Jim", "Jules", "Janet"};
    names.emplace_back("Ann");
    std:: string name ("Alan");
    names.emplace_back(std::move(name));
    names.emplace_front("Hugo");
    names.emplace(++begin(names), "Hannah");

3.删除元素

代码语言:javascript
复制
    std::list<int> numbers { 2, 5, 2, 3, 6, 7, 8, 2, 9};
    numbers.remove(2); // List is now 5 3 6 7 8 9
第二条语句移除了 numbers 中出现的所有值等于 2 的元素。

成员函数 remove_if() 期望传入一个一元断言作为参数。

一元断言接受一个和元素同类型的参数或引用,返回一个布尔值。断言返回 true 的所有元素都会被移除。

代码语言:javascript
复制
    numbers.remove_if([](int n){return n%2 == 0;});// Remove even numbers. Result 5 3 7 9
这里的参数是一个 lambda 表达式,但也可以是一个函数对象。

成员函数 unique() 非常有意思,它可以移除连续的重复元素,留下其中的第一个。
    std::list<std::string> words { "one", "two", "two", "two","three", "four", "four"};
    words.unique () ; // Now contains "one" "two" "three" "four"

3.排序

sort() 函数模板要求使用随机访问迭代器。但 list 容器并不提供随机访问迭代器,只提供双向迭代器,因此不能对 list 中的元素使用 sort() 算法。

list模板定义了自己的 sort() 函数。sort() 有两个版本:无参 sort() 函数将所有元素升序排列。第二个版本的 sort() 接受一个函数对象或 lambda 表达式作为参数,这两种参数都定义一个断言用来比较两个元素。

代码语言:javascript
复制
names.sort(std::greater<std::string>()); // Names in descending sequence
 names.sort(std::greater<>()); // Function object uses perfect forwarding

"Jules" "Jim" "Janet" "Jane" "Hugo" "Hannah" "Ann" "Alan"

在必要时可以将自定义的函数对象传给断言来对 list 排序。如果为自己的类定义了 operator()(),然后就可以继续使用 std::greater<>。

当我们需要比较非默认类型时,就需要一个函数对象。

例如,假设我们想对 names 中的元素进行排序,但是不想使用字符串对象标准的 std::greater<> 来比较,而是想将相同初始字符的字符串按长度排序。

代码语言:javascript
复制
// Order strings by length when the initial letters are the same
class my_greater{  
public:  
bool operator () (const std::strings s1, const std::strings s2)  
{        if (s1[0] == s2 [0])            
return si.length() > s2.length();      
else            return s1 > s2;    
}
};

可以用这个来对 names 中的元素排序:

代码语言:javascript
复制
names.sort(my_greater()); // Sort using my_greater

"Jules" "Janet" "Jane” "Jim" "Hannah" "Hugo" "Alan" "Ann"

names 中初始字符相同的元素按长度降序排列。

如果不需要重用 my_greater() 断言,这里也可以使用 lambda 表达式。

代码语言:javascript
复制
names.sort([](const std::strings s1, const std::strings s2)
{    if (s1[0] == s2[0])        
return s1.length() > s2.length();  
else        return s1 > s2;
});

4.合并

list 的成员函数 merge() 以另一个具有相同类型元素的 list 容器作为参数。两个容器中的元素都必须是升序。参数 list 容器中的元素会被合并到当前的 list 容器中。

代码语言:javascript
复制
    std::list<int> my_values {2, 4, 6, 14};
    std::list<int> your_values{ -2, 1, 7, 10};
    my_values.merge (your_values);//my_values contains: -2 1 2 4 6 7 10 14
    your_values.empty(); // Returns true

元素从 your_values 转移到 my_values,在执行完这个操作后,your_values 中就没有元素了。

在另一个版本的 merge() 函数中,可以提供一个比较函数作为该函数的第二个参数,用来在合并过程中比较元素。

代码语言:javascript
复制
    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"

list 容器的成员函数 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));

splice() 的第一个参数是指向目的容器的迭代器。第二个参数是元素的来源。第三个参数是一个指向源list容器中被粘接元素的迭代器,它会被插入到第一个参数所指向位置之前。代码执行完中后,容器中的内容如下:

代码语言:javascript
复制
your_words: "seven", "nine"my_words : "three", "four", "six", "eight"

当要粘接源 list 容器中的一段元素时,第 3 和第 4 个参数定义这段元素的范围

代码语言:javascript
复制
your_words.splice(++std::begin(your_words),my_words,++std::begin(my_words), std::end(my_words));

将 my_words 从第二个元素直到末尾的元素,粘接到 your_words 的第二个元前。

上面两个 list 容器的内容如下:

代码语言:javascript
复制
your_words:"seven", "four", "six", "eight","nine" my_words: "three"

下面的语句可以将 your_words 的全部元素粘接到 my_words 中:

代码语言:javascript
复制
my_words.splice(std::begin(my_words), your_words);

your_words 的所有元素被移到了 my_words 的第一个元素"three”之前。然后,your_words 会变为空。即使 your_words 为空,也仍然可以向它粘接元素:

代码语言:javascript
复制
your_words.splice(std::end(your_words), my_words);

现在,my_words 变为空,your_words 拥有全部元素。第一个参数也可以是 std::begin (your_words),因为当容器为空时,它也会返回一个结束迭代器.

forward_list

forward_list 容器以单链表的形式存储元素。

drward_list 和 list 最主要的区别是:它不能反向遍历元素;只能从头到尾遍历。

forward_list 的操作比 list 容器还要快,而且占用的内存更少.

代码语言:javascript
复制
无法使用反向迭代器。只能从它得到const或non-const前向迭代器,这些迭代器都不能解引用,只能自增;
 没有可以返回最后一个元素引用的成员函数back();只有成员函数front();
 因为只能通过自增前面元素的迭代器来到达序列的终点,所以push_back()、pop_back()、emplace_back()也无法使用。

1.大小

forward_list 的迭代器都是前向迭代器。它没有成员函数 size(),因此不能用一个前向迭代器减去另一个前向迭代器.

代码语言:javascript
复制
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

2.访问

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

代码语言:javascript
复制
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

3.插入

通过使用成员函数 cbefore_begin() 和 before_begin() 来解决。

它们分别可以返回指向第一个元素之前位置的 const 和 non-const 迭代器。可以使用它们在开始位置插入或粘接元素。

代码语言:javascript
复制
std::forward_list<std::string> my_words {"three", "six", "eight"}
std::forward_list<std::string> your_words {"seven", "four", "nine"};
my_words.splice_after(my_words.before_begin(), your_words, ++std::begin(your_words));

这个操作的效果是将 your_words 的最后一个元素粘接到 my_words 的开始位置,因此 my_words 会包含这些字符串对象:"ninef"、"three"、"six"、"eight"。这时 your_words 就只剩下两个字符串元素:"seven"和"four”。

另一个版本的 splice_afler(),将 forward_list的一段元素粘接到另一个容器中:

代码语言:javascript
复制
my_words.splice_after (my_words . before_begin () , your_words,++std::begin(your_words), std::end(your_words));

特别说明:

运算符重载在类的建立中常用。

参考博客整理好的,见https://www.cnblogs.com/xiaokang01/p/9166745.html

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码出名企路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • list
  • forward_list
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档