c++ list详解

一.list介绍及使用

list这里指的是双向带头循环链表,首先是官网:list - C++ Reference

下面是list的部分成员变量:

这里是作者截图一部分,是VS里面stl_list.h文件的,表明链表的节点有两个指针,一个指向前一个,一个指向后一个。

1.1构造函数

先看表

|-----------------------------------------------------------|-----------------------------|
| 构造函数( (constructor)) | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的 元素 |
| list() | 构造空的list |
| list (const list& x) | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造 list |

最后一个是迭代器构造,传一个迭代器,通过这个区间构造函数。下面是案例:

cpp 复制代码
string a("abcdefghijk");        
list<int> a1(5, 1);                 //1 1 1 1 1
list<int> a2;                       //空
list<int> a3(a1);                   //1 1 1 1 1
list<int> a4(a.begin(), a.end());   //97 ... 107,字符串内容字符强转整型转换成ASCII码值

1.2iterator迭代器的使用

先看表

|-------------|-------------------------------------------------------------------------|
| 函数声明 | 接口说明 |
| begin,end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin,rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置 |

这里的因为链表是带头节点(哨兵位)的,因此实际上begin就是第一个有效数据,end是头节点,rbegin是倒数第一个有效数据,但是返回的是头节点的地址,解引用还是最后一个有效数据,这里实际上是属于一种矫正行为。rend是最后一个有效数据下一个位置即头节点,但是返回的是第一个有效数据的地址,只是在解引用上会表现出头节点。这种现象本质原因是因为两种迭代器的类型和++方式等不同导致的。

1.3list的使用

先看表

|------------|------------------------------|
| 函数声明 | 接口说明 |
| empty | 检测list是否为空,是返回true,否则返回false |
| size | 返回list中有效节点的个数 |
| front | 返回list的第一个节点中值的引用 |
| back | 返回list的最后一个节点中值的引用 |
| push_front | 在list首元素前插入值为val的元素 |
| push_back | 在list尾部插入值为val的元素 |
| pop_front | 删除list中第一个元素 |
| pop_back | 删除list中最后一个元素 |
| insert | 在list position 位置中插入值为val的元素 |
| erase | 删除list position位置的元素 |
| swap | 交换两个list中的元素 |
| clear | 清空list中的有效元素 |

这里针对每个函数做出简单说明

empty

cpp 复制代码
// list::empty
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  int sum (0);
  for (int i=1;i<=10;++i) 
     mylist.push_back(i);    //尾插i到链表里
  while (!mylist.empty())    //只要链表不为空就继续循环
  {
     sum += mylist.front();  //计算链表数据之和
     mylist.pop_front();     //删除链表第一个数据
  }
  cout << "total: " << sum << '\n';
  return 0;
}

size

cpp 复制代码
// list::size
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> myints;
  cout << "0. size: " << myints.size() << '\n';   //输出0.size: 0
  for (int i=0; i<10; i++) 
      myints.push_back(i);
  cout << "1. size: " << myints.size() << '\n';   //输出1.size: 10
  myints.insert (myints.begin(),10,100);          //在开头插入10个100
  cout << "2. size: " << myints.size() << '\n';   //输出2.size: 20
  myints.pop_back();                              //删除一个节点
  cout << "3. size: " << myints.size() << '\n';   //输出3.size: 19
  return 0;
}

front

cpp 复制代码
// list::front
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  mylist.push_back(77);               //尾插77
  mylist.push_back(22);               //尾插22
  mylist.front() -= mylist.back();       //使第一个数据减最后一个数据的值
  cout << "mylist.front() is now " << mylist.front() << '\n';
                                         //输出mylist.front() is now 55
  return 0;
}

back

cpp 复制代码
// list::back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  mylist.push_back(10);           //尾插10
  while (mylist.back() != 0)
  {
    mylist.push_back ( mylist.back() -1 );       //循环依次尾插9 8 7 6 5 4 3 2 1 0
  }
  cout << "mylist contains:";
  for (auto it=mylist.begin(); it!=mylist.end() ; ++it)
    cout << ' ' << *it;            
  //最终输出mylist contains: 10 9 8 7 6 5 4 3 2 1 0
  return 0;
}

push_front

cpp 复制代码
// list::push_front
#include <iostream>
#include <list>
using namespce std;
int main ()
{
  list<int> mylist (2,100);         // 创建链表为100 100
  mylist.push_front (200);          //头插200,链表为 200 100 100
  mylist.push_front (300);          //头插300,链表为 300 200 100 100
  cout << "mylist contains:";
  for (auto it=mylist.begin(); it!=mylist.end(); ++it)
    cout << ' ' << *it;
  //输出mylist contains:300 200 100 100 
  return 0;
}

push_back

cpp 复制代码
// list::push_back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  int myint;
  do {
    cin >> myint;
    mylist.push_back (myint); //一直输入插入数据
  } while (myint);
  cout << "mylist stores " << mylist.size() << " numbers.\n";
  //输出看输入了多少个数据
  return 0;
}

pop_front

cpp 复制代码
// list::pop_front
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  mylist.push_back (100);
  mylist.push_back (200);
  mylist.push_back (300);    //尾插完成链表100 200 300

  while (!mylist.empty())
  {
    cout << ' ' << mylist.front();
    mylist.pop_front();
  }
  cout << "\nFinal size of mylist is " << mylist.size() << '\n';
  //输出100 200 300Final size of mylist is 0
  return 0;
}

pop_back

