目次
1. リストの紹介
C++ では、std::list
データのチェーン ストレージ用に標準ライブラリによって提供されるコンテナ クラスです。リンク リスト(リスト) は、物理ストレージ ユニット上の非連続ストレージ構造であり、データ要素の論理的順序は、リンク リスト内のポインタ リンクによって実装されます。
- リンク リストの構成: リンク リストは一連のノードで構成されます。
- ノードの構成: 1. データ要素を格納するデータフィールド2.次のノードのアドレスを格納するポインタ フィールド。
STL のリンク リストは双方向循環リンク リストであり、リンク リストの格納方法は連続したメモリ空間ではないため、リンク リストのイテレータは前方と後方の移動のみをサポートし、双方向イテレータです。
2. std::list と std::vector の違い
-
低レベルの実装:
list
二重リンクリストによって実装され、各要素には前と次の要素へのポインタが含まれます。この実装により、リスト内の任意の場所で要素の挿入と削除が効率的に行われますが、要素へのランダム アクセスのパフォーマンスは低下します。vector
これは動的配列によって実装され、要素はメモリに継続的に格納されます。この実装により、要素へのランダムアクセスのパフォーマンスは非常に良くなりますが、途中または先頭の要素の挿入/削除は要素の移動を伴うため、パフォーマンスは比較的低くなります。
-
動的なサイズ変更:
list
のサイズは動的に拡大および縮小できます。これは、要素の格納にリンク リストが使用され、挿入および削除操作のパフォーマンスがリストのサイズに依存しないためです。vector
サイズは動的に増加することもありますが、メモリの再割り当てが必要な場合、多数の要素のコピーとメモリの再割り当て操作が発生する可能性があります。
-
アクセス効率:
list
ランダム アクセスはサポートされておらず、イテレータを介したシーケンシャル アクセスのみがサポートされています。頻繁に挿入および削除操作を必要とするシナリオでは、list
パフォーマンスが向上します。vector
ランダム アクセスをサポートし、添字を介して要素に直接アクセスできます。頻繁なランダム アクセスが必要な操作の場合、vector
パフォーマンスは向上します。
-
メモリ使用量:
list
要素を格納する場合、要素自体の値に加えて、前後の要素へのポインタを格納するための追加のスペースが必要です。したがって、通常はvector
よりも多くのメモリを使用します。vector
要素を格納する場合、要素自体の値と追加の制御情報のみを格納する必要があるため、通常はlist
よりもメモリ使用量が少なくなります。
-
挿入および削除操作:
list
隣接する要素のポインタを変更するだけで他の要素を移動する必要がないため、任意の位置の要素の挿入や削除に効率的です。list
重要なプロパティがあります。挿入操作も削除操作も、元のリスト反復子を無効にすることはありません。これはvector
では当てはまりません。vector
配列の最後で操作するだけでよく、他の要素を移動する必要がないため、最後に要素を挿入および削除する場合に効率的です。ただし、途中または先頭で要素を挿入または削除する場合は、他の要素を移動する必要があります。
要約すると、list
またはを使用するかどうかはvector
、特定のアプリケーション シナリオとニーズによって異なります。頻繁な挿入および削除操作が必要であり、要素への頻繁なランダム アクセスが必要ない場合は、これを選択できますlist
。要素へのランダム アクセスを頻繁に行い、挿入および削除操作を少なくする必要がある場合は、これを選択できますvector
。また、コンテナの途中で挿入や削除の操作を行う必要があり、アクセス効率が高くない場合にも利用を検討できますlist
。
3. リストコンストラクター
コンストラクターのプロトタイプ | 説明する | |
---|---|---|
1 | list<T> lt | デフォルトのコンストラクター。テンプレート クラスを使用して実装されます。 |
2 | list(lt.begin(), lt.end()) | lt[begin, end) 区間内の要素をそれ自体にコピーします。 |
3 | list(n, 要素) | コンストラクターは n 個の要素をそれ自体にコピーします |
4 | リスト(constリスト<) | コピーコンストラクター |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void printVec(list<int> &v)
{
for (list<int>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void printVec(list<double> &v)
{
for (list<double>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void test01()
{
list<int> v1;
v1.push_back(10); //添加元素
v1.push_back(20);
printVec(v1);
list<int> v2(v1.begin(), v1.end());
printVec(v2);
list<double> v3(5, 6.32);
printVec(v3);
list<double> v4(v3);
printVec(v4);
}
int main()
{
test01();
system("pause");
return 0;
}
//result
10 20
10 20
6.32 6.32 6.32 6.32 6.32
6.32 6.32 6.32 6.32 6.32
知らせ:
list を関数のパラメータまたは戻り値として使用する場合、「&」を省略することはできません。
4. リストの割り当て
関数プロトタイプ: =、assign | 説明する | |
---|---|---|
1 | list& 演算子=(const リスト <) | オーバーロードされた = 演算子 |
2 | assign(開始、終了) | [begin, end) 間隔のデータをコピーしてそれ自体に割り当てます |
3 | assign(n, 要素) | n 個の要素のコピーをそれ自体に割り当てます |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void printVec(list<int> &v)
{
for (list<int>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void test01()
{
list<int> v1;
v1.push_back(10); //添加元素
v1.push_back(20);
printVec(v1);
list<int> v2 = v1;
printVec(v2);
list<int> v3;
v3.assign(v1.begin(), v1.end());
printVec(v3);
list<int> v4;
v4.assign(6, 1);
printVec(v4);
}
int main()
{
test01();
system("pause");
return 0;
}
//result
10 20
10 20
10 20
1 1 1 1 1 1
5. リストの長さの操作
関数プロトタイプ: 空、サイズ、サイズ変更 | 説明する | |
---|---|---|
1 | 空の() | コンテナが空かどうかを判断する |
2 | サイズ() | コンテナ内の要素の数を返します。 |
3 | サイズ変更(int num) | コンテナの長さを num に再指定します。コンテナーが長くなると、新しい位置はデフォルト値で埋められ、コンテナーが短くなると、コンテナーの長さを超える最後の要素が削除されます。 |
4 | サイズ変更(int num, 要素) | コンテナの長さを num に再指定します。コンテナーが長くなると、新しい位置が要素値で埋められ、コンテナーが短くなると、コンテナーの長さを超える最後の要素が削除されます。 |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void printVec(list<int> &v)
{
for (list<int>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void test01()
{
list<int> v1;
if (v1.empty()) //判断是否为空
{
cout << "当前v1为空!" << endl;
}
v1.push_back(10); //添加元素
v1.push_back(20);
v1.push_back(30);
if (!v1.empty())
{
cout << "v1中元素个数:" << v1.size() << endl;
printVec(v1);
}
v1.resize(5);
printVec(v1);
v1.resize(10, 1);
printVec(v1);
}
int main()
{
test01();
system("pause");
return 0;
}
//result
当前v1为空!
v1中元素个数:3
10 20 30
10 20 30 0 0
10 20 30 0 0 1 1 1 1 1
6. リストの挿入と削除
関数プロトタイプ:push_back、pop_back、insert、erase、clear | 説明する | |
---|---|---|
1 | Push_back(要素) | 最後に要素を挿入 |
2 | ポップバック() | 最後の要素を削除する |
3 | Push_front(要素) | コンテナの先頭に要素を挿入します |
4 | ポップフロント() | コンテナの先頭から最初の要素を削除します |
5 | insert(反復子 p, 要素) | イテレータは、要素を挿入する位置 p を指します。 |
6 | insert(反復子 p, int n, 要素) | イテレータは位置 p を指し、n 個の要素を挿入します。 |
7 | insert(p,反復子の開始,反復子の終了) | 区間 [開始、終了) の位置 p にデータを挿入します。戻り値はありません。 |
8 | 消去(反復子 p) | イテレータが指す要素を削除します |
9 | Erase(反復子の開始、反復子の終了) | イテレータの最初から最後までの要素を削除します。 |
10 | 削除(要素) | 要素の値に一致するコンテナ内のすべての要素を削除します |
11 | クリア() | コンテナ内のすべての要素を削除します |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void printVec(list<int> &v)
{
for (list<int>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void test01()
{
list<int> v1;
v1.push_back(1); //尾部添加元素
v1.push_back(2);
v1.push_back(3);
v1.push_back(3);
cout << "尾部添加元素: " << endl;
printVec(v1);
v1.pop_back(); //尾部删除元素
cout << "尾部删除元素: " << endl;
printVec(v1);
v1.push_front(100); //头部添加元素
v1.push_front(200);
v1.push_front(300);
cout << "头部添加元素: " << endl;
printVec(v1);
v1.pop_front(); //头部删除元素
v1.pop_front();
cout << "头部删除元素: " << endl;
printVec(v1);
v1.insert(v1.begin(), 100); //插入元素100
cout << "插入元素100: " << endl;
printVec(v1);
v1.insert(v1.begin(), 5, 100); //插入5个元素100
cout << "插入5个元素100: " << endl;
printVec(v1);
v1.erase(v1.begin()); //删除元素
cout << "删除元素v1.begin(): " << endl;
printVec(v1);
v1.remove(100);
cout << "删除所有100元素: " << endl;
printVec(v1);
v1.clear(); //清空容器
printVec(v1);
}
int main()
{
test01();
system("pause");
return 0;
}
//result
尾部添加元素:
1 2 3 3
尾部删除元素:
1 2 3
头部添加元素:
300 200 100 1 2 3
头部删除元素:
100 1 2 3
插入元素100:
100 100 1 2 3
插入5个元素100:
100 100 100 100 100 100 100 1 2 3
删除元素v1.begin():
100 100 100 100 100 100 1 2 3
删除所有100元素:
1 2 3
7. リストデータ取得
関数プロトタイプ:front()、back | 説明する | |
---|---|---|
1 | フロント() | コンテナ内の最初のデータ要素を返します。 |
2 | 戻る() | コンテナ内の最後のデータ要素を返します。 |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void test01()
{
list<int> v1 = {
1, 2, 3, 4, 5, 6 };
cout << "v1.front() = " << v1.front() << endl;
cout << "v1.back() = " << v1.back() << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
//result
v1.front() = 1
v1.back() = 6
8. リストの交換、反転、ソート
関数プロトタイプ: swap、reverse、sort | 説明する | |
---|---|---|
1 | スワップ(リストlt) | lt 内の要素を独自の要素と交換します |
2 | 逆行する() | 逆リンクリスト |
3 | 選別() | リンクリストの並べ替え |
例:
#include <iostream>
#include <list> //必须包含该头文件
using namespace std;
void printVec(list<int> &v)
{
for (list<int>::iterator At = v.begin(); At != v.end(); At++)
{
cout << *At << " ";
}
cout << endl;
}
void test01()
{
list<int> v1 = {
9, 5, 7, 8, 6 };
list<int> v2 = {
5, 4, 3, 2, 1 };
v1.swap(v2); //互换v1与v2中的元素
cout << "list v1 : " ;
printVec(v1);
cout << "list v2 : " ;
printVec(v2);
v2.sort(); //链表排序
cout << "v2链表排序 : ";
printVec(v2);
v2.reverse(); //反转链表v2
cout << "v2反转链表 : ";
printVec(v2);
}
int main()
{
test01();
system("pause");
return 0;
}
//result
list v1 : 5 4 3 2 1
list v2 : 9 5 7 8 6
v2链表排序 : 5 6 7 8 9
v2反转链表 : 9 8 7 6 5