C++——list的相识和利用

锦通  金牌会员 | 2025-2-13 01:35:45 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 877|帖子 877|积分 2631

目录
引言
forward_list与list
标准库中的list
一、list的常用接口
1.list的迭代器
2.list的初始化
3.list的容量操纵
4.list的访问操纵
5.list的修改操纵
6.list的其他操纵
二、list与vector的对比
结束语

引言

本篇博客要介绍的是STL中的list。
求点赞收藏品评关注!!!十分感谢!!!
forward_list与list

首先我们先简单相识一下forward_list与list:
forward_list与list都是C++标准模板库(STL)中的容器,它们提供了不同的链表实现,适用于不同的场景。
   forward_list
  定义与结构:
  1.forward_list是C++11引入的一种容器,它提供了一种单向链表的数据结构。
  2.它只维护一个指向下一个节点的指针,因此内存利用相对高效。
  特点:
  1.只能在单向遍历,即只能从前今后遍历,不能反向遍历。
  2.在已知位置的情况下,插入和删除操纵非常高效,时间复杂度为O(1)。
  3.不支持通过索引访问元素,只能利用迭代器进行访问。
  适用场景:
  1.适用于必要频繁进行前向遍历和插入、删除操纵的场景。
  2.当内存利用要求较高,且不必要双向遍历时,forward_list是更好的选择。
    list
  定义与结构:
  1.list是C++标准库中基于带头双向循环链表实现的容器。
  2.它支持双向迭代器,可以从前今后和从后往前遍历。
  特点:
  1.在任何位置进行插入和删除操纵的时间复杂度都是近似O(1)。
  2.支持双向遍历,迭代器利用更机动。
  3.与forward_list相比,内存占用稍多,因为每个节点必要维护两个指针(一个指向前一个节点,一个指向下一个节点)。
  适用场景:
  1.适用于必要双向遍历和更机动操纵的场景。
  2.当必要在列表中心频繁插入或删除元素时,list是更好的选择。
  具体内容可以看看这两篇文章:
数据结构——单链表
数据结构——双向链表
本文的重点是list。
标准库中的list

一、list的常用接口

list接口






1.list的迭代器

list的迭代器访问元素与我们之前学习的其他容器的迭代器访问一样。
  1. int main()
  2. {
  3.         list<int> li = { 1, 2, 3, 4, 5 };
  4.         list<int>::iterator it = li.begin();
  5.         cout << "顺序遍历:";
  6.         while (it != li.end())
  7.         {
  8.                 cout << *it << " ";
  9.                 ++it;
  10.         }
  11.         cout << endl;
  12.         cout << "逆序遍历:";
  13.         list<int>::reverse_iterator rit = li.rbegin();
  14.         while (rit != li.rend())
  15.         {
  16.                 cout << *rit << " ";
  17.                 ++rit;
  18.         }
  19.         return 0;
  20. }
复制代码

由于list的迭代器是双向迭代器,支持++和--操纵,因此可以在list中向前和向后遍历。
2.list的初始化



list的初始化与我们之前学习的其他容器的初始化一样,我们直接看个简单的利用示例:
  1. int main()
  2. {
  3.     // 默认构造函数
  4.     list<int> numbers1;
  5.     cout << "默认构造: ";
  6.     for (const auto& num : numbers1)
  7.     {
  8.         cout << num << " ";
  9.     }
  10.     cout << endl;
  11.     // n个val构造
  12.     list<int> numbers2(5, 5);
  13.     cout << "n个val构造: ";
  14.     for (const auto& num : numbers2)
  15.     {
  16.         cout << num << " ";
  17.     }
  18.     cout << endl;
  19.     int arr[] = { 1, 2, 3, 4, 5 };
  20.     // 通过vector的迭代器初始化
  21.     list<int> numbers3(arr, arr + sizeof(arr) / sizeof(arr[0]));
  22.     cout << "迭代器区间构造: ";
  23.     for (const auto& num : numbers3)
  24.     {
  25.         cout << num << " ";
  26.     }
  27.     cout << endl;
  28.     list<int> numbers4 = { 6, 7, 8, 9, 10 };
  29.     // 拷贝构造
  30.     list<int> numbers5(numbers4);
  31.     cout << "拷贝构造: ";
  32.     for (const auto& num : numbers5)
  33.     {
  34.         cout << num << " ";
  35.     }
  36.     cout << endl;
  37.     numbers1 = numbers2;
  38.     // 赋值重载
  39.     cout << "赋值重载: ";
  40.     for (const auto& num : numbers1)
  41.     {
  42.         cout << num << " ";
  43.     }
  44.     cout << endl;
  45.     return 0;
  46. }
