list的基本介绍
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高 效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率 更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间 开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素)
list的函数接口
std::list是C++标准模板库(STL)中的一个双向链表容器。它可以存储任何数据类型,并提供了一系列方法来方便地操作链表。
以下是std::list的一些常用方法:
⭕push_back(数据):在链表末尾添加一个元素。
⭕push_front(数据):在链表头部添加一个元素。
⭕pop_back():从链表末尾删除一个元素。
⭕pop_front():从链表头部删除一个元素。
⭕insert(迭代器,数据):在链表中的任意位置插入一个元素。
⭕remove():删除链表中所有与指定的元素相等的元素。
⭕erase(迭代器):删除链表中的一个或多个元素。
⭕clear():删除链表中的所有元素。
⭕size():返回链表中元素的数量。
⭕empty():检查链表是否为空。
以下是一个使用std::list的示例代码:
#include <iostream>
#include <list>
int main() {
std::list<int> myList; // 声明一个整型链表
// 在链表末尾添加元素
myList.push_back(1);
myList.push_back(2);
myList.push_back(3);
// 在链表头部添加元素
myList.push_front(0);
// 输出链表中的元素
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 在链表中间插入一个元素
auto it = myList.begin();
++it; // 指向第二个元素
myList.insert(it, 100);
// 删除链表中的一个元素
it = myList.begin();
++it; // 指向第二个元素
myList.erase(it);
// 输出链表中的元素
for (auto& elem : myList) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
输出结果为:
0 1 2 3
0 2 3
可以看到,std::list提供了很多方便的方法来操作链表,使用起来非常灵活和方便。
list模拟实现
list_node(节点)
template <class T>
struct list_node
{//节点
list_node* _prev;
list_node* _next;
T _data;
};
链表迭代器
//迭代器实现
template<class T,class Ref>
struct list_iterator
{
typedef list_node<T> node;
node* _node;
list_iterator(node* it)
{
_node = it;
}
//先把list_node重命名,然后创建node*
//重载++、!=、*
list_iterator<T,Ref>& operator++()
{//++
_node= _node->_next;
return *this;//这里重点看一下,我当初实现的时候各种错误
}
//后置++
list_iterator<T,Ref> operator++(int)
{
list_iterator<T,Ref> tem(*this);
_node = _node->_next;
return tem;
}
//前置--
list_iterator<T,Ref>& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
list_iterator<T,Ref> operator--(int)
{
list_iterator<T,Ref> tem(*this);
_node = _node->_prev;
return tem;
}
Ref operator*()
{//*引用重载
return _node->_data;
}
bool operator!=(const list_iterator<T,Ref>& it )
{
return _node != it._node;
}
//重载->
T* operator->()
{
return &(_node->_data);
}
};
list(链表)模拟代码
template<class T>
class List
{//链表实现
public:
typedef list_node<T> node;//这一句要放到类中,不然就是错误模板
typedef list_iterator<T,T&> iterator;//重命名迭代器
typedef list_iterator<T,const T&> const_iterator;
List()
{//链表构造
_head = getnode();
}
//清除数据
void clear()
{
iterator it = begin();
while (it != end())
{//足个删除
it=erase(it);
}
}
//析构函数
~List()
{
clear();
//复用clear,然后清除头节点就行了
delete _head;
_head = nullptr;
}
template<class iterator_create>
List(iterator_create first,iterator_create last)
{//迭代器构造- -同样也是构造函数
_head = getnode();
while (first != last)
{
push_back(*first);
++first;
}
}
//拷贝构造实现
//List(const List<T>& L1)
//{
// _head = getnode();
// for (auto e : L1)
// {//一个遍历直接尾插,e会自动去拿节点的值
// push_back(e);
// }
//}
void swap(List<T>& tem)
{
std::swap(_head,tem._head);
}
List(const List<T>& L1)
{
_head = getnode();
List<T> tem(L1.begin(), L1.end());
swap(tem);
}
//赋值重载
List<T>& operator=(List<T> L1)
{//这里的参数不能用引用
swap(L1);
return *this;
}
//创建节点函数
node* getnode()
{
node* ret = new node;
ret->_next = ret->_prev = ret;
return ret;
}
iterator begin()
{
return iterator(_head->_next);
}
const_iterator begin()const
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end()const //如果一个函数参数是const list,当list调用成员函数的时候,那么就需要this指针
{ //你传参传的时候用const修饰了,但是内部的this指针如果你没有写const版本的成员函数,
return const_iterator(_head);//就会存在一个权限放大的问题
}
void push_back(const T& x = T())
{
node* tem = getnode();//开辟空间
tem->_data = x;
//把节点插入链表.第一步找到尾,尾就是头的前一个
node* tail = _head->_prev;
tail->_next = tem;
tem->_prev = tail;
tem->_next = _head;
_head->_prev = tem;
}
iterator insert(iterator pos,const T& x=T())
{
node* cur = pos._node;//先拿到目前位置的指针
node* prev = cur->_prev;
node* new_node = getnode();//建立新的节点
new_node->_data = x;//赋值
//插入节点
prev->_next = new_node;
new_node->_prev = prev;
new_node->_next = cur;
cur->_prev = new_node;
//完成
return pos;//迭代器不是节点,而是里面有一个节点的信息记住记住
}
iterator erase(iterator pos)
{
assert(pos._node != _head);
node* cur = pos._node;//拿到该节点
node* prev = cur->_prev;
node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
private:
node* _head;
};
list介绍到此告一段落,这篇文章可以当作是c++手册的零食,没有看手册的那么枯燥,但同样也么有那么详细,你在学习list的时候,此时应该学习了并且熟练运用string跟vector。所有关于接口那一块直接一并带过了,你知道的,stl库中各种容器的接口都是类似的。