list的使用
头文件:
#inliude<list>
C++标准库(STL)中的std::list
是一个双向链表容器,它提供了一些有用的操作和功能。与std::vector
相比,std::list
具有以下特点:
-
链表结构:
std::list
使用链表结构实现,每个元素都包含指向前一个和后一个元素的指针,因此在插入和删除元素时,不会涉及内存的重新分配和移动,相对于std::vector
,插入和删除操作的时间复杂度为O(1)。 -
无连续内存:由于
std::list
使用链表实现,它的元素在内存中是不连续存储的。这意味着std::list
不支持随机访问,不能像std::vector
那样使用索引访问元素,只能通过迭代器进行遍历。 -
动态大小:
std::list
的大小可以动态增长和收缩,不需要预先指定容器的大小。 -
插入和删除效率高:由于链表结构的特性,
std::list
在任意位置插入和删除元素的效率都很高,不会引起其他元素的移动。 -
不支持快速随机访问:由于没有连续的内存存储,
std::list
不支持像std::vector
那样的快速随机访问。如果需要通过索引访问元素,应该使用std::vector
。
std::list
提供了一系列成员函数和算法,可以方便地对链表进行操作,例如push_back()
、push_front()
、pop_back()
、pop_front()
、insert()
、erase()
等。此外,std::list
还提供了size()
、empty()
、front()
、back()
等成员函数来获取链表的大小和访问元素。
需要注意的是,由于std::list
的元素在内存中不是连续存储的,因此在访问元素时,性能可能相对较低。如果需要频繁的随机访问和修改元素,std::vector
可能是更好的选择。而std::list
在需要频繁插入和删除元素的场景下,具有更好的性能。
listd的尾插和遍历举例
#include<iostream>
#include<list>
using namespace std;
void test_list1()
{
list<int> lt;
//插入数据(尾插)
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
//遍历:迭代器
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
it = lt.begin(); //迭代器修改值
while (it != lt.end())
{
*it *= 2;
++it;
}
cout << endl;
//遍历:范围for
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_list1();
return 0;
}
头插和尾部弹出
#include<iostream>
#include<list>
using namespace std;
void test_list1()
{
list<int> lt;
//插入数据(尾插)
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
//遍历:迭代器
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
it = lt.begin(); //迭代器修改值
while (it != lt.end())
{
*it *= 2;
++it;
}
cout << endl;
//遍历:范围for
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//头插和尾部弹出
lt.push_front(10);
lt.push_front(10);
lt.push_front(10);
lt.pop_back();
lt.pop_back();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_list1();
return 0;
}
insert和erase的指针失效问题
在使用std::list
的insert()
和erase()
函数时,insert()不会出现指针失效的问题。这是因为std::list
使用链表结构实现,插入和删除元素时只需要调整节点的指针,不会涉及内存的重新分配和移动。
具体来说,当调用insert()
函数插入一个元素时,它会在指定位置之前插入一个新的节点,并调整前一个节点和后一个节点的指针,使得链表仍然保持正确的连接关系。插入操作后,原来存在的节点和迭代器仍然有效。
当调用erase()
函数删除一个元素时,它会删除指定位置的节点,指针的位置会失效。
void test_list2()
{
list<int> lt;
//插入数据(尾插)
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
auto pos = find(lt.begin(), lt.end(), 3);
if (pos != lt.end()) //pos不会失效
{
lt.insert(pos, 30);
*pos *= 100;
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
pos = find(lt.begin(), lt.end(), 4);
if (pos != lt.end()) //pos会失效
{
lt.erase(pos);
//cout << *pos << " "; 此时的pos已经被删除 成了野指针
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_list2();
return 0;
}
自定义实现list
基本框架
#pragma once
namespace b
{
template<class T>
class list_node
{
T _data; //存放数据
list_node<T>* _next; //指向下一个结点
list_node<T>* _prev; //指向前一个结点
list_node(const T& x = T()) //构造函数
:_data(x)
, _next(nullptr)
, _prev(nullptr)
{}
};
template<class T>
class list
{
typedef; list_node<T> Node;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->prev = _head;
}
private:
Node* _head; //头结点
};
}
实现push_back()
将结点信息传入push_back()函数,然后将头结点 尾部结点 和新节点的指针做一一对应即可
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
//_head tail newnode
tail->_next = newnode;
newnode->prev = tail;
newnode->_next = _head;
_head->_next = newnode;
}
iterator
template<class T>
struct _list_iterator
{
typedef list_node<T> Node;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
#pragma once
namespace b
{
template<class T>
class list_node
{
T _data; //存放数据
list_node<T>* _next; //指向下一个结点
list_node<T>* _prev; //指向前一个结点
list_node(const T& x = T()) //构造函数
:_data(x)
, _next(nullptr)
, _prev(nullptr)
{}
};
template<class T>
struct _list_iterator
{
typedef list_node<T> Node;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
};
//
//
//对list中的迭代器进行补充
template<class T>
class list
{
typedef; list_node<T> Node;
public:
typedef _list_iterator<T> iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
list()
{
_head = new Node;
_head->_next = _head;
_head->prev = _head;
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
//_head tail newnode
tail->_next = newnode;
newnode->prev = tail;
newnode->_next = _head;
_head->_next = newnode;
}
private:
Node* _head; //头结点
};
}
iterator中的运算符重载
template<class T>
struct _list_iterator
{
typedef list_node<T> Node;
typedef _list_iterator<T, Ref, Ptr> iterator;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
bool operator!=(const iterator& it) const
{
return _node != it._node;
}
bool operator==(const iterator& it)const
{
return _node == it.node;
}
//*it it.operator*() 解引用
T& operator*()
{
return _node->next;
}
T* operator->() { return &(operator*()); }
// ++it
iterator& operator++()
{
return _node->_next;
}
};
在使用T* operator->() { return &(operator*()); }
输出cout<<it->?
时实际上有两个->-> 为it->->? 编译器优化了一个
在std::list
中,运算符重载->
的作用是用于访问链表中节点的成员。当我们使用->
操作符访问链表中的节点时,实际上是访问了节点的指针,并通过该指针来访问节点的成员。
例如,假设有一个std::list
对象myList
,其中包含了若干个节点,每个节点都有一个成员变量data
。我们可以通过迭代器来遍历链表,并使用->
操作符访问每个节点的data
成员。示例如下:
#include <iostream>
#include <list>
struct Node {
int data;
Node(int d) : data(d) {}
};
int main() {
std::list<Node> myList;
myList.push_back(Node(1));
myList.push_back(Node(2));
myList.push_back(Node(3));
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << it->data << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,我们定义了一个结构体Node
,其中包含一个整型成员变量data
。然后,我们创建了一个std::list
对象myList
,并向其中插入了三个节点。接下来,我们使用迭代器遍历链表,并通过->
操作符访问每个节点的data
成员,将其输出到控制台。
编译器在优化代码时,可能会对->
操作符进行一些优化,例如将其转换为直接访问成员的方式,从而提高代码的执行效率。这种优化是由编译器自动完成的,我们无需手动干预。
需要注意的是,->
操作符的优化可能因编译器和编译选项的不同而有所差异。在实际使用中,我们应该根据具体情况进行测试和评估,以确保代码的正确性和性能。
const iterator
此时我们的iterator并不能处理const类型
#pragma once
namespace b
{
template<class T>
class list_node
{
T _data; //存放数据
list_node<T>* _next; //指向下一个结点
list_node<T>* _prev; //指向前一个结点
list_node(const T& x = T()) //构造函数
:_data(x)
, _next(nullptr)
, _prev(nullptr)
{}
};
//template<class T>
//为处理const类型 改为像指针一样的对象
template<class T,class Ref,class Ptr>
struct _list_iterator
{
typedef list_node<T> Node;
typedef _list_iterator<T, Ref, Ptr> iterator;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
bool operator!=(const iterator& it) const
{
return _node != it._node;
}
bool operator==(const iterator& it)const
{
return _node == it.node;
}
//*it it.operator*() 解引用
Ref operator*()
{
return _node->next;
}
Ref operator->() { return &(operator*()); }
// ++it
iterator& operator++()
{
return _node->_next;
}
};
template<class T>
class list
{
typedef; list_node<T> Node;
public:
typedef _list_iterator<T> iterator;
typedef _list_const_iterator<T,const T&,const T*> const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
list()
{
_head = new Node;
_head->_next = _head;
_head->prev = _head;
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
//_head tail newnode
tail->_next = newnode;
newnode->prev = tail;
newnode->_next = _head;
_head->_next = newnode;
}
private:
Node* _head; //头结点
};
}
insert
思路和push_back类似
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
push_front
利用insert实现
void push_front(const T& x)
{
insert(begin(), x);
}
erease
void erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->prev = prev;
delete cur;
return iterator(next);
}
pop_front
void pop_front()
{
erase(begin());
}
编译器报错
为不发生报错 要在自定义结构体iterator中加入
typedef biddirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef ptrdiff_t difference_type;
具体原因未知?
完整代码
#pragma once
namespace b
{
template<class T>
class list_node
{
T _data; //存放数据
list_node<T>* _next; //指向下一个结点
list_node<T>* _prev; //指向前一个结点
list_node(const T& x = T()) //构造函数
:_data(x)
, _next(nullptr)
, _prev(nullptr)
{}
};
//template<class T>
//为处理const类型 改为像指针一样的对象
template<class T,class Ref,class Ptr>
struct _list_iterator
{
typedef list_node<T> Node;
typedef _list_iterator<T, Ref, Ptr> iterator;
typedef biddirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef ptrdiff_t difference_type;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
bool operator!=(const iterator& it) const
{
return _node != it._node;
}
bool operator==(const iterator& it)const
{
return _node == it.node;
}
//*it it.operator*() 解引用
Ref operator*()
{
return _node->next;
}
Ref operator->() { return &(operator*()); }
// ++it
iterator& operator++()
{
return _node->_next;
}
};
template<class T>
class list
{
typedef; list_node<T> Node;
public:
typedef _list_iterator<T> iterator;
typedef _list_const_iterator<T,const T&,const T*> const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
list()
{
_head = new Node;
_head->_next = _head;
_head->prev = _head;
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
//_head tail newnode
tail->_next = newnode;
newnode->prev = tail;
newnode->_next = _head;
_head->_next = newnode;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
void pop_front()
{
erase(begin());
}
void erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->prev = prev;
delete cur;
return iterator(next);
}
private:
Node* _head; //头结点
};
}