构造函数 | 接口说明 |
---|---|
list() | 构造空的list |
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
#include <iostream>
#include <list>
int main()
{
std::list<int> l1; // 构造空的l1
std::list<int> l2(4, 100); // l2中放4个值为100的元素
std::list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
std::list<int> l4(l3); // 用l3拷贝构造l4
// 以数组为迭代器区间构造l5
int array[] = { 16, 2, 77, 29 };
std::list<int> l5(array, array + sizeof(array) / sizeof(int));
// 用迭代器方式打印l5中的元素
for (std::list<int>::iterator it = l5.begin(); it != l5.end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
for (auto& e : l5)
std::cout << e << " ";
std::cout << std::endl;
system("pause");
return 0;
}
函数声明 | 接口说明 |
---|---|
begin() | 返回第一个元素的迭代器 |
end() | 返回最后一个元素下一个位置的迭代器 |
rbegin() | 返回第一个元素的reverse_iterator,即end位置 |
rend() | 返回最后一个元素下一个位置的reverse_iterator,即begin位置 |
cbegin() | (C++11) 返回第一个元素的cosnt_iterator |
cend() | (C++11) 返回最后一个元素下一个位置的const_iterator |
crbegin() | (C++11) 即crend()位置 |
crend() | (C++11) 即crbegin()位置 |
函数生命 | 接口说明 |
---|---|
bool empty() const | 检测list是否为空,是返回true,否则返回false |
size_t size() const | 返回list中有效节点的个数 |
#include <iostream>
using namespace std;
#include <list>
int main()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 打印list中有效节点的个数
cout << l.size() << endl;
// 检测list是否为空
if (l.empty())
cout << "空的list" << endl;
else
{
for (const auto& e : l)
cout << e << " ";
cout << endl;
}
return 0;
}
函数声明 | 接口说明 |
---|---|
reference front() | 返回list的第一个节点中值的引用 |
const_reference front() const | 返回list的第一个节点中值的const引用 |
reference back() | 返回list的最后一个节点中值的引用 |
const_reference back() const | 返回list的最后一个节点中值的const引用 |
#include <iostream>
using namespace std;
#include <list>
int main()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l1(array, array + sizeof(array) / sizeof(array[0]));
for (auto& e : l1)
cout << e << " ";
cout << endl;
// 将list中第一个节点与最后一个节点中的值改为10
l1.front() = 10;
l1.back() = 10;
for (auto& e : l1)
cout << e << " ";
cout << endl;
const list<int> l2(array, array + sizeof(array) / sizeof(array[0]));
//int& a = l2.front();// 因为l2是const类型的list对象,因此其front()为const 引用类型
const int& ca = l2.front();
system("pause");
return 0;
}
函数声明 | 接口说明 |
---|---|
void push_front (const value_type& val) | 在list首元素前插入值为val的元素 |
void pop_front() | 删除list中第一个元素 |
void push_back (const value_type& val) | 在list尾部插入值为val的元素 |
void pop_back() | 删除list中最后一个元素 |
template <class… Args>void emplace_front (Args&&… args) (C++11) | 在list第一个元素前根据参数直接构造元素 |
template <class… Args>void emplace_back (Args&&… args) (C++11) | 在list最后一个元素后根据参数直接构造元素 |
template <class… Args>iterator emplace( const_iterator position, Args&&… args) (C++11) | 在链表的任意位置根据参数直接构造元素 |
iterator insert (iterator position, const value_type& val) | 在list position 位置中插入值为val的元素 |
void insert (iterator position, size_type n, const value_type& val) | 在list position位置插入n个值为val的元素 |
void insert (iterator position, InputIterator first, InputIterator last) | 在list position位置插入[first, last)区间中元素 |
iterator erase (iterator position) | 删除list position位置的元素 |
iterator erase (iterator first, iterator last) | 删除list中[first, last)区间中的元素 |
void swap (list& x) | 交换两个list中的元素 |
void resize (size_type n, value_type val = value_type()) | 将list中有效元素个数改变到n个,多出的元素用val填充 |
void clear() | 清空list中的有效元素 |
#include <list>
#include<iostream>
#include<vector>
using namespace std;
void PrintList(list<int>& l)
{
for (auto& e : l)
cout << e << " ";
cout << endl;
}
//=========================================================================================
// push_back/pop_back/push_front/pop_front
void TestList1()
{
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);
}
//=========================================================================================
// emplace_back / emplace_front / emplace
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{
cout << "Date(int, int, int):" << this << endl;
}
Date(const Date& d)
: _year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date(const Date&):" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
// push_back尾插:先构造好元素,然后将元素拷贝到节点中,插入时先调构造函数,再调拷贝构造函数
// emplace_back尾插:先构造节点,然后调用构造函数在节点中直接构造对象
// emplace_back比push_back更高效,少了一次拷贝构造函数的调用
void TestList2()
{
list<Date> l;
Date d(2018, 10, 20);
l.push_back(d);
l.emplace_back(2018, 10, 21);
l.emplace_front(2018, 10, 19);
}
//=========================================================================================
// insert /erase
void TestList3()
{
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 TestList4()
{
// 用数组来构造list
int array1[] = { 1, 2, 3 };
list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
PrintList(l1);
// 将l1中元素个数增加到10个,多出的元素用默认值填充
// (注意:如果list中放置的是内置类型,默认值为0, 如果list中放置自定义类型元素,调用缺省构造函数)
l1.resize(10);
PrintList(l1);
// 将l1中的元素增加到20个,多出的元素用4来填充
l1.resize(20, 4);
PrintList(l1);
// 将l1中的元素减少到5个
l1.resize(5);
PrintList(l1);
// 用vector中的元素来构造list
vector<int> v{ 4, 5, 6 };
list<int> l2(v.begin(), v.end());
PrintList(l2);
// 交换l1和l2中的元素
l1.swap(l2);
PrintList(l1);
PrintList(l2);
// 将l2中的元素清空
l2.clear();
cout << l2.size() << endl;
}
int main()
{
TestList1();
TestList2();
TestList3();
TestList4();
system("pause");
return 0;
}
迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
#include<iostream>
#include<list>
using namespace std;
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时,必须先给其赋值
l.erase(it);
++it;
}
}
// 改正
void TestListIterator()
{
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())
{
l.erase(it++); // it = l.erase(it);
}
}
int main()
{
//TestListIterator1();
TestListIterator();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
namespace fxl{
// List的节点类
template<class T>
struct ListNode{
ListNode(const T& val=T())
:_pre(nullptr)
,_next(nullptr)
,_val(val){
}
ListNode* _pPre;
ListNode* _pNext;
T _val;
};
/*
List 的迭代器
迭代器有两种实现方式:
1. 原生态指针,比如:vector
2. 将原生态指针进行封装,因迭代器的使用形式与指针完全相同,因此,在自定义的类中必须实现以下方
法:
1. 指针可以解引用,迭代器的类中必须重载operator*()
2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前
移 动,所以需要重载,如果是forward_list就不需要重载--
4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
*/
template<class T, class Ref, class Ptr>
class ListIterator
{
typedef ListNode<T>* PNode;
typedef ListIterator<T, Ref, Ptr> Self;
public:
ListIterator(PNode pNode = nullptr)
: _pNode(pNode)
{}
ListIterator(const Self& l)
: _pNode(l._pNode)
{}
T& operator*()
{
return _pNode->_val;
}
T* operator->()
{
return &(operator*());
}
Self& operator++()
{
_pNode = _pNode->_pNext;
return *this;
}
Self operator++(int)
{
Self temp(*this);
_pNode = _pNode->_pNext;
return temp;
}
bool operator!=(const Self& l)
{
return _pNode != l._pNode;
}
bool operator==(const Self& l)
{
return _pNode != l._pNode;
}
PNode _pNode;
};
/*
List 的反向迭代器,反向迭代器与正向迭代器刚好是相反的,反向迭代器++,迭代器往前移动,反向迭代
器--, 迭代器往后移动,因此反向迭代器可以在正向迭代器的基础之上来实现
*/
template<class T,class Ref,class Ptr,class Iterator>
class ListReverseIterator
{
typedef ListReverseIterator<T, Ref, Ptr, Iterator> Self;
ListReverseIterator(const Iterator& it)
:_it(it)
{}
ListReverseIterator(const Self& s)
:_it(s._it){
}
Ref operator*(){
Iterator temp = _it;
return temp;
}
Ptr operator->(){
return &(operator*());
}
// 反向迭代器的++,就是正向迭代器的--
Self operator++(){
_it--;
return *this;
}
Self operator++(int){
Iterator temp(_it);
--_it;
return temp;
}
// 反向迭代器的--,就是正向迭代器的++
Self operator--(){
_it++;
return *this;
}
Self operator--(int){
Iterator temp(_it);
++_it;
return temp;
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
bool operator==(const Self& s)
{
return _it == s._it;
}
private:
Iterator _it;
};
template<class T>
class List{
typedef ListNode<T> Node;
typedef Node* PNode;
public:
typedef ListIterator<T, T&, T*> Iterator;
typedef ListIterator<T, const T&, const T&> ConstIterator;
typedef ListReverseIterator<T, T&, T*, Iterator> ReverseIterator;
typedef ListReverseIterator<T, const T&, const T*, ConstIterator> ConstReverseIterator;
public:
List()
{
CreateHead();
}
List(int n, const T& value=T()){
CreateHead();
for (int i = 0; i < n; i++)
PushBack(value);
}
template<class Iterator>
List(Iterator first,Iterator last){
CreateHead();
while (first != last){
PushBack(*first);
first++:
}
}
List(const List<T>& l){
CreateHead();
List<T> temp(l.Begin(), l.End());
this->Swap(temp);
}
List<T>& operator=(const List<T>& l){
if (this != &l)
{
List<T> temp(l);
this->Swap(temp);
}
return *this;
}
~List()
{
Clear();
delete _pHead;
_pHead = nullptr;
}
///
// List Iterator
Iterator Begin(){
return Iterator(_pHead->_pNext);
}
Iterator End()
{
return Iterator(_pHead);
}
ReverseIterator RBegin()
{
return ReverseIterator(End());
}
ReverseIterator REnd()
{
return ReverseIterator(Begin());
}
ConstIterator CBegin()const
{
return ConstIterator(_pHead->_pNext);
}
ConstIterator CEnd()const
{
return ConstIterator(_pHead);
}
ConstReverseIterator CRBegin()const
{
return ConstReverseIterator(CEnd());
}
ConstReverseIterator CREnd()const
{
return ConstReverseIterator(CBegin());
}
///
// List Capacity
size_t Size()const
{
size_t count = 0;
PNode pCur = _pHead->_pNext;
while (pCur != _pHead)
{
++count;
pCur = pCur->_pNext;
}
return count;
}
bool Empty()const
{
return _pHead->_pNext == _pHead;
}
void ReSize(size_t newSize, const T& val = T())
{
size_t oldSize = Size();
if (oldSize <= newSize)
{
for (size_t i = oldSize; i < newSize; ++i)
PushBack(val);
}
else
{
for (size_t i = newSize; i < oldSize; ++i)
PopBack();
}
}
// List Access
T& Front()
{
return _pHead->_pNext->_val;
}
const T& Front()const
{
return _pHead->_pNext->_val;
}
T& Back()
{
return _pHead->_pPre->_val;
}
const T& Back()const
{
return _pHead->_pPre->_val;
}
// List Modify
void PushBack(const T& val)
{
PNode pNewNode = new Node(val);
// 先把新节点尾插进去
pNewNode->_pNext = _pHead;
pNewNode->_pPre = _pHead->_pPre;
// 再链接剩余两个指针
_pHead->_pPre = pNewNode;
pNewNode->_pPre->_pNext = pNewNode;
}
// 尾删
void PopBack()
{
// 找到待删除节点
PNode pDel = _pHead->_pPre;
if (pDel != _pHead)
{
_pHead->_pPre = pDel->_pPre;
pDel->_pPre->_pNext = _pHead;
delete pDel;
}
}
// 头插
void PushFront(const T& val)
{
PNode pNewNode = new Node(val);
// 先把新节点尾插进去
pNewNode->_pNext = _pHead->_pNext;
pNewNode->_pPre = _pHead;
// 再链接剩余两个指针
_pHead->_pNext = pNewNode;
pNewNode->_pNext->_pPre = pNewNode;
}
// 头删
void PopFront()
{
// 找到待删除节点
PNode pDel = _pHead->_pNext;
if (pDel != _pHead)
{
_pHead->_pNext = pDel->_pNext;
pDel->_pNext->_pPre = _pHead;
delete pDel;
}
}
// 在pos位置前插入值为val的节点
Iterator Insert(Iterator pos, const T& val)
{
PNode pNewNode = new Node(val);
PNode pCur = pos._pNode;
// 先将新节点插入
pNewNode->_pPre = pCur->_pPre;
pNewNode->_pNext = pCur;
pNewNode->_pPre->_pNext = pNewNode;
pCur->_pPre = pNewNode;
return Iterator(pNewNode);
}
// 删除pos位置的节点,返回该节点的下一个位置
Iterator Erase(Iterator pos)
{
// 找到待删除的节点
PNode pDel = pos._pNode;
PNode pRet = pDel->_pNext;
// 将该节点从链表中拆下来并删除
pDel->_pPre->_pNext = pDel->_pNext;
pDel->_pNext->_pPre = pDel->_pPre;
delete pDel;
return Iterator(pRet);
}
void Clear()
{
PNode pCur = _pHead->_pNext;
while (pCur != _pHead)
{
_pHead->_pNext = pCur->_pNext;
delete pCur;
pCur = _pHead->_pNext;
}
_pHead->_pNext = _pHead;
_pHead->_pPre = _pHead;
}
void Swap(List<T>& l)
{
swap(_pHead, l._pHead);
}
private:
void CreateHead()
{
_pHead = new Node;
_pHead->_pPre = _pHead;
_pHead->_pNext = _pHead;
}
private:
PNode _pHead;
};
}
vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:
vector | list | |
---|---|---|
底层结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
随机访问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素效率O(N) |
插入和删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1) |
空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 | 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低 |
迭代器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响 |
使用场景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |