Оглавление
1. Введение в список документов
2. Сравнение эффективности между списком + сортировкой и вектором + сортировкой
2. Класс Iterator — основная структура
Сначала реализуйте конструктор итератора:
Повторное использование конструкции копирования:
Один, список использует
1. Введение в список документов
1. Список — это последовательный контейнер, который можно вставлять и удалять в любом месте в пределах постоянного диапазона, а контейнер можно повторять вперед и назад.2. Нижний слой списка представляет собой структуру двусвязного списка.Каждый элемент в двусвязном списке хранится в независимом узле, не связанном друг с другом.В узле указатели указывают на предыдущий элемент и следующий элемент .3. list очень похож на forward_list : основное отличие состоит в том, что forward_list — это односвязный список , который можно повторять только вперед, что делает его проще и эффективнее.4. По сравнению с другими последовательными контейнерами (array , vector , deque) list обычно имеет лучшую эффективность выполнения для вставки и удаления элементов в любой позиции .5. По сравнению с другими последовательными контейнерами самый большой недостаток list и forward_list в том, что он не поддерживает произвольный доступ в любой позиции , например: для доступа к шестому элементу списка он должен быть из известной позиции.( например, head или tail ) для итерации до этой позиции, для итерации в этой позиции требуется список служебных данных линейного времени и некоторое дополнительное пространство для сохранения связанной информации каждого узла (для больших списков с меньшими элементами типа хранения Это может быть важным фактором )
URL-адрес документа STL: list — Справочник по C++ (cplusplus.com)
2. Общие интерфейсы
STL аналогичен шаблону дизайна, и поясняются только специальные части.
1. сортировать в списке
Мы знаем, что в библиотеке алгоритмов есть функция сортировки, так почему же список надо писать отдельной сортировкой? ? Причина: Использование сортировки в библиотеке алгоритмов имеет обязательное условие — адреса данных непрерывны. (Базовая реализация сортировки списка в основном представляет собой нерекурсивную сортировку слиянием .)
2. Сравнение эффективности между списком + сортировкой и вектором + сортировкой
Сразу вывод : интерфейс сортировки списка не имеет большого значения .
Эксперимент эффективности:
Тестовая группа: используйте список, чтобы принять данные, и используйте сортировку списка для сортировки.
Контрольная группа: сначала используйте вектор для получения данных, затем используйте сортировку в библиотеке алгоритмов для сортировки и, наконец, передайте данные в список.
Результаты эффективности времени:
Между ними нет большой разницы, когда объем данных небольшой, но когда объем данных велик, разрыв будет от 5 до 10 раз.
3. Об инвалидации итератора
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);
}
}
4. очистить
Очистите данные связанного списка и сохраните головной узел .
Во-вторых, реализация списка
1. Каркасная конструкция
1
2 #include <iostream>
3 #include <list>
4 uising namespace std;
5 namespace my_list
6 {
7 template<class T>
8 struct list_node
9 {
10 list_node ( const T& val = T() ) // 针对不同的数据类型,所以用数据类型的仿函数
11 :_val(val)
12 ,_prev(nullptr)
13 ,_next(nullptr)
14 {}
15
16 T _val;
17 list_node<T>* _prev ;
18 list_node<T>* _next ;
19 };
template<class T>
22 class list
23 {
24 typedef list_node<T> Node;
25 public:
26 list() //
27 {
28 _head = new Node;
29 _head->_prev = _head;
30 _head->_next = _head;
31 }
private:
45 Node* _head;
46 };
47 }
2. Класс Iterator — основная структура
Раньше строки и векторы были непрерывны в физической памяти, поэтому итераторы почти подобны указателям, а данные могут быть представлены однократным разыменованием. Список не хранится непрерывно в физической памяти. Нижний уровень списка представляет собой двусвязный список с заголовком, и данные, на которые указывает головной узел, обрабатываются и обрабатываются.
struct _list_iterator // 由于List的迭代器,表层是通过头结点进行操作,数据在头里面,一层解引用
25 { // 解决不了问题
26 typedef list_node<T> Node;
27 typedef _list_iterator iterator;
28 Node* _node; // 迭代器类,内部只要一个结点的指针即可
29
30 _list_iterator(Node* x)
31 : _node(x)
32 {}
33
34 // 重载迭代器*,因为结点的解引用,只是得到结点。目的:支持读,写
35 T& operator*()
36 {
37 return _node->_val;
38 }
39 // 重载迭代器++,list不是连续的空间,地址++不合理;目的:++后为下一个迭代器位置,且支持读,写
40 iterator& operator++() // 前置++
41 {
42 _node = _node->_next;
43 return *this;
44 }
45
46
47 bool operator!=(const iterator& v) const
48 {
49 return _node != v._node;
50 }
Вы заметили? Мы не написали уничтожение и копирование конструкции в нашей структуре итератора ? На самом деле внутри спрятана черная курица.
Первый — уничтожение: 1. Указатель _node принадлежит связному списку, мы не можем случайно освободить место . 2. Для пользовательских типов система автоматически вызывает деструктор пользовательского типа.
Второй - копирование: нам нужен только адрес цели и копируем значение. Глубокая копия не нужна, поэтому писать не нужно.
3. оператор-> реализация
Предполагая, что T является пользовательским типом, вам нужно прочитать функции-члены и даже переменные-члены внутри. Тогда общий способ записи: (*it).a1, так как это указатель, будет удобнее писать стрелку
// 实现
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &(operator*()); //这个挺怪异的,马上讲解
}
// 测试
void test2()
{
struct pos
{
int a1 = 1;
int a2 = 2;
};
my_list::list<pos> x;
my_list::list<pos>::iterator it = x.begin();
while (it != x.end())
{
cout << (*it).a1;
cout << it->a1;
}
}
4. const - итератор
Обычные итераторы могут быть разыменованы для получения онтологии данных, в то время как константные итераторы могут быть разыменованы для получения онтологии, и онтология не может быть изменена. Идея реализации следующая:
Код итератора:
#include <iostream>
#include <string>
using namespace std;
namespace my_list
{
template <class T>
struct list_node // 不用修改
{
list_node(const T& data = T())
: _data(data)
, _next(nullptr)
, _prv(nullptr)
{}
T _data;
list_node* _next;
list_node* _prv;
};
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)
{
return _node != it._node;
}
bool operator==(const iterator& it)
{
return _node == it._node;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
_node = _node->_next;
return *tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &(operator*());
}
};
template <class T> // 提供const迭代器类型+ const迭代器的begin(),end()
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T&, T*> iterator;
typedef list_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->_prv = _head;
}
void push_back( const T& val)
{
Node* tmp = new Node(val);
tmp->_data = val;
tmp->_next = _head;
tmp->_prv = _head->_prv;
_head->_prv->_next = tmp;
_head->_prv = tmp;
}
private:
Node* _head;
};
}
5. вставить
Реализуем самый простой 1:
// 在当前位置插入一个数据,当前数据向后移
iterator insert(iterator pos, const T& data)
{
Node* cur = pos._node;
Node* prv = cur->_prv;
Node* newnode = new Node(data);
newnode->_next = cur;
newnode->_prv = prv;
prv->_next = newnode;
cur->_prv = newnode;
return iterator(newnode);
}
Таким образом, push_back также может повторно использовать вставку:
void push_back( const T& val)
{
/*Node* tmp = new Node(val);
tmp->_data = val;
tmp->_next = _head;
tmp->_prv = _head->_prv;
_head->_prv->_next = tmp;
_head->_prv = tmp;*/
insert(iterator(_head), val); // 头插同理
}
void push_front(const T& val)
{
insert(iterator(_head->_next), val);
}
6. стереть
Удаляет позицию итератора pos, затем возвращает следующий итератор.
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prv = cur->_prv;
Node* next = cur->_next;
prv->_next = next;
next->_prv = prv;
delete cur;
return iterator(next);
}
Здесь завершается только основной код итератора, а другие мелкие функции предназначены только для совместного использования кода.
7. ясно — осознать
При завершении очистки можно повторно использовать деструктор списка .
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
8. Копируйте конструкцию
Традиционный способ написания: Скопируйте данные один за другим самостоятельно.
Современный способ написания: используйте конструктор итератора, а затем замените узел _head.
Сначала реализуйте конструктор итератора :
template <class input_iterator>
list( input_iterator begin, input_iterator end)
{
list_initial(); // 对头结点进行初始化
while (begin != end)
{
push_back(*begin);
++begin;
}
}
Повторное использование конструкции копирования:
void swap(list<T>& x) // 顺便实现一个swap
{
std::swap(x._head, _head);
}
list(const list<T>& x)
{
list_initial();
list tmp(x.begin(), x.end());
swap(tmp);
}
// tmp 调用析构时,会将this的_headfree掉
9. оператор=
list<T>& operator=(list<T> tmp) // 拷贝构造
{
swap(tmp);
return *this;
}
10. Полный код
#include <iostream>
#include <string>
using namespace std;
namespace my_list
{
template <class T>
struct list_node
{
list_node(const T& data = T())
: _data(data)
, _next(nullptr)
, _prv(nullptr)
{}
T _data;
list_node* _next;
list_node* _prv;
};
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)
{
return _node != it._node;
}
bool operator==(const iterator& it)
{
return _node == it._node;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
_node = _node->_next;
return *tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &(operator*());
}
};
template <class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
void list_initial()
{
_head = new Node;
_head->_next = _head;
_head->_prv = _head;
}
list()
{
list_initial();
}
template <class input_iterator>
list( input_iterator begin, input_iterator end)
{
list_initial(); // 对头结点进行初始化
while (begin != end)
{
push_back(*begin);
++begin;
}
}
void swap(list<T>& x)
{
std::swap(x._head, _head);
}
list(const list<T>& x)
{
list_initial();
list tmp(x.begin(), x.end());
swap(tmp);
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
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);
}
void push_back( const T& val)
{
/*Node* tmp = new Node(val);
tmp->_data = val;
tmp->_next = _head;
tmp->_prv = _head->_prv;
_head->_prv->_next = tmp;
_head->_prv = tmp;*/
insert(iterator(_head), val); // 头插同理
}
void push_front(const T& val)
{
insert(iterator(_head->_next), val);
}
// 在当前位置插入一个数据,当前数据向后移
iterator insert(iterator pos, const T& data)
{
Node* cur = pos._node;
Node* prv = cur->_prv;
Node* newnode = new Node(data);
newnode->_next = cur;
newnode->_prv = prv;
prv->_next = newnode;
cur->_prv = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prv = cur->_prv;
Node* next = cur->_next;
prv->_next = next;
next->_prv = prv;
delete cur;
return iterator(next);
}
iterator Pop_back()
{
erase(iterator(_head->_prv));
return _head;
}
iterator Pop_front()
{
Node* next = _head->_next->_next;
erase(iterator(_head->_next));
return next;
}
list<T>& operator=(list<T> tmp)
{
swap(tmp);
return *this;
}
private:
Node* _head;
};
}
эпилог
Этот раздел здесь, спасибо, друзья, за просмотр, если у вас есть какие-либо предложения, добро пожаловать в комментарии в области комментариев, если вы приносите пользу своим друзьям, оставьте свои лайки, ваши лайки и проблемы станут блоггерами. Движущей силой творчества мастера .