list类
概念:
1.list底层是**带头节点双向循环链表**。
2. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代已让其更简单高效。
3. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
4. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
常用方法、接口:
构造函数:
构造空的list:
list<int> L;
构造list中包含n个值为val的元素:
list<int> L(size_t n,const T& val);
区间构造list:
list<int>(iterator first,iterator last);//这里的迭代器不是原生态迭代器,需要进行将节点类型的指针重新封装。后面会说到。
拷贝构造:
list<int> L1(10,5);
list<int> L2(L1);
list迭代器的使用:
begin()&end();rbegin()&rend();
注意:
1.begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动。
2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动。
3. list迭代器不是原生态的指针,因为++操作不能指向下一个节点,后面会详细说明。
补充:
list中的迭代器失效只有一种场景:迭代器指向的节点不存在。
在list中因为迭代器不是原生态指针,不能直接++操作取到下一个节点,所以这里我们需要将节点类型的指针封装成迭代器。
list迭代器:将节点类型的指针封装:
步骤:
1.迭代器的构造
list_iterator(Node* pCur)
: _pCur(pCur)
{}
2.将迭代器的对象按照指针的方式应用(例如:* ->)
T& operator*()
{
return _pCur->_data;
}
T* operator->()
{
return &(_pCur->_data);
}
3.让迭代器可以移动(++ --)
Self& operator++()//前置++
{
_pCur = _pCur->_pNext;
return *this;
}
Self operator++(int)//后置++
{
Self temp(*this);
_pCur = _pCur->_pNext;
return temp;
}
Self& operator--()//前置--
{
_pCur = _pCur->_pPre;
return *this;
}
Self operator--(int)//后置--
{
Self temp(*this);
_pCur = _pCur->_pPre;
return temp;
}
注意:自定义类型作为返回值时,尽量返回引用,这样效率高,如果按值得方式返回,还需要创建一个临时对象。(但是需注意若要用引用返回返回值,返回值生命周期必须比此函数长)
4.迭代器之间可以进行比较(== !=)
bool operator!=(const Self& s)
{
return _pCur != s._pCur;
}
bool operator==(const Self& s)
{
return _pCur == s._pCur;
}
补充:
如何给一个类定义迭代器:
1.分析该类的迭代器是原生态指针还是需要对指针进行重新封装。(取决于当前的数据类型:vector:原生态指针, list:需要对原生态指针进行封装)
2.结合该种数据结构,定义迭代器类
3.将迭代器与类结合:在容器类中–》typedef 迭代器类型 iterator(这样写为了统一,方便应用)。
4.容器应该提供:begin()和end()。
list 容量
检测list是否为空,是返回true,否返回false。
list<int> L(10, 5);
if (L.empty())
cout << "空" << endl;
else
cout << "非空" << endl;
返回list中的节点个数
list<int> L(10, 5);
cout << L.size() << endl;
元素访问
访问链表中第一个节点中值
vector<int> v{ 1, 2, 3 };
list<int> L(v.begin(),v.end());
cout << L.front() << endl;
访问链表中最后一个节点中的值
vector<int> v{ 1, 2, 3 };
list<int> L(v.begin(),v.end());
cout << L.back() << endl;
总结:
vector和list的区别: