- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素.
list实现的基本框架:

先贴上官方文档:list文档介绍
一、 对于list的构造我们有四种构造方式:
构造空的list、构造含有n个值为val的元素初始化list、拷贝构造、区间(first,last)元素构造list。
void TestList1()
{
list<int> l1; // 构造空的l1
list<int> l2(4, 100); // l2中放4个值为100的元素
list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
list<int> l4(l3); // 用l3拷贝构造l4
// 以数组为迭代器区间构造l5
int array[] = { 16,2,77,29 };
list<int> l5(array, array + sizeof(array) / sizeof(int));
// 列表格式初始化C++11
list<int> l6{ 1,2,3,4,5 };
// 用迭代器方式打印l5中的元素
list<int>::iterator it = l5.begin();
while (it != l5.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// C++11范围for的方式遍历
for (auto& e : l5)
cout << e << " ";
cout << endl;
}
二、ist迭代器的使用:
1.在使用上时可以将迭代器理解成一个指针,该指针指向list中的某个节点。同时提供了正向迭代器和反向迭代器。
2… 注意:begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
// 注意这里调用的是list的 begin() const,返回list的const_iterator对象
for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
{
cout << *it << " ";
// *it = 10; 编译不通过
}
cout << endl;
}
void TestList2()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 使用正向迭代器正向list中的元素
// list<int>::iterator it = l.begin(); // C++98中语法
auto it = l.begin(); // C++11之后推荐写法
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 使用反向迭代器逆向打印list中的元素
// list<int>::reverse_iterator rit = l.rbegin();
auto rit = l.rbegin();
while (rit != l.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
三、list的空间大小

四、list元素获取

五、list的元素操作
1.对于list来说,insert不会导致迭代器失效,vector存在迭代器失效是因为在扩容时reserve采取异地扩容的方式,这就导致原有迭代器指向了已经被释放的空间。
但list并不存在扩容这样的操作,list直接按需申请空间,你要插入多少个节点,那我就申请多少个节点,然后将所有的节点链接到哨兵节点前面就好了,所以insert之后迭代器依旧可以继续使用,因为他对应的节点空间不会被销毁,依旧好好的存在着。
2.对于list的erase来说就不一样了,erase会释放迭代器对应节点的空间,自然erase之后迭代器就会失效,如果想要继续使用迭代器,则可以利用erase的返回值来更新迭代器,erase会返回被删除节点的下一个节点的迭代器,我们可以用erase的返回值来更新迭代器
// list插入和删除
// push_back/pop_back/push_front/pop_front
void TestList3()
{
int array[] = { 1, 2, 3 };
list<int> L(array, array + sizeof(array) / sizeof(array[0]));
// 在list的尾部插入4,头部插入0
L.push_back(4);
L.push_front(0);
PrintList(L);
// 删除list尾部节点和头部节点
L.pop_back();
L.pop_front();
PrintList(L);
}
// insert /erase
void TestList4()
{
int array1[] = { 1, 2, 3 };
list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
// 获取链表中第二个节点
auto pos = ++L.begin();
cout << *pos << endl;
// 在pos前插入值为4的元素
L.insert(pos, 4);
PrintList(L);
// 在pos前插入5个值为5的元素
L.insert(pos, 5, 5);
PrintList(L);
// 在pos前插入[v.begin(), v.end)区间中的元素
vector<int> v{ 7, 8, 9 };
L.insert(pos, v.begin(), v.end());
PrintList(L);
// 删除pos位置上的元素
L.erase(pos);
PrintList(L);
// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
L.erase(L.begin(), L.end());
PrintList(L);
}
其实list还提供了一个sort函数,但因为效率很低,我们基本上不会用,对比同样的算法库函数sort,如果你要排序list,倒不如先将list的数据拷贝到vector进行排序,等排完序再将数据拷贝回list里面去,就算这样的排序的性能都是要比直接用list进行排序的性能要高不少 ---- 一定要release下进行对比,能差一倍!
测试代码:
#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
#include<time.h>
#define N 100000
int main()
{
list<int> lt1,lt2;
vector<int> v;
for (int i = 0; i < N; ++i)
{
auto e = rand();
lt1.push_back(e);
lt2.push_back(e);
}
//拷贝到vector进行排序
int begin1 = clock();
for (auto e : lt1)
{
v.push_back(e);
}
sort(v.begin(), v.end()); //调用算法库sort进行排序
size_t i = 0;
for (auto& e : lt1) //将数据拷贝回去
{
e = v[i++];
}
int end1 = clock();
int begin2 = clock();
lt2.sort();
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("list sort: %d\n", end2 - begin2);
}

算法库的sort底层用的是快速排序,为了key值选的合适,快排会进行三数取中,所以会进行迭代器的作差,而list的双向迭代器肯定不支持做差,所以调用算法库的sort就会报错。
如果想要排序链表,那就只能调用list类的成员函数sort来进行排序,list的sort底层用的是归并排序。
这里就要引出迭代器从功能上来说,可以分为三类:只能++的单向迭代器(单链表、哈希表),既能++也能 - - 的双向迭代器(list带头双向循环链表),既能++也能 - - 还能±某个具体的数的随机迭代器(string、vector)

resize用于调整链表的空间,如果是调整大一些,那就是一个一个的申请节点,尾插到链表上面去。如果是调整小一些,那也需要一个个的释放节点,相当于尾删节点。
// insert /erase
void TestList4()
{
int array1[] = { 1, 2, 3 };
list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
// 获取链表中第二个节点
auto pos = ++L.begin();
cout << *pos << endl;
// 在pos前插入值为4的元素
L.insert(pos, 4);
PrintList(L);
// 在pos前插入5个值为5的元素
L.insert(pos, 5, 5);
PrintList(L);
// 在pos前插入[v.begin(), v.end)区间中的元素
vector<int> v{ 7, 8, 9 };
L.insert(pos, v.begin(), v.end());
PrintList(L);
// 删除pos位置上的元素
L.erase(pos);
PrintList(L);
// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
L.erase(L.begin(), L.end());
PrintList(L);
}
// resize/swap/clear
void TestList5()
{
// 用数组来构造list
int array1[] = { 1, 2, 3 };
list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
PrintList(l1);
// 交换l1和l2中的元素
list<int> l2;
l1.swap(l2);
PrintList(l1);
PrintList(l2);
// 将l2中的元素清空
l2.clear();
cout << l2.size() << endl;
}
迭代器的价值是什么:
a.迭代器对底层的实现进行封装,不暴露底层实现的细节。
b.提供统一的访问方式,降低使用者的使用成本。
template<class T, class Ref, class Ptr>三个模板参数分别是提供 T& 、const T& 和提供解引用访问(→)//迭代器 --- 自定义类型封装node*
//分类:1.原生指针 2.用自定义类型对原生指针的封装,模拟指针的行为
//即->迭代器: 要么就是原生指针,要么就是自定义类型对原生指针的封装,本质都是模拟指针的行为!
template<class T, class Ref, class Ptr> //通过增加模板参数,提供多版本迭代器
struct _list_iterator
{
typedef list_node<T> node; //加上模板参数才是类型,否则只是类名
typedef _list_iterator<T, Ref, Ptr> self; //方便后面返回值,做参数
node* _node; //指针
_list_iterator(node* n) // 提供构造函数,遍历使用
:_node(n)
{}
Ref operator* () //提供const版本迭代器
{
return _node->_data;
}
//const 迭代器和普通迭代器的区别是什么? 一个内容可修改 ,一个内容不可被修改
Ptr operator->() //->重载
{
return &_node->_data; //引用返回减少拷贝
}
//++返回什么? 返回迭代器呀!
self& operator++() //遍历 前置++
{
_node = _node->_next;
return *this;
}
self& operator++(int) //后置++
{
self tmp(*this); //拷贝构造一下
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self& operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
bool operator ==(const self& s)
{
return _node == s._node;
}
};
10.对于迭代器的封装: 底层物理上都是指向这个对象的4个字节的指针。但语法的千差万别,主要还是运算符重载!

//insert函数 pos前插入x
//insert这里迭代器会不会失效? 不会,pos指向节点不会发生地址改变
void insert(iterator pos, const T& x)
{
node* cur = pos._node; //指针
node* prev = cur->_prev;
node* new_node = new node(x); //堆区申请
prev->_next = new_node;
new_node->_prev = prev;
new_node->_next = cur;
cur->_prev = new_node;
}
//erase函数 删除pos位置
void erase(iterator pos)
{
assert(pos != end()); //防止删除哨兵节点
node* next = pos._node->_next;
node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
}

//赋值重载
list<T>& operator=(const list<T> l)
{
swap(l);
return *this;
}
//反向迭代器
// 适配器 -- 复用
template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{
Iterator _it;
typedef Reverse_iterator<Iterator, Ref, Ptr> Self;
Reverse_iterator(Iterator it)
:_it(it)
{}
Ref operator*()
{
Iterator tmp = _it;
return *(--tmp);
}
Ptr operator->()
{
return &(operator*());
}
Self& operator++()
{
--_it;
return *this;
}
Self& operator--()
{
++_it;
return *this;
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
};
vector
优点:
a.支持下标的随机访问
b.尾插尾删效率高
c.CPU高速缓存命中率高(局部性原理)
缺点:
a.前面部分插入删除数据效率低(插入和删除需要挪动数据,尤其是数据量大时效率较低)
b.扩容有消耗,还存在一定空间浪费(2倍扩容是比较合适的,开大了浪费空间,开小了需要频繁扩容)
list
优点:
a.按需申请释放空间,无需扩容
b.任意位置插入删除的时间复杂度是O(1),不考虑find查找位置,只是单纯erase和insert。
缺点:
a.不支持下标的随机访问
b.CPU高速缓存命中率低(局部性原理)
少熬夜,早点休息!
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复
假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl
在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定
我有一个gem,它有一个根据Rails.env的不同行为的方法:defself.envifdefined?(Rails)Rails.envelsif...现在我想编写一个规范来测试这个代码路径。目前我是这样做的:Kernel.const_set(:Rails,nil)Rails.should_receive(:env).and_return('production')...没关系,只是感觉很丑。另一种方法是在spec_helper中声明:moduleRails;end而且效果也很好。但也许有更好的方法?理想情况下,这应该有效:rails=double('Rails')rails.sho