cpp 复制代码
// list::pop_back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  int sum (0);
  mylist.push_back (100);
  mylist.push_back (200);
  mylist.push_back (300);
  while (!mylist.empty())
  {
    sum+=mylist.back();
    mylist.pop_back();     //尾删
  }
  std::cout << "The elements of mylist summed " << sum << '\n';
  //输出The elements of mylist summed 600
  return 0;
}

insert

这个函数有许多种不同的类型,详细可以看list::insert - C++ Reference

cpp 复制代码
iterator insert (const_iterator position, const value_type& val);
//                 传迭代器位置                要插入的数据
iterator insert (const_iterator position, InputIterator first, InputIterator last);
//                 传迭代器位置                要插入的数据的迭代器区间
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main ()
{
  list<int> mylist;
  list<int>::iterator it;
  for (int i=1; i<=5; ++i) 
    mylist.push_back(i);         // 1 2 3 4 5
  it = mylist.begin();
  ++it;                           //it迭代器指向2
  mylist.insert (it,10);                        // 1 10 2 3 4 5
                    
  mylist.insert (it,2,20);                      // 1 10 20 20 2 3 4 5

  --it;       // it向前指向第二个20          

  vector<int> myvector (2,30);    //30 30
  mylist.insert (it,myvector.begin(),myvector.end());
              //在第一个列表里面插入 1 10 20 30 30 20 2 3 4 5              
  return 0;
}

erase

cpp 复制代码
iterator erase (const_iterator position);       //删除迭代器位置的数据
iterator erase (const_iterator first, const_iterator last);
//删除迭代器区间位置的数据,左闭右开
//返回的迭代器位置是删除之前该元素的下一个元素的位置
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  list<int>::iterator it1,it2;
  for (int i=1; i<10; ++i) 
     mylist.push_back(i*10);
                              // 10 20 30 40 50 60 70 80 90
  it1 = it2 = mylist.begin(); 
  advance (it2,6);            // 使it2迭代器往后6个数据指向,即70
  ++it1;                   
  it1 = mylist.erase (it1);   // 10 30 40 50 60 70 80 90
                              //    ^           ^

  it2 = mylist.erase (it2);   // 10 30 40 50 60 80 90
                              //    ^           ^

  ++it1;                      //       ^        ^
  --it2;                      //       ^     ^

  mylist.erase (it1,it2);     // 10 30 60 80 90
                              //        ^
  return 0;
}

swap

cpp 复制代码
// swap lists
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> first (3,100);   // 链表100 100 100
  list<int> second (5,200);  // 链表200 200 200 200 200
  first.swap(second);        // 两者交换
  //first链表变成200 200 200 200 200
  //second链表变成100 100 100
  return 0;
}

clear

cpp 复制代码
// clearing lists
#include <iostream>
#include <list>
using namespace std;
int main ()
{
  list<int> mylist;
  list<int>::iterator it;
  mylist.push_back (100);
  mylist.push_back (200);
  mylist.push_back (300);
  //链表 100 200 300
  mylist.clear();
  //链表为空
  mylist.push_back (1101);
  mylist.push_back (2202);
  //链表 1101 2202 
  return 0;
}

二.list与vector对比

|-------|------------------------------------------------------------------------|----------------------------------------------|
| | vector | list |
| 底层结构 | 连续开辟的空间,类似变长数组 | 双向带头循环链表 |
| 随机访问 | 支持下表随机访问,复杂度O(1) | 不支持随机访问,可以用迭代器遍历访问 |
| 插入和删除 | 效率比较低,需要搬移元素,可能需要异地扩容 | 效率较高,直接改变链表节点的_next和_prev即可 |
| 空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用 率高,缓存利用率高 | 底层节点动态开辟,小节点容 易造成内存碎片,空间利用率 低,缓存利用率低 |
| 迭代器 | 原生态指针 | 对原生态指针(节点指针)进行 封装 |
| 迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为 插入元素有可能会导致重新扩容,致使原来迭代器 失效,删除时,当前迭代器需要重新赋值否则会失 效 | 插入元素不会导致迭代器失 效,删除元素时,只会导致当 前迭代器失效,其他迭代器不 受影响 |
| 使用场景 | 需要高效存储,支持随机访问,不关心插入删除效 率 | 大量插入和删除操作,不关心 随机访问 |

相关推荐
啊我不会诶1 小时前
2023西安邀请赛vp补题
c++·算法
khalil10201 小时前
代码随想录算法训练营Day-38动态规划06 | 322. 零钱兑换、279.完全平方数、139.单词拆分、多重背包、总结
数据结构·c++·算法·leetcode·动态规划
6Hzlia2 小时前
Hot 100 刷题计划】 LeetCode 146. LRU 缓存 | C++ 哈希表+双向链表
c++·leetcode·缓存
我不是懒洋洋2 小时前
【数据结构】二叉树OJ(单值二叉树、检查两棵树是否相同、对称二叉树、二叉树的前序遍历、另一颗树的子树)
c语言·数据结构·c++·经验分享·算法·leetcode·visual studio
wljy12 小时前
每日一题(2026.4.29) 猫猫与数学
c语言·c++·算法·蓝桥杯·stl·牛客
FreeGo~2 小时前
手撕C++】内存管理:感受C++的魅力吧
开发语言·c++
大袁同学2 小时前
【进程间通信】:洞穿边界修管道,映射内存渡进程
linux·c++·管道·进程间通信·ipc
ximu_polaris2 小时前
设计模式(C++)-行为型模式-责任链模式
c++·设计模式·责任链模式
Rabitebla3 小时前
【C++】string 类:原理、踩坑与对象语义
linux·c语言·数据结构·c++·算法·github·学习方法