• 【C++】list的介绍及使用说明


    目录

    00.引言

    01.list的介绍

    模版类

    独立节点存储

    list的使用

    1.构造函数

    2.迭代器的使用

    分类

    运用

    3.容量管理

    empty():

    size():

    4.元素访问

    5.增删查改


    00.引言

    我们学习数据结构时,学过两个基本的数据结构:顺序表链表顺序表中的数据是连续存储的,并且支持随机访问,但是增删数据的效率比较低,因为要挪动数据;链表中的数据是分散存储的,每个节点包含一个数据和指向令一个节点的指针,不支持随机访问,但插入和删除操作的效率较高。

    vector容器内部数据存储原理可以认为是顺序表,而list可以认为是双向链表(每个节点都包含指向前一个和后一个节点的指针)。

    01.list的介绍

    模版类

    和"std::vector"一样,“std::list”实现不同类型数据的管理也是通过模版类的机制实现的。当创建一个list对象时,可以通过模版参数来指定存储的元素类型。使用"<数据类型>"来显式实例化指定存储的元素类型,例如“std::list”表示存储整数类型的链表,“std::list”表示存储双精度浮点型的链表……

    1. #include
    2. template<typename T>
    3. class list {
    4. ……
    5. };
    6. int main() {
    7. list<int> myList1;
    8. list<double> myList2;
    9. return 0;
    10. }

    独立节点存储

    list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。因此list可以在任意位置进行插入和删除,且时间复杂度为O(1),并且该容器可以前后双向迭代。

    list的使用

    在cpluscplus网站上有具体的list的使用说文档:list使用文档,在实际运用中,需要熟悉一些常用的list接口,以下列出了一些常见接口:

    1.构造函数

    在使用list创建对象之前,需要包含头文件”。

    我们需要调用构造函数来创建对象,list的构造函数有以下几种:

    1.list():无参构造

    2.list(size_type n,const value_type& val = value_type()):初始化n个val的元素

    3.list(const list& x):拷贝构造函数

    4.list(inputiterator first, inputiterator last):用(first,last)区间中的元素构造

    代码演示:

    1. void text1()
    2. {
    3. list<int> l1;
    4. list<int> l2(5, 10);
    5. list<int> l3(l2);
    6. list<int> l4(l3.begin(), l3.end());
    7. int arr[] = { 0,1,2,3,4 };
    8. list<int> l5(arr, arr + 5);
    9. }

    运行结果:

    2.迭代器的使用

    可以将 list 中的迭代器理解为一个指针,该指针指向list中的某一个节点

    分类

        · begin + end:返回第一个数据位置的迭代器 + 返回最后一个数据的下一个位置的迭代器

        · rbegin + rend:返回第一个元素的reverse_iterator,即end位置 + 返回最后一个元素下一个位置的reverse_iterator,即begin位置

    注意:

    1.begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

    2.rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

    运用

    遍历list只能用迭代器和范围for,而vector还可以用类似与数组的下标[]访问,那是因为vector中的数据是连续存储的,而list中是分散存储的,元素地址直接并无关联。

    迭代器和范围for遍历:

    1. void text2(const list<int>& l)
    2. {
    3. for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it) {
    4. cout << *it << " ";
    5. //*it = 10; 编译不通过
    6. }
    7. cout << endl;
    8. }
    9. int main() {
    10. //text1();
    11. list<int> l{ 1,2,3,4,5,6,7,8,9 };
    12. text2(l);
    13. return 0;
    14. }

    运行结果: 

    可以看到这里使用的是const迭代器,这表示迭代器指向的元素是不可被修改的,来看看const_iterator的底层是这样定义的:

    1. class list {
    2. public:
    3. // other members...
    4. // 内部定义 const_iterator
    5. typedef const T* const_iterator;
    6. };

    所以const迭代器指向的元素是常量,不可进行修改操作,而迭代器本身是可以++或--的,如果是

    typedef T* const const_iterator;

    这种方式定义,则表示迭代器本身不可++或--操作,其指向的元素则可以被修改。

    正向反向迭代器:

    1. void text3()
    2. {
    3. int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    4. list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    5. // 使用正向迭代器正向list中的元素
    6. // list::iterator it = l.begin(); // C++98中语法
    7. auto it = l.begin(); // C++11之后推荐写法
    8. while (it != l.end())
    9. {
    10. cout << *it << " ";
    11. ++it;
    12. }
    13. cout << endl;
    14. // 使用反向迭代器逆向打印list中的元素
    15. // list::reverse_iterator rit = l.rbegin();
    16. auto rit = l.rbegin();
    17. while (rit != l.rend())
    18. {
    19. cout << *rit << " ";
    20. ++rit;
    21. }
    22. cout << endl;
    23. }
    24. int main() {
    25. text3();
    26. return 0;
    27. }

    运行结果: 

    在C++11中引入了auto关键字,编译器会自动推导auto定义的变量的类型,具体内容可以参考我的这篇博客:auto关键字与基于范围的for循环(C++11)

    3.容量管理

    list虽然不需要动态开辟管理内存空间,但是还是要对节点的数量(数据的个数)进行跟踪管理

    empty():

    用于检查列表是否为空。如果列表为空,则返回 ‘true’,否则返回 ‘false’。

    代码演示:

    1. void text4()
    2. {
    3. list<int> myList;
    4. if (myList.empty()) {
    5. std::cout << "List is empty!" << std::endl;
    6. }
    7. else {
    8. std::cout << "List is not empty. Size: " << myList.size() << std::endl;
    9. }
    10. }
    11. int main() {
    12. text4();
    13. return 0;
    14. }

     运行结果:

    size():

    用于获取列表中的数量。

    代码演示:

    1. void text5()
    2. {
    3. list<int> myList = { 1, 2, 3, 4, 5 };
    4. std::cout << "Size of list: " << myList.size() << std::endl;
    5. }
    6. int main() {
    7. text5();
    8. return 0;
    9. }

    运行结果:

    4.元素访问

    有别与begin、end,list中可以通过front、back函数分别获取list的第一个节点的引用和最后一个节点的引用,是直接获取元素本身,而不是元素地址(指向元素的迭代器(指针))。

    代码演示:

    1. void text6()
    2. {
    3. list<int> myList = { 1, 2, 3, 4, 5 };
    4. int firstElement = myList.front();
    5. cout << "First element: " << firstElement << endl;
    6. int lastElement = myList.back();
    7. cout << "Last element: " << lastElement << endl;
    8. }
    9. int main() {
    10. text6();
    11. return 0;
    12. }

    5.增删查改

    下面是代码演示:

    1. void PrintList(const list<int>& l)
    2. {
    3. // 注意这里调用的是list的 begin() const,返回list的const_iterator对象
    4. for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
    5. {
    6. cout << *it << " ";
    7. // *it = 10; 编译不通过
    8. }
    9. cout << endl;
    10. }
    11. // list插入和删除
    12. // push_back/pop_back/push_front/pop_front
    13. void Test1()
    14. {
    15. int array[] = { 1, 2, 3 };
    16. list<int> L(array, array + sizeof(array) / sizeof(array[0]));
    17. // 在list的尾部插入4,头部插入0
    18. L.push_back(4);
    19. L.push_front(0);
    20. PrintList(L);
    21. // 删除list尾部节点和头部节点
    22. L.pop_back();
    23. L.pop_front();
    24. PrintList(L);
    25. }
    26. // insert /erase
    27. void Test2()
    28. {
    29. int array1[] = { 1, 2, 3 };
    30. list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
    31. // 获取链表中第二个节点
    32. auto pos = ++L.begin();
    33. cout << *pos << endl;
    34. // 在pos前插入值为4的元素
    35. L.insert(pos, 4);
    36. PrintList(L);
    37. // 在pos前插入5个值为5的元素
    38. L.insert(pos, 5, 5);
    39. PrintList(L);
    40. // 在pos前插入[v.begin(), v.end)区间中的元素
    41. vector<int> v{ 7, 8, 9 };
    42. L.insert(pos, v.begin(), v.end());
    43. PrintList(L);
    44. // 删除pos位置上的元素
    45. L.erase(pos);
    46. PrintList(L);
    47. // 删除list中[begin, end)区间中的元素,即删除list中的所有元素
    48. L.erase(L.begin(), L.end());
    49. PrintList(L);
    50. }
    51. // resize/swap/clear
    52. void Test3()
    53. {
    54. // 用数组来构造list
    55. int array1[] = { 1, 2, 3 };
    56. list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
    57. PrintList(l1);
    58. // 交换l1和l2中的元素
    59. list<int> l2;
    60. l1.swap(l2);
    61. PrintList(l1);
    62. PrintList(l2);
    63. // 将l2中的元素清空
    64. l2.clear();
    65. cout << l2.size() << endl;
    66. }
    67. int main() {
    68. Test1();
    69. Test2();
    70. Test3();
    71. return 0;
    72. }

    运行结果: 

    以上就是关于list的介绍和使用说明的有关知识了,欢迎在评论区留言~感觉这篇博客对您有帮助的可以点赞关注支持一波喔~😉 

  • 相关阅读:
    Linux 命令(194)—— ethtool 命令
    Go语言的自给自足:编译自身的神奇之旅
    填矩阵 码蹄集
    LLC谐振变换器软启动过程分析与问题处理
    AlphaControls控件TsRadioGroup的使用
    记录eclipse revert resources (0%)
    深度学习的进展,产业化,规模化、数据化
    CSS 分组 和 嵌套 选择器
    【环境】ubuntu下anaconda虚拟环境中安装的pytorch终于配置成功了!
    【VPX610】 青翼科技基于6U VPX总线架构的高性能实时信号处理平台
  • 原文地址:https://blog.csdn.net/dhgiuyawhiudwqha/article/details/137911706