当前位置: 首页 > news >正文

C++-->stl: list的使用

前言list的认识

list是可以在固定时间(O(1))内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向(当然有一个哨兵位 就是说头节点)
其前一个元素和后一个元素。
3. listforward_list非常相似:最主要的不同在于forward_list是单链表,只能向前迭代,已让其更简单高(list是doubly list的接口 forward_list是单链表的接口)
效。
4. 与其他的序列式容器相比(arrayvectordeque)list通常在任意位置进行插入、移除元素的执行效率 更好。
5. 与其他序列式容器相比,listforward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间  (需要创建一个结点去遍历,比随机访问效率低)
   开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的list来说这不合适 可能是一个重要的因素

1 list的构造
常用的构造函数

default (1)

默认构造)

explicit list (const allocator_type& alloc = allocator_type());

      fill (2)

 n个val值填充

explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());

range (3)

   迭代器构造

template <class InputIterator>list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());

copy (4)

拷贝构造

list (const list& x);
参考代码 : 
   
void print_list(list<int>& list1)
{list<int>::iterator it = list1.begin();while (it != list1.end()){cout << *it;it++;}cout << endl;}
void construct_test()
{list<int>list_1;// 空构造list<int>list_2(20,0);// n  vallist<int>::iterator it = list_2.begin();  // 迭代器构造 list<int> th(list_2.begin(),list_2.end());// 不支持的写法 list<int> th(list_2.begin(),list_2.begin()+4);// 因为list的迭代器不支持随机访问哦 其实本质上是因为只给迭代器封装了++的操作 int arr[] = {1,2,3,4,5,6};list<int>four(arr,arr+4);// 这也是迭代器构造 数组肯定是可以通过下标 随机访问啦 // 拷贝构造//list<int> five = four;list<int> five(four);print_list(five);}

2. list的迭代器 

    list的图解 

迭代器的理解:

  
  这个迭代器的内部封装可以大致这么理解:
    肯定是对node的操作,封装node的移动,有++,--以及!=   判断俩个迭代器是否相等就这样就很常见,封装的目的很简单,就是为了统一容器的操作哦!。
    后期博客我会更新其中的模拟实现这是很有意思的:
   内部是没有封装+某个数的 
   所以之前例子中指出 像这样的 list<int>::iterator(it,it+3);  是不被允许的哦!
总之因为list的底层容器是双向链表,每个节点地址之间是不连续的,所以我们为了统一容器操作就封装迭代器了。
迭代器使用:代码实例:
void iterator_test() {int arr[] = { 1,2,3,4,5,6 };list<int>four(arr, arr + 5);// 用迭代器遍历four这个链表list<int>::iterator it = four.begin();// 正向迭代器list<int>::reverse_iterator rit = four.rbegin();// 反向迭代器while (it != four.end()){cout << *it;it++;}cout << endl;while (rit != four.rend()){cout << *rit;rit++;}cout << endl;}
输出:12345   
           54321

doubly list的图解

   这里只标出了 node之间指针的关系,这里值得注意的就是如何实现双向循环的,就是创建一个头节点,头节点作用就是用来很方便的实现增删查改(减少判空这个策略数据结构中可以多了解理解) 总之就是让头节点的pre指向尾节点,尾节点的next指向头节点。 总之头节点是不存放有效数据的! 所以下面我说说迭代器遍历的问题!

 list迭代器遍历的理解: 

      显然 list<T>iterator:: begin 指向的是头节点的下一个位置,node结构体中总是存放的是头节点,所以需要遍历的时候需要总是从头节点开始一一遍历,而不是随机访问。 
    判断到尾部的方法有很多一个是根据size,以及cur节点的遍历到了头节点了。

3. list element access(元素获取)

     list的获取常用的就是一个是front  和 back  双向链表根据头节点实现起来很方便的。

     

3.1 reference front()  和reference back的使用
        这个list的front的成员函数,简单来说就是返回一个头节点的元素引用。 
这里的referrence其实是一个typedef,这个在库里面实现是很常见的  可以这样理解:
typedef reference T;
  back同理如上。
   
// list::front
#include <iostream>
#include <list>int main ()
{std::list<int> mylist;mylist.push_back(77);mylist.push_back(22);// now front equals 77, and back 22mylist.front() -= mylist.back();std::cout << "mylist.front() is now " << mylist.front() << '\n';return 0;
}

输出: 55

4. list modified 元素修改

    list常用的修改方面的有:  头插,头删,尾删,尾插,还有指定位置删除,和指定位置插入,还有指定尾插删除, 以及swap (经常被用于去写构造函数的还有拷贝构造)

4.1 push_front 和pop_front

实现:
// list::push_front
#include <iostream>
#include <list>int main ()
{std::list<int> mylist (2,100);         // two ints with a value of 100mylist.push_front (200);mylist.push_front (300);std::cout << "mylist contains:";for (std::list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
}

实现:
// list::pop_front
#include <iostream>
#include <list>int main ()
{std::list<int> mylist;mylist.push_back (100);mylist.push_back (200);mylist.push_back (300);std::cout << "Popping out the elements in mylist:";while (!mylist.empty()){std::cout << ' ' << mylist.front();mylist.pop_front();}std::cout << "\nFinal size of mylist is " << mylist.size() << '\n';return 0;
}

4.2 push_back和pop_front 

    尾插和尾删 返回值都是void注意哦
  
// list::push_back
#include <iostream>
#include <list>int main ()
{std::list<int> mylist;int myint;std::cout << "Please enter some integers (enter 0 to end):\n";do {std::cin >> myint;mylist.push_back (myint);} while (myint);std::cout << "mylist stores " << mylist.size() << " numbers.\n";return 0;
}

 

// list::pop_back
#include <iostream>
#include <list>int main ()
{std::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';return 0;
}

4.3 insert

  在指定位置(用迭代器哦) 插入一个元素,
     插入n个val 在指定位置
  在指定位置插入一个范围迭代器区间的元素

void insert_test() {int arr[] = { 1,2,3,4,5 };//    1  2  3  4  5int arr1[] = { 11,12,13,14,15 };//    11  12  13  14  15vector<int>v1(arr,arr+5);list<int> mylist(arr1,arr1+5);     //     !list<int>::iterator it = mylist.begin();it++;mylist.insert(it,10);// 迭代器可能存在失效的问题 所以这里一定要给it重赋值   print_list(mylist);// n valit = mylist.begin();it++;mylist.insert(it, 10,1);print_list(mylist);//  范围插入it = mylist.begin();it++;mylist.insert(it, v1.begin()+2, v1.end());print_list(mylist);}
输出:11 10 12 13 14 15
11 1 1 1 1 1 1 1 1 1 1 10 12 13 14 15
11 3 4 5 1 1 1 1 1 1 1 1 1 1 10 12 13 14 15

4.4 erase 删除

 传递的参数是list的迭代器,指定位置删除和指定的迭代器范围区间的元素删除。

std:: iterator& advance(iterator,len) 这是库里的移动迭代器的函数

// erasing from list
#include <iostream>
#include <list>int main ()
{std::list<int> mylist;std::list<int>::iterator it1,it2;// set some values:for (int i=1; i<10; ++i) mylist.push_back(i*10);// 10 20 30 40 50 60 70 80 90it1 = it2 = mylist.begin(); // ^^advance (it2,6);            // ^                 ^++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//        ^std::cout << "mylist contains:";for (it1=mylist.begin(); it1!=mylist.end(); ++it1)std::cout << ' ' << *it1;std::cout << '\n';return 0;
}

5. 迭代器失效问题

   我们之前说过迭代器的实现可以理解为一个封装了node的移动和判断的类。  这里的数据存放是node,所以本质上还是用node*指向了一个节点,所以在list中只要该节点不删除,这个节点依然存在。 
  在list中插入不会导致迭代器失效,因为这个节点并没有被销毁(vector中的迭代器失效是因为他们的内存是连续的,当扩容的时候可能导致旧内存被销数据被移动到新内存)但是list的底层是双向循环链表,内存之间都是用指针(地址)连接起来的,不用在乎是否连续,你不主动销毁内存是不会被销毁的。
   所以只有当删除的时候才会被销毁。

谈谈erase和insert的返回值

   erase:
iterator erase (iterator position);
iterator erase (iterator first, iterator last); 

insert:

single element (1)
iterator insert (iterator position, const value_type& val);
fill (2)
void insert (iterator position, size_type n, const value_type& val);
range (3)
template <class InputIterator>void insert (iterator position, InputIterator first, InputIterator last);
insert:大体上分为两,一种是插入一个元素,一个是插入多个元素。
返回值:简单理解就是返回插入元素后的下一个位置的元素
erase则是返回删除元素的下一个位置
处理迭代器失效: 就是重新赋值给迭代器:
错误实例 没有重新赋值 
修正: 
   代码: 
void TestListIterator1()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给//其赋值it =l.erase(it);++it;}
}

http://www.lryc.cn/news/613867.html

相关文章:

  • 《Java枚举类深度解析:定义与实战应用》
  • 一洽客服系统:APP路由等级与路由条件设置
  • SITIME汽车时钟发生器Chorus保障智能汽车安全
  • 【MATLAB技巧】打开脚本(m文件)后,中文乱码的解决方案
  • TensorFlow深度学习实战(29)——自监督学习(Self-Supervised Learning)
  • element plus table 表格操作列根据按钮数量自适应宽度
  • 宝龙地产债务化解解决方案二:基于资产代币化与轻资产转型的战略重构
  • (1-9-1) Maven 特性、安装、配置、打包
  • Electron——窗口
  • linux mysql 8.X主从复制
  • 【Linux】从零开始:RPM 打包全流程实战万字指南(含目录结构、spec 编写、分步调试)
  • 避免“卡脖子”!如何减少内存I/O延迟对程序的影响?
  • Function + 异常策略链:构建可组合的异常封装工具类
  • 二叉树、算法
  • 防火墙概述
  • React 原生部落的生存现状:观察“Hooks 猎人“如何用useEffect设陷阱反被依赖项追杀
  • 【Unity3D实例-功能-跳跃】角色跳跃
  • Rocky Linux 10.0下安装使用KVM虚拟机
  • 破界之光:DeepSeek 如何重构AI搜索引擎的文明坐标 || #AIcoding·八月创作之星挑战赛#
  • Mac上安装和配置MySQL(使用Homebrew安装MySQL 8.0)
  • [202403-E]春日
  • 等保测评-Nginx中间件
  • DM8数据库服务正常,但是登录报错 [-70019]:没有匹配的可登录服务器
  • cAdvisor 容器监控软件学习
  • docker下载安装和使用(Hyper-V方式)
  • Socket编程预习
  • AI赋能SEO关键词优化策略
  • 深入理解 robots.txt:网站与搜索引擎的 “沟通协议”
  • sqli-labs通关笔记-第38关 GET字符型堆叠注入(单引号闭合 手工注入+脚本注入两种方法)
  • Dubbo应用开发之基于xml的第一个Dubbo程序