目次
1. リストの導入と利用
1.1 リストの概要
英語の原本
list - C++ Reference (cplusplus.com) https://legacy.cplusplus.com/reference/list/list/?kw=list中国語の紹介 (翻訳は正確ではありません。ドキュメントと併せてご理解ください)
1. リストは、一定の範囲内の任意の位置に挿入および削除でき、双方向に反復できる連続したコンテナです 2. リストの最下層は二重リンクリスト構造です
。二重リンクリストは独立した独立したノードに格納されます。ポインタはノード内の前の要素と次の要素を指します (詳細については、他のブログを参照してください) 3. リストは forward_list と非常に似ています: 主な
違いそれは、forward_list が単一リンクされたリストであり、一方向にしかできないということです。反復がより単純で効率的です。
4. 他の順次コンテナー (配列、ベクトル、デキュー) と比較して、リストは通常、任意の位置で要素を挿入および削除する際の実行効率が優れています。 5. 他のシーケンシャル
コンテナと比較して、リスト forward_list の最大の欠陥は、任意の位置でのランダム アクセスをサポートしていないことです。例: リストの 6 番目の要素にアクセスするには、既知の位置 (先頭など) から反復する必要があります。このプロセスには線形時間のオーバーヘッドが必要です。
メンバー タイプのリスト1.2 リストの使用
1.2.1 構築、破壊、代入演算子のオーバーロード
コンストラクターの紹介:
(1) 空のコンテナ コンストラクタ (デフォルト コンストラクタ) パラメータなしの構築
要素を持たない空のコンテナを構築します 。
(2) フィルコンストラクタの フィル構造体
n個の要素を含むコンテナを構築します 。各要素はvalのコピーです 。
(3) レンジコンストラクタ レンジ構築
範囲 [first,last) と同じ数の要素を含むコンテナを構築します。各要素は、その範囲内の対応する要素から同じ順序で構築されます。
(4) コピーコンストラクタ コピーコンストラクタ
xの各要素のコピーを 同じ順序で含むコンテナーを構築します。
注意:所有测试均在 VS2022 下进行 // constructing lists #include <iostream> #include <list> using namespace std; int main() { // constructors used in the same order as described above: list<int> lt1; // empty list of ints list<int> lt2(4, 100); // four ints with value 100 list<int> lt3(lt2.begin(), lt2.end()); // iterating through second list<int> lt4(lt3); // a copy of third // list<int> lt4 = lt3; int array[] = { 16,9,7,49,55,248 }; list<int> lt5(array, array + sizeof(array) / sizeof(int)); cout << "The contents of lt5 are: "; list<int>::iterator it = lt5.begin(); while (it != lt5.end()) { cout << *it << ' '; it++; } cout << endl; return 0; } output: The contents of lt5 are: 16 9 7 49 55 248
1.2.2 イテレータ
1. begin と end は前方反復子であり、反復子に対して ++ 操作を実行し、反復子は後方に移動します。
2. rbegin(end) と rend(begin) は逆方向反復子で、反復子に対して ++ 操作を実行し、反復します。イテレータを前方に進めるには、
これら 4 つのイテレータ関数の使い方を学ぶ必要があるだけです。
1.2.3 容量
古い友人の空とサイズについては、あまり紹介する必要はありません。
1.2.4 要素へのアクセス:
int main() { // list front list<int> list1; list1.push_back(13); list1.push_back(14); // now front equals 13, and back 14 list1.front() -= list1.back(); cout << "list1.front() is now " << list1.front() << endl; // // list back list<int> list2; list2.push_back(10); while (list2.back() != 0) { list2.push_back(list2.back() - 1); } cout << "list2 contains:"; list<int>::iterator it = list2.begin(); while (it != list2.end()) { cout << ' ' << *it; ++it; } cout << endl; return 0; } output: list1.front() is now -1 list2 contains: 10 9 8 7 6 5 4 3 2 1 0
1.2.5 修飾子
int main() { list<int> lt1; lt1.push_back(1); lt1.push_back(2); lt1.push_back(3); lt1.push_front(4); lt1.push_front(5); lt1.push_front(6); list<int>::iterator it = lt1.begin(); it++; lt1.insert(it, 10); lt1.pop_back(); lt1.pop_front(); it++; lt1.erase(it); for (auto e : lt1) { cout << e << " "; } cout << endl; return 0; } output: 10 5 1 2
比較的複雑な関数インターフェイスを備えたいくつかの関数に注目してください。
1.2.6 操作
int main() { list<int> lt1; lt1.push_back(1); lt1.push_back(3); lt1.push_back(2); lt1.push_back(5); lt1.push_back(4); list<int> lt2; list<int>::iterator it2 = lt2.begin(); lt2.splice(it2, lt1); // 此时lt1已经为空了 for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.reverse(); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.sort(); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.remove(5); for (auto e : lt2) { cout << e << " "; } cout << endl; lt2.push_back(4); lt2.push_back(4); lt2.push_back(4); lt2.unique(); for (auto e : lt2) { cout << e << " "; } cout << endl; list<int> lt3; lt3.push_back(9); lt3.push_back(8); lt3.push_back(7); lt3.push_back(6); lt2.sort(), lt3.sort(); // 注意先排序再合并 lt3.merge(lt2); // 此时lt2已经为空了 for (auto e : lt3) { cout << e << " "; } cout << endl; return 0; } output: 1 3 2 5 4 4 5 2 3 1 1 2 3 4 5 1 2 3 4 1 2 3 4 1 2 3 4 6 7 8 9
1.2.7 非メンバー関数
2. リストの詳細な分析とシミュレーションの実装
2.1 リストの詳細な分析
2.1.1 イテレータとコンコンイテレータのテンプレート化
template<class T, class Ref, class Ptr> //普通正向迭代器 <T, T&, T*> //const正向迭代器 <T, const T&, const T*> struct __list_iterator { typedef list_node<T> Node; typedef __list_iterator<T, Ref, Ptr> itor; Node* _node; __list_iterator(Node* node) :_node(node) {} itor& operator++() { _node = _node->_next; return *this; } itor& operator--() { _node = _node->_prev; return *this; } itor operator++(int) // 前置++ { itor tmp(*this); _node = _node->_next; return tmp; } itor operator--(int) // 前置-- { itor tmp(*this); _node = _node->_prev; return tmp; } Ref operator*() // 引用 { return _node->_data; } Ptr operator->() // 指针 { return &_node->_data; } bool operator==(const itor& it) { return _node == it._node; } bool operator!=(const itor& it) { return _node != it._node; } };
2.1.2 逆反復子
template<class Iterator> class ReverseListIterator { // 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量 // 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量 // 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的 public: typedef typename Iterator::Ref Ref; typedef typename Iterator::Ptr Ptr; typedef ReverseListIterator<Iterator> Self; public: // // 构造 ReverseListIterator(Iterator it) : _it(it) {} // // 具有指针类似行为 Ref operator*() { Iterator temp(_it); --temp; return *temp; } Ptr operator->() { return &(operator*()); } // // 迭代器支持移动 Self& operator++() { --_it; return *this; } Self operator++(int) { Self temp(*this); --_it; return temp; } Self& operator--() { ++_it; return *this; } Self operator--(int) { Self temp(*this); ++_it; return temp; } // // 迭代器支持比较 bool operator!=(const Self& l)const { return _it != l._it; } bool operator==(const Self& l)const { return _it != l._it; } Iterator _it; };
2.2 リストのシミュレーション実装
namespace mylist { template<class T> struct list_node { T _data; list_node<T>* _prev; list_node<T>* _next; list_node(const T& x = T()) :_data(x) ,_prev(nullptr) ,_next(nullptr) {} }; // 迭代器 与 const迭代器 //template<class T> template<class T, class Ref, class Ptr> struct __list_iterator { typedef list_node<T> Node; typedef __list_iterator<T, Ref, Ptr> itor; Node* _node; __list_iterator(Node* node) :_node(node) {} itor& operator++() { _node = _node->_next; return *this; } itor& operator--() { _node = _node->_prev; return *this; } itor operator++(int) // 前置++ { itor tmp(*this); _node = _node->_next; return tmp; } itor operator--(int) // 前置-- { itor tmp(*this); _node = _node->_prev; return tmp; } Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } bool operator==(const itor& it) { return _node == it._node; } bool operator!=(const itor& it) { return _node != it._node; } }; // 反向迭代器 template<class Iterator, class Ref, class Ptr> struct Reverse_iterator { Iterator _it; typedef Reverse_iterator<Iterator, Ref, Ptr> Self; Reverse_iterator(Iterator it) :_it(it) {} Ref operator*() { Iterator tmp = _it; return *(--tmp); } Ptr operator->() { return &(operator*()); } Self& operator++() { --_it; return *this; } Self& operator--() { ++_it; return *this; } bool operator!=(const Self& s) { return _it != s._it; } }; 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; typedef Reverse_iterator<iterator, T&, T*> reverse_iterator; typedef Reverse_iterator<const_iterator, const T&, const <T*>const_reverse_iterator; iterator begin() { return _head->_next; } iterator end() { return _head; } const_iterator begin() const { return const_iterator(_head->_next); } const_iterator end() const { return const_iterator(_head); } const_reverse_iterator rbegin() const { // list_node<int>* return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; } list() { empty_init(); } // list(list<T>& lt) list(const list<T>& lt) { empty_init(); for (auto e : lt) { push_back(e); } } /*list<int>& operator=(const list<int>& lt) { if (this != <) { clear(); for (auto e : lt) { push_back(e); } } return *this; }*/ void swap(list<T>& lt) { std::swap(_head, lt._head); std::swap(_size, lt._size); } list<int>& operator=(list<int> lt) { swap(lt); return *this; } ~list() { clear(); delete _head; _head = nullptr; _size = 0; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } void push_back(const T& x) { insert(end(), x); } void push_front(const T& x) { insert(begin(), x); } void pop_back() { erase(--end()); } void pop_front() { erase(begin()); } T& front() { return _head->_next->_data; } const T& front()const { return _head->_next->_data; } T& back() { return _head->_prev->_data; } const T& back()const { return _head->_prev->_data; } size_t size() const { return _size; } bool empty() const { return _size == 0; } iterator insert(iterator pos, const T& x) { Node* cur = pos._node; Node* newnode = new Node(x); Node* prev = cur->_prev; prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; ++_size; return iterator(newnode); } iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; delete cur; prev->_next = next; next->_prev = prev; --_size; return iterator(next); } private: Node* _head; size_t _size; }; }
2.3 リストとベクトルの比較
ベクトル リスト
基礎構造 動的シーケンスリスト、連続スペース ヘッドノードを備えた双方向循環リンクリスト ランダムアクセス ランダムアクセスをサポートしており、特定の要素へのアクセス効率はO(1)です。 ランダム アクセスはサポートされておらず、
要素へのアクセス効率は O(N) です。挿入と削除 任意の位置での挿入と削除は非効率であり、要素の移動が必要です。時間計算量は
O(N) です。挿入中に容量の拡張が必要になる場合があります。容量の拡張: 新しい領域を開き
、要素をコピーし、古い領域を解放するため、効率が低下します。 。任意の位置での挿入と削除は非常に効率的で、
要素を移動する必要がなく、時間計算量は
O(1)です。スペース利用 最下層は連続空間であり、メモリの断片化を引き起こしにくく、
空間使用率とキャッシュ使用率が高くなります。基礎となるノードは動的に開かれるため、小さなノードでは
メモリの断片化、スペース使用率、
キャッシュ使用率の低下が起こりやすくなります。イテレーター オリジナルのエコロジー指針 オリジナルのエコロジーポインタ(ノードポインタ)をカプセル化 イテレータが無効です 要素を挿入するときは、すべての反復子を再割り当てする必要があります。
要素を挿入すると再拡張が発生し、元の反復子が無効になる可能性があるためです。
削除するときは、現在の反復子を再割り当てする必要があります。再割り当てしないと無効になります。要素を挿入してもイテレータは無効になりません。
要素を削除しても、現在のイテレータが無効になるだけです
。他のイテレータは影響を受けません。使用するシーン 効率的なストレージが必要、ランダム アクセスをサポート、挿入と削除の効率は気にしない 大量の挿入および削除操作、ランダム
アクセスは気にしない