【C++】——list
文章目录
- list介绍和使用
- list注意事项
- list模拟实现
- list和vector的不同
list介绍和使用
在C++中,list是一个带头双向链表
list注意事项
- 迭代器失效
删除元素:当使用迭代器删除一个元素时,指向该元素的迭代器会失效,但是不会影响其他迭代器。- 内部实现:与vector相比,list不支持随机访问,就是说不能通过下标访问元素,但是可以使用迭代器 ,在删除和插入时非常方便
- 元素唯一性:list中的元素是不重复的,如果尝试插入已经存在的元素,该元素将被覆盖。
- 容量:list没有容量概念,根据动态分配内存
- 内存效率:他的插入和删除操作时间复杂度都为O(1)
list模拟实现
- 指针可以解引用,迭代器的类中必须重载operator*()
- 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
- 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
至于operator–()/operator–(int)释放需要重载,
根据具体的结构来抉择,双向链表可以向前 移动,所以需要重载,如果是forward_list就不需要重载–- 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
#pragma once
#include <assert.h>
namespace rq
{
template<class T>
struct 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;
typedef list_iterator<T> Self;
Node* _node;
list_iterator(Node* node)
:_node(node)
{}
T& operator*()
{
return _node->_data;
}
T* operator&()
{
return _node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
bool operator!=(const Self& x)
{
return _node != x._node;
}
bool operator==(const Self& x)
{
return _node == x._node;
}
};
template<class T>
struct list_const_iterator
{
typedef list_node<T> Node;
typedef list_const_iterator<T> Self;
Node* _node;
list_const_iterator(Node* node)
:_node(node)
{}
const T& operator*()
{
return _node->_data;
}
const T* operator&()
{
return _node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
bool operator!=(const Self& x)
{
return _node != x._node;
}
bool operator==(const Self& x)
{
return _node == x._node;
}
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T> iterator;
typedef list_const_iterator<T> const_iterator;
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);//erase以后会返回下一个位置
}
}
void emtpy_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
emtpy_init();
}
//拷贝构造
list(const list<T>& lt)
{
emtpy_init();
for (auto& e : lt)
{
push_back(e);
}
}
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
iterator begin()
{
/*iterator it(_head->_next);
return it;*/
/* return iterator(_head->_next); 匿名对象*/
return _head->_next;//接收指针,但是返回会隐式转换成iterator类型
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
/*iterator it(_head->_next);
return it;*/
/* return iterator(_head->_next); 匿名对象*/
return _head->_next;//接收指针,但是返回会隐式转换成iterator类型
}
const_iterator end() const
{
return _head;
}
size_t size() const
{
return _size;
}
void swap(list<int>& it)
{
std::swap(_head, it._head);
std::swap(_size, it._size);
}
bool empty() const
{
return _size == 0;
}
void push_back(const T& x)
{
/*Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
++_size;*/
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
iterator erase(iterator pos)
{
assert(pos != end());//不能把头节点删了
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
--_size;
return next;
}
iterator insert(iterator pos, const T& x)
{
//prev newnode cur
Node* newnode = new Node(x);
Node* prev = pos._node->_prev;
Node* cur = pos._node;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return newnode;
}
private:
Node* _head;
size_t _size;
};
template<class Container>
void print_container(const Container& v)
{
list<int>::const_iterator it = v.begin();
while (it != v.end())
{
//*it += 10;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_list01()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
print_container(lt);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<string>
#include <algorithm>
#include <list>
using namespace std;
void test_list1()
{
string s("sgfaiug");
cout << s << endl;
sort(s.begin(), s.end());
cout << s << endl;
}
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);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
auto it = lt.begin();
int k = 3;
while (k--)
{
++it;
}
lt.insert(it, 30);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
int x = 0;
cin >> x;
it = find(lt.begin(), lt.end(), x);
if (it != lt.end())//迭代器从前往后找,左闭右开,等于end就是没找到
{
lt.erase(it);
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
#include "list.h"
int main()
{
//test_list2();
rq::test_list01();
return 0;
}
list和vector的不同
- 插入和删除:
vector:是一个动态数组,它在内存中连续存储元素。这意味着vector可以支持快速的随机访问(通过下标),但插入和删除操作(尤其是在容器中间或开始位置)可能需要移动大量元素,因此可能相对较慢。
list:是一个双向链表,每个元素都包含数据和指向列表中前一个和后一个元素的指针(或链接)。因此,list在插入和删除元素时(尤其是在容器中间或开始位置)非常高效,因为它只需要更改指针,而不需要移动其他元素。但是,list不支持快速的随机访问。- 内存使用:vector使用的是一个连续的空间,空间利用率高,而list使用的是非连续的空间,空间利用率低
- 使用场景:
如果你需要频繁地随机访问元素,且插入和删除操作较少,那么 vector 及其迭代器可能更适合。
如果你需要频繁地在容器的中间进行插入和删除操作,那么 list 及其迭代器可能更合适。- 性能:vector 的迭代器由于其随机访问特性,在需要快速访问容器中任意元素时非常高效。然而,在涉及大量插入或删除操作(尤其是在容器中间)时,可能会因为元素移动或内存重新分配而导致性能下降。
list 的迭代器在遍历容器时通常较慢,因为它们不支持随机访问。但是,list 的插入和删除操作(特别是在容器的中间)通常比 vector 更快,因为它们不需要移动大量元素。