复制代码
输出结果为:

3.list的容量操纵

函数名称功能
size返回列表中元素的数量
max_size返回列表可容纳的最大元素数量
empty查抄列表是否为空,是则返回 true,否则返回 false
resize重新设置列表中元素的数量,超过原来数量则用默认值添补
clear清空列表中的全部元素
这些函数也是很容易理解的,我们照旧直接看代码示例:
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3,4,5 };
  4.         cout << "Size of list: " << li.size() << endl;
  5.         cout << "Max size of list: " << li.max_size() << endl;
  6.         if (li.empty())
  7.         {
  8.                 cout << "empty" << endl;
  9.         }
  10.         else
  11.         {
  12.                 cout << "not empty" << endl;
  13.         }
  14.         li.clear();
  15.         if (li.empty())
  16.         {
  17.                 cout << "empty" << endl;
  18.         }
  19.         else
  20.         {
  21.                 cout << "not empty" << endl;
  22.         }
  23.         return 0;
  24. }
复制代码
输出结果为

与deque这一容器一样,list也没有reserve这一接口。
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3 };
  4.         li.resize(5);
  5.         for (auto& num : li)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         cout << endl;
  10.         li.resize(2);
  11.         for (auto& num : li)
  12.         {
  13.                 cout << num << " ";
  14.         }
  15.         return 0;
  16. }
复制代码
输出结果为;

4.list的访问操纵

函数名称功能
back返回列表最后一个元素
front返回列表第一个元素
由于list 是一个双向链表,不支持高效的随机访问。在链表中,访问某个元素必要重新节点开始顺序遍历,直到找到目标元素。因此,为 list 提供下标运算符或 at 方法并不合适。
访问操纵的利用示例如下:
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3 };
  4.         cout << li.front() << endl;
  5.         cout << li.back() << endl;
  6.         return 0;
  7. }
复制代码
输出结果为:

5.list的修改操纵

常用的修改操纵有如下几个:
函数名称功能push_back在列表尾部添加元素push_front在列表头部添加元素pop_back删除列表最后一个元素pop_front删除列表第一个元素insert在指定位置插入元素erase删除指定位置或区间的元素swap交换两个列表assign利用指定列表替换原列表 这些接口我们也是十分认识了,我们直接看代码示例:
   尾删和尾插:
  1. int main()
  2. {
  3.         list<int> li;
  4.         li.push_back(1);
  5.         li.push_back(2);
  6.         li.push_back(3);
  7.         li.push_back(4);
  8.         for (auto& num : li)
  9.         {
  10.                 cout << num << " ";
  11.         }
  12.         cout << endl;
  13.         li.pop_back();
  14.         for (auto& num : li)
  15.         {
  16.                 cout << num << " ";
  17.         }
  18.         cout << endl;
  19.         return 0;
  20. }
复制代码
输出结果为:
  

    头删和头插:
  1. int main()
  2. {
  3.         list<int> li;
  4.         li.push_front(1);
  5.         li.push_front(2);
  6.         li.push_front(3);
  7.         li.push_front(4);
  8.         for (auto& num : li)
  9.         {
  10.                 cout << num << " ";
  11.         }
  12.         cout << endl;
  13.         li.pop_front();
  14.         for (auto& num : li)
  15.         {
  16.                 cout << num << " ";
  17.         }
  18.         cout << endl;
  19.         return 0;
  20. }
复制代码
输出结果为:
  

    assign和swap:
  1. int main()
  2. {
  3.         list<int> li1 = { 1,1,1,1,1 };
  4.         li1.assign(3, 3);
  5.         for (auto& num : li1)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         cout << endl;
  10.         list<int> li2 = { 2,2,2,2,2 };
  11.         li1.swap(li2);
  12.         for (auto& num : li1)
  13.         {
  14.                 cout << num << " ";
  15.         }
  16.         cout << endl;
  17.         for (auto& num : li2)
  18.         {
  19.                 cout << num << " ";
  20.         }
  21.         return 0;
  22. }
复制代码
输出结果为:
  

    insert:
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3,4,5 };
  4.         list<int>::iterator it = li.begin();
  5.         it = li.insert(it, 6);
  6.         it = li.insert(it, 7);
  7.         for (auto& num : li)
  8.         {
  9.                 cout << num << " ";
  10.         }
  11.         return 0;
  12. }
复制代码
输出结果为:
  

    erase:
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3,4,5 };
  4.         list<int>::iterator it = li.begin();
  5.         it = li.erase(it);
  6.         for (auto& num : li)
  7.         {
  8.                 cout << num << " ";
  9.         }
  10.         return 0;
  11. }
复制代码
输出结果为:
  

  6.list的其他操纵

接下来我们来学习list的其他操纵:
函数名称功能描述splice将元素从一个列表转移到另一个列表remove移除具有特定值的元素remove_if移除满意条件的元素unique移除重复的值sort对容器中的元素进行排序merge合并已排序的列表reverse反转元素的顺序   splice:
  splice 是 list 提供的一个成员函数,用于将一个列表(list)中的元素移动到另一个列表中,而不必要进行元素复制或移动操纵。
  利用示例:
  1. int main()
  2. {
  3.         list<int> li1 = { 1,2,3 };
  4.         list<int> li2 = { 4,5,6 };
  5.         list<int> li3 = { 7,8,9 };
  6.         list<int> li4 = { 0 };
  7.         // 将 li2 的所有元素拼接到 li1 的末尾
  8.         // li1 现在包含 {1, 2, 3, 4, 5, 6}
  9.         // li2 现在为空 {}
  10.         li1.splice(li1.end(), li2);
  11.         for (auto num : li1)
  12.         {
  13.                 cout << num << " ";
  14.         }
  15.         cout << endl;
  16.         // 获取 li3 的迭代器,指向第一个元素(7)
  17.         auto begin1 = li3.begin();
  18.         // 将迭代器向前移动一位,指向第二个元素(8)
  19.         ++begin1;
  20.         // 将 li3 的第一个元素(7)移动到 li4 的末尾
  21.         // li4 现在包含 {0, 7}
  22.         // li3 现在包含 {8, 9}
  23.         li4.splice(li4.end(), li3, li3.begin(), begin1);
  24.         for (auto num : li4)
  25.         {
  26.                 cout << num << " ";
  27.         }
  28.         return 0;
  29. }
复制代码
输出结果为:
  

    remove:
  用于从容器中移除全部等于指定值的元素。
  与成员函数 erase 不同,成员函数 erase 按元素的位置擦除元素(利用迭代器),此函数  按元素的值删除元素。
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3,3,4,5 };
  4.         li.remove(3);
  5.         for (auto num : li)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         cout << endl;
  10.         return 0;
  11. }
复制代码
输出结果为:
  

    remove_if:
  用于从容器中移除满意特定条件的元素。
  1. bool fun(int num)
  2. {
  3.         return num == 3;
  4. }
  5. int main()
  6. {
  7.         list<int> li = { 1,2,3,3,4,5 };
  8.         li.remove_if(fun);
  9.         for (auto num : li)
  10.         {
  11.                 cout << num << " ";
  12.         }
  13.         cout << endl;
  14.         return 0;
  15. }
复制代码
输出结果为:
  

    unique:
  用于移除容器中连续重复的元素。
  1. int main()
  2. {
  3.         list<int> li = { 1,2,3,3,4,5 };
  4.         li.unique();
  5.         for (auto num : li)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         return 0;
  10. }
复制代码
输出结果为:
  

    sort:
  用于对容器中的元素进行排序。
  1. int main()
  2. {
  3.         list<int> li = { 9,1,5,3,2,4,8,0,7,6 };
  4.         li.sort();
  5.         for (auto num : li)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         return 0;
  10. }
复制代码
输出结果为:
  

    merge:
  用于将两个已排序的范围合并成一个有序范围。
  要求输入的两个范围必须是有序的(通常是升序)。它会将两个范围中的元素按顺序合并到目标范围中。目标范围必须有充足的空间来存储合并后的结果。
  1. int main()
  2. {
  3.         list<int> li1 = { 1,3,2,5,7 };
  4.         list<int> li2 = { 2,3,4,6,8 };
  5.         li1.sort();
  6.         li2.sort();
  7.         li1.merge(li2);
  8.         for (auto num : li1)
  9.         {
  10.                 cout << num << " ";
  11.         }
  12.         return 0;
  13. }
复制代码
输出结果为:
  

  reverse:
用于反转容器中元素的顺序。
  1. int main()
  2. {
  3.         list<int> li = { 9,1,5,3,2,4,8,0,7,6 };
  4.         li.reverse();
  5.         for (auto num : li)
  6.         {
  7.                 cout << num << " ";
  8.         }
  9.         return 0;
  10. }
复制代码
输出结果为:

二、list与vector的对比

vectorlist
底层结构动态顺序表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素效率O(N)
插入和删除任意位置插入和删除效率低,必要搬移元素,时间复杂度为O(N),插入时有可能必要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不必要搬移元素,时间复杂度为0(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,末节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,要给全部的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器必要重新赋值否则会失效插入元素不会导致选代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
利用场景必要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操纵,不关心随机访问
结束语

求点赞收藏品评关注!!!
感谢各位大佬的支持!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

锦通

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表