一、标准库的list类
list的底层是一个带哨兵位的双向循环链表结构
对比forward_list的单链表结构,list的迭代器是一个双向迭代器
与vector等顺序结构的容器相比,list在任意位置进行插入删除的效率更好,但是不支持任意位置的随机访问
list是一个模板类,在使用的时候我们需要给出元素的类型使用list类时,需要包含头文件<list>
二、list类的常用接口
1.常用的构造函数
1.默认构造函数,构造一个空的链表
list();
例如:
void test()
{list<int> lt;
}
2.构造一个list对象并用n个val初始化
list(size_type n, const value_type& val = value_type());
例如:
void test()
{list<int> lt(3, 1);for (auto i : lt){cout << i << " ";}
}
运行结果:
3.list类的拷贝构造函数
list(const list& x);
例如:
void test()
{list<int> lt(3, 1);list<int> lt1(lt);for (auto i : lt1){cout << i << " ";}
}
运行结果:
4.使用迭代器的初始化构造
Template<class InputIterator>list(InputIterator first, InputIterator last);
例如:
void test()
{list<int> lt(3, 1);list<int> lt1(lt.begin(), lt.end());for (auto i : lt1){cout << i << " ";}
}
运行结果:
2.常用的容量接口
还是老几样,我这里一块讲一讲吧。
1.size,empty,max_size,resize
size:返回链表容器中的元素数。
size_type size() const;
empty:返回列表容器是否为空(即其大小是否为 0)。
(注意:此函数不会以任何方式修改容器。若要清除链表容器的内容,请参阅 list::clear。)bool empty() const;
max_size(不常用):返回链表容器可以容纳的最大元素数。
(由于已知的系统或库实现限制,这是容器可以达到的最大潜在大小,但绝不能保证容器能够达到该大小:在达到该大小之前,它仍然可能无法在任何时候分配存储。)size_type max_size() const;
resize:调整容器的大小,使其包含 n 个元素。
如果 n 小于当前容器大小,则内容将减少到其前 n 个元素,删除超出(并销毁它们)的元素。
如果 n 大于当前容器大小,则通过在末尾插入所需数量的元素来扩展内容,以达到 n 的大小。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将进行值初始化。
(请注意,此函数通过插入或擦除容器中的元素来更改容器的实际内容。)void resize (size_type n, value_type val = value_type());
例如:
void test()
{list<int> lt(3, 1);/*list<int> lt1(lt);*/list<int> lt1(lt.begin(), lt.end());list<int> lt2;cout << lt1.size() <<endl;cout << lt1.empty() <<endl;cout << lt2.empty() <<endl;lt1.resize(10);cout << lt1.size() << endl;for (auto i : lt1){cout << i << " ";}
}
运行结果:
3. 常用的访问与遍历
1.迭代器
iterator begin();const_iterator begin() const;iterator end();const_iterator end() const;
迭代器:用于获取链表中第一个节点的位置和最后一个节点的下一个位置(即哨兵位)
例如:
void test()
{list<int> lt(5, 2);list<int> ::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}
}
运行结果:
2.反向迭代器
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
反向迭代器,rbegin获取容器中最后一个节点的位置,rend获取容器中哨兵位的位置
void test()
{list<int> lt = { 1,23,4,4,5,2 };list<int> ::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";++rit;}
}
运行结果:
注意:反向迭代器rit也要用++而不是--
3.front和back
front:
reference front();
const_reference front() const;
返回链表中第一个节点存储的数据的引用
back:
reference back();
const_reference back() const;
返回链表中最后一个节点存储的数据的引用
例如:
void test()
{list<int> lt = {213123,123,34524,213};cout << lt.front() << endl;cout << lt.back() << endl;}
运行结果:
4.list的增删查改
1)push_front和pop_front
push_front叫头插,从链表头部插入一个元素
void push_front(const value_type& val);
pop_front叫头删,从链表头部删除一个元素
void pop_front();
void pop_front();
例如:
void test()
{list<int> lt = {213123,123,34524,213};cout << lt.front() << endl;lt.push_front(21345);cout << lt.front() << endl;lt.pop_front();cout << lt.front() << endl;
}
运行结果:
2) push_back和pop_back
push_back叫尾插,从链表尾部插入一个元素
void push_back(const value_type& val);
pop_back叫尾删,从链表尾部删除一个元素
void pop_back();
例如:
void test()
{list<int> lt = {213123,123,34524,213};cout << lt.back() << endl;lt.push_back(21345);cout << lt.back() << endl;lt.pop_back();cout << lt.back() << endl;
}
运行结果:
3) find
template <class InputIterator, class T>InputIterator find(InputIterator first, InputIterator last, const T& val);
在两个迭代器区间寻找val并返回其所在处的迭代器
例如:
void test()
{list<int> lt;for (int i = 0; i < 3; i++){lt.push_back(i);}list<int>::iterator pos = find(lt.begin(), lt.end(), 1);*pos = 114514;for (auto i : lt){cout << i << " ";}
}
运行结果:
注意:该函数并非list的成员函数,是标准库中的函数,多个容器共用该find函数
可以看到,我们可以直接对list的迭代器进行解引用并修改其内容,但是迭代器不应该指向节点吗?为什么对其解引用能修改数据呢?
实际上list的迭代器并不是用原生态指针进行模拟实现的,需要进行底层的封装,这里会在list的模拟实现的源代码中体现。
4)insert
iterator insert(iterator position, const value_type& val);
void insert(iterator position, size_type n, const value_type& val);
————————————————————————————————————————
template<class InputIterator>
void insert(iterator position, InputIterator first, InputIterator last);
在position位置的前面插入一个或多个元素
例如:
void test()
{list<int> lt;for (int i = 0; i < 3; i++){lt.push_back(i);}list<int>::iterator pos = find(lt.begin(), lt.end(), 1);lt.insert(pos, 8848);for (auto i : lt){cout << i << " ";}
}
运行结果:
细心的同学可能已经发现了,list的insert操作是不会导致迭代器失效的,因为pos指向的节点不变,相对位置也不变。
但是list的删除操作一定会导致迭代器失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
5) erase
iterator erase(iterator position);iterator erase(iterator first, iterator last);
删除position位置的元素或者 [first,last) 区间的所有元素
例如:
void test()
{list<int> lt;for (int i = 0; i < 3; i++){lt.push_back(i);}list<int>::iterator pos = find(lt.begin(), lt.end(), 1);lt.erase(lt.begin());for (auto i : lt){cout << i << " ";}
}
运行结果:
void test()
{list<int> lt;for (int i = 0; i < 3; i++){lt.push_back(i);}list<int>::iterator pos = find(lt.begin(), lt.end(), 2);lt.erase(lt.begin(),pos);for (auto i : lt){cout << i << " ";}
}
运行结果:
6) swap
void swap(vector& x);
交换两个list对象
例如:
void test()
{list<int> lt;list<int> lt2(6, 12);for (int i = 0; i < 10; i++){lt.push_back(i);}lt.swap(lt2);for (auto i : lt2){cout << i << " ";}cout << endl;
}
运行结果:
7)assign
template <class InputIterator>
void assign(InputIterator first, InputIterator last);
void assign(size_type n, const value_type& val);
为list指定新内容,替换其当前内容并修改节点个数
例如:
void test()
{list<int> lt;for (int i = 0; i < 10; i++){lt.push_back(i);}lt.assign(4, 0);for (auto i : lt){cout << i << " ";}
}
运行结果:
8)clear
void clear();
删除链表所有节点
例如:
void test()
{list<int> lt;for (int i = 0; i < 10; i++){lt.push_back(i);}lt.clear();cout << lt.size() << endl;
}
运行结果:
5.list的顺序修改接口
(1)sort
void sort();
template<class Compare>
void sort(Compare comp);
list由于结构的特殊性,是无法使用标准库中的sort函数的,因为迭代器无法进行相减的操作
并且在C++文档中,我们可以看到标准库中的sort也限定了迭代器的类型必须是可以进行随机访问(RandomAccess)的
default (1)
template <class RandomAccessIterator>void sort (RandomAccessIterator first, RandomAccessIterator last);
custom (2)
template <class RandomAccessIterator, class Compare>void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
迭代器有几个功能分类:
- 单向迭代器,只能进行++
- 双向迭代器,可以++和--
- 随机迭代器,可以++,--,+和-
但是list的sort函数也是没什么必要的,因为直接对链表排序的效率比拷贝到vector进行排序再拷贝回链表要慢的多。
(2)reverse
template <class BidirectionalIterator>void reverse (BidirectionalIterator first, BidirectionalIterator last);
对链表进行逆置
例如:
void test()
{list<int> lt;for (int i = 0; i < 10; i++){lt.push_back(i);}for (auto i : lt){cout << i << " ";}cout << endl;lt.reverse();for (auto i : lt){cout << i << " ";}cout << endl;}
运行结果:
如有错误,欢迎指正。