C++ STL の各コンテナの簡単な紹介

1.STLとは何ですか?

1. 標準テンプレート ライブラリである STL (Standard Template Library) は、一般的に使用される多くの基本データ構造と基本アルゴリズムを含む効率的な C++ プログラム ライブラリです。これは、ソフトウェアの再利用性を高度に体現した、大多数の C++ プログラマにスケーラブルなアプリケーション フレームワークを提供します。

2. 論理レベルから見ると、汎用プログラミングの考え方がSTL に具体化されています。この考え方では、基本的なアルゴリズムのほとんどが、対応するデータ構造から独立して抽象化および一般化され、同じまたは類似の方法でさまざまな状況に対処するために使用されます。

3. 実装レベルの観点から見ると、STL 全体はテンプレートに基づいて型パラメータ化された方法で実装されます

STL には 6 つの主要なコンポーネントがありますが、主にコンテナ、イテレータ、アルゴリズムの 3 つの部分で構成されます。

  • コンテナ: 特定のタイプのオブジェクトのコレクションを管理するために使用されます。各コンテナには長所と短所があるため、プログラムのさまざまなニーズを満たすために、STL では 7 つの基本的なコンテナ タイプを用意しています。
  • イテレータ(Iterators): オブジェクト コレクションの要素に対してトラバーサル アクションを実行するために使用されます。このオブジェクトのコレクションはコンテナである場合もあれば、コンテナの一部である場合もあります。各種類のコンテナには独自のイテレータが用意されており、これらのイテレータはその種類のコンテナの内部構造を認識しています。
  • アルゴリズム(アルゴリズム): 要素の並べ替え、検索、コピー、消去など、オブジェクト コレクション内の要素を処理するために使用されます。すべてのコンテナのイテレータが一貫したインターフェイスを提供するため、イテレータを使用すると、アルゴリズムを一度作成して任意のコンテナに適用するだけで済みます。

STL の基本概念は、データと操作を分離することです。データはコンテナーによって管理され、操作はアルゴリズムによって実行され、イテレーターは 2 つの間の接着剤として機能するため、どのアルゴリズムでもどのコンテナーとも対話できます。

2. コンテナ

コンテナは、特定の種類のオブジェクトを管理するために使用されます。プログラムのさまざまなニーズを満たすために、STL は 7 つの基本コンテナ タイプのうち 2 つのタイプを用意しました。

  • シーケンス コンテナー。順序付け可能なクラスターであり、各要素の位置は、要素の値とは無関係に、いつ、どこに挿入されるかに応じて固定されます。6 つの要素をクラスターに追加すると、それらは挿入されたときと同じ順序になります。STL では、ベクトル (vector)、両端キュー (deque)、リスト (list) の 3 つのシリアル コンテナーが提供されており、さらに文字列や配列もシリアル コンテナーとして使用できます。
  • 連想コンテナー (連想コンテナー)。これは順序付けされたクラスターであり、要素の位置は特定の並べ替え基準と要素の値に依存し、挿入順序とは関係ありません。このようなクラスターに 6 つの要素を入れる場合、その位置は挿入順序ではなく要素の値によって決まります。STL は、コレクション (セット)、複数のコレクション (マルチセット)、マッピング (マップ)、および複数のマッピング (マルチマップ) の 4 つの連想コンテナーを提供します。

概略図を次の図に示します。

2.1 ベクトル

ベクター (vector): シリアル コンテナーであり、実際には配列に似ていますが、配列よりも優れています。一般に、配列は動的に拡張できないため、プログラムの実行中にメモリが無駄に消費されるか、境界外が発生します。ベクタはこの欠陥を補っているだけであり、メモリ容量が足りない場合には、再度十分なメモリを申請してメモリをコピーする必要があります。

特徴

  • 連続したメモリ空間を持っているため、ランダム アクセス ([] 演算子と .at()) を非常によくサポートでき、ランダム アクセスは高速です。(アドバンテージ)
  • 要素の先頭または途中に挿入または削除する場合、元の相対的な順序を維持するために、挿入または削除ポイント以降のすべての要素を移動する必要があるため、挿入または削除の効率は比較的低くなります。(欠点)
  • 後で要素を挿入および削除するのが最も速く、一般にこの時点ではメモリを移動する必要はありません。(アドバンテージ)
  • 概要: 拡張可能な配列(動的配列)と同等で、ランダムアクセスは高速で、先頭と途中の挿入や削除は非効率ですが、末尾の挿入や削除は効率的です。

該当シーン

単純なオブジェクト、小さな変更、ランダム アクセスが頻繁に発生するシーンに適しています。

次の例では、整数のベクトルを定義し、6 つの要素を挿入し、すべての要素を出力します。

#include <iostream>
#include <vector>

  C++资料领取进裙:580475042
  
using namespace std;

int main(int argc, char* argv[])
{
	vector<int> vecTemp;

	for (int i = 0; i<6; i++)
		vecTemp.push_back(i);

	for (int i = 0; i<vecTemp.size(); i++)
		cout << vecTemp[i] <<" "; // 输出:0 1 2 3 4 5

	return 0;
}

2.2と

Deque (両端キュー) は、双方向の開口部 (ポインタ配列を介して複数の連続空間を動的に結合する) を持つ連続メモリ空間であり、いつでも新しい空間を追加できます。deque の最大のタスクは、これらのセグメント化された連続空間で全体の連続性の錯覚を維持し、ランダム アクセス インターフェイスを提供することです。

特徴

  • Deque の先頭と末尾に新しいスペースを追加すると、一定量の連続したスペースが構成され、Deque 全体の先頭または末尾につながれるため、先頭または末尾への要素の挿入は非常に高速になります。(アドバンテージ)
  • 要素を中央に配置すると、他の要素を移動する必要があるため、時間がかかります。(欠点)
  • deque はリストとベクトルの間の妥協点です。リストの利点とベクトルのランダムアクセス効率の高さの両方の利点を持ちます。
  • まとめ:ランダムアクセスに対応しているが、ベクターほど効率は良くなく、先頭と末尾の挿入や削除は効率的だが、途中の挿入や削除は非効率である。

該当シーン

頻繁なランダム アクセスが必要で、両端でのデータの挿入と削除を考慮するシナリオに適しています。

次の例では、浮動小数点型の両端キューを宣言し、コンテナーの最後に 6 つの要素を挿入し、最後にすべての要素を出力します。

#include <iostream>
#include <deque>

using namespace std;

int main(int argc, char* argv[])
{
	deque<float> dequeTemp;

	for (int i = 0; i<6; i++)
		dequeTemp.push_back(i);

	for (int i = 0; i<dequeTemp.size(); i++)
		cout << dequeTemp[i] << " "; // 输出:0 1 2 3 4 5

	return 0;
}

2.3 リスト

リストは二重リンク リストによって実装され、要素はヒープに格納され、各要素はメモリの一部に配置されます。スペースを予約する習慣がないため、要素の各割り当てはメモリから割り当てられ、要素を削除するたびに、要素が占有しているメモリが解放されます。

特徴

  • メモリ空間は不連続である可能性があり、データはポインタを介してアクセスされます。この機能によりランダム アクセスが非常に非効率になるため、[] 演算子のオーバーロードは提供されません。(欠点)
  • 連結リストの特性上、任意の位置での挿入・削除の効率が高いです。(アドバンテージ)
  • 最初と最後の 2 つの要素への直接アクセスのみがサポートされており、他の要素を (同じアクセス時間で) 取得したい場合は、リンク リストをたどる必要があります。(欠点)
  • 概要: ランダム アクセスはサポートされていないため、任意の位置での挿入と削除の方が効率的です。

該当シーン

挿入および削除操作が頻繁に実行され、ランダム アクセスがまれであるシナリオに適しています。

次の例では、文字を配置できる空のリストを生成し、「a」から「z」までのすべての文字をそのリストに挿入し、ループを使用して毎回セットの最初の要素を出力および削除します。これにより、すべての文字が出力されます。要素:

#include <iostream>
#include <list>

using namespace std;

  C++资料领取进裙:580475042

int main(int argc, char* argv[])
{
	list<char> listTemp;

	for (char c = 'a'; c <= 'z'; ++c)
		listTemp.push_back(c);

	while (!listTemp.empty())
	{
		cout <<listTemp.front() << " ";
		listTemp.pop_front();
	}

	return 0;
}

メンバー関数 empty() の戻り値は、コンテナー内に要素が残っているかどうかを示し、この関数が false を返す限り、ループは継続します。ループ内では、メンバー関数front() が最初の要素を返し、pop_front() 関数が最初の要素を削除します。

注: list<pointer> は最もパフォーマンスが低いメソッドです。ポインタには構築と破壊がなく、多くのメモリを占有しないため、list<object> または Vector<pointer> を直接使用することをお勧めします。

2.4セット

セット (コレクション) は、その名前が示すように、数学的なコレクションです。各要素は最大 1 回出現し、セット内の要素は小さいものから大きいものへと並べ替えられています。

特徴

  • 赤黒ツリーを使用して実装されており、その内部要素はその値に従って自動的にソートされます。各要素の値は 1 回だけ出現でき、重複は許可されません。
  • 値を挿入するたびに、赤黒ツリーを調整する必要があり、効率に一定の影響を与えます。(欠点)
  • 連想コンテナの場合、メモリコピーやメモリ移動を行う必要がないため、マップやセットの挿入や削除の効率が他のシーケンスコンテナよりも高くなります。(アドバンテージ)
  • 概要: 赤黒ツリーによって実装され、その内部要素はその値に従って自動的にソートされ、各要素の値は 1 回のみ出現でき、繰り返しは許可されず、挿入と削除の効率が他のシーケンスよりも高くなります。コンテナ。

該当シーン

これは、要素が特定のコレクションに含まれており、並べ替える必要があるかどうかを頻繁にチェックするシナリオに適しています。

次の例は、set (コレクション) の 2 つの特性を示しています。

#include <iostream>
#include <set>

using namespace std;

int main(int argc, char* argv[])
{
	set<int> setTemp;

	setTemp.insert(3);
	setTemp.insert(1);
	setTemp.insert(2);
	setTemp.insert(1);

	set<int>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		cout << *it << " ";
	}

	return 0;
}

出力結果: 1 2 3。合計 4 つの数字が挿入されていますが、セット内の数字は 3 つだけで順序付けされており、上記のセット set の 2 つの特徴が順序付けされており、繰り返しがないことがわかります。

セット コレクション内の要素が構造体の場合、その構造体は演算子 '<' のオーバーロードを実装する必要があります。

#include <iostream>
#include <set>
#include <string>

using namespace std;

struct People
{
	string name;
	int age;

	bool operator <(const People p) const
	{
		return age < p.age;
	}
};

int main(int argc, char* argv[])
{
	set<People> setTemp;

	setTemp.insert({"张三",14});
	setTemp.insert({ "李四", 16 });
	setTemp.insert({ "隔壁老王", 10 });

	set<People>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		printf("姓名:%s 年龄:%d\n", (*it).name.c_str(), (*it).age);
	}

	return 0;
}

  C++资料领取进裙:580475042
/*
输出结果
姓名:王二麻子 年龄:10
姓名:张三 年龄:14
姓名:李四 年龄:16 
*/ 

結果が年齢の降順に並べられていることがわかります。さらに、文字列は c_str() を使用して変換する必要があります。そうしないと文字化けが出力されます。

さらに、Multiset は set と同じですが、要素の繰り返しが許可される点が異なります。つまり、multiset には同じ値を持つ複数の要素を含めることができます。ここではあまり紹介しません。

2.5マップ

マップは赤黒ツリーで実装されており、その要素は「キー値/実数値」で構成されるペア(キー/値ペア)です。

マップは主にデータの 1 対 1 マッピングに使用されます。マップ内に赤黒ツリーが構築されます。このツリーにはデータを自動的にソートする機能があり、マップ内のすべてのデータが順番に並べられます。たとえば、クラスでは、各生徒の生徒番号と名前の間に 1 対 1 のマッピング関係があります。

特徴

  • 各要素にはキーがあり、出現できるのは 1 回だけであり、重複は許可されません。
  • キー値に基づいてレコードを高速に検索します。検索の複雑さは基本的に O(logN) です。レコードが 1000 件ある場合、二分検索は最大 10 回 (1024) 回検索できます。(アドバンテージ)
  • 値を挿入するたびに、赤黒ツリーを調整する必要があり、効率に一定の影響を与えます。(欠点)
  • ノードの追加と削除は、演算ノードを除き、イテレータにはほとんど影響を与えません。他のノードには影響しません。(アドバンテージ)
  • イテレータの場合、実数値は変更できますが、キーは変更できません。
  • 概要: 要素はキーと値のペアであり、キーと値は必要な任意の型にすることができ、各要素にはキーがあり、出現できるのは 1 回だけであり、繰り返しは許可されず、キーに従ってレコードをすばやく検索できます。

該当シーン

データ ディクショナリを保存する必要があり、キーに基づいて値を簡単に見つけることができるシナリオに適しています。

#include "stdafx.h"
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main(int argc, char* argv[])
{
	map<int, string> mapTemp;

	mapTemp.insert({ 5,"张三" });
	mapTemp.insert({ 3, "李四"});
	mapTemp.insert({ 4, "隔壁老王" });

	map<int, string>::iterator it;
	for (it = mapTemp.begin(); it != mapTemp.end(); it++)
	{
		printf("学号:%d 姓名:%s\n", (*it).first, (*it).second.c_str());
	}

	return 0;
}

/*
输出结果:
学号:3 姓名:李四
学号:4 姓名:隔壁老王
学号:5 姓名:张三
*/

マルチマップはマップと同じですが、要素の繰り返しを許可します。つまり、マルチマップには同じキー値 (キー) を持つ複数の要素を含めることができます。ここではあまり紹介しません。

2.6 コンテナアダプター

上記の 7 つの基本コンテナ カテゴリに加えて、特別なニーズを満たすために、STL は基本コンテナ カテゴリに基づいて実装されるいくつかの特別な (事前定義された) コンテナ アダプタも提供します。含む:

1、スタック

名前がすべてを物語っているように、スタック コンテナーは要素に対して LIFO (後入れ先出し) 管理戦略を採用しています。

2、行列

キューコンテナは要素に対して FIFO (先入れ先出し) 管理戦略を採用しています。つまり、普通のバッファ(バッファ)です。

3、優先キュー

priority_queue コンテナ内の要素には、異なる優先順位を設定できます。いわゆる優先度は、プログラマが提供するソート基準に基づいて定義されます (デフォルトでは演算子が使用されます)。Priority queue の効果は、「次の要素は常にキュー内で最も高い優先度を持つ要素である」というバッファと同等です。複数の要素が同時に最高の優先順位を持つ場合、それらの順序は定義されません。

3. まとめ

各コンテナの特徴まとめ

  • Vector はランダム アクセスをサポートしており、先頭と中間での挿入または削除は効率的ではありませんが、末尾での挿入または削除は効率的です。
  • ランダムアクセスにも対応していますが、ベクターほど効率は高くなく、先頭と末尾の挿入や削除は効率的ですが、途中の挿入や削除は非効率です。
  • リストはランダムアクセスをサポートしておらず、任意の位置での挿入および削除の効率が高いです。
  • セットは赤黒ツリーで実装され、その内部要素はその値に従って自動的にソートされます。各要素の値は 1 回のみ出現でき、繰り返しは許可されません。挿入および削除の効率は他のシーケンス コンテナよりも高くなります。 。
  • マップの要素はキーと値のペアです。キーと値は必要な任意のタイプにすることができます。各要素にはキーがあり、一度だけ表示できます。重複は許可されません。キーに基づいてレコードをすばやく検索します。

実際の使用プロセスでは、これらのコンテナのどれを選択するかは、次の原則に従う必要があります。

1. 効率的なランダムアクセスが必要で、挿入と削除の効率を気にしない場合は、vector を使用します。
2. ランダム アクセスが必要で、両端でのデータの挿入と削除の効率を重視する場合は、deque を使用します。
3. 多数の要素を挿入および削除する必要があり、ランダム アクセスの効率を気にしない場合は、リストを使用します。
4. 要素が特定のセット内に存在し、並べ替える必要があるかどうかを頻繁に確認する場合は、それがのみ存在する場合は set を使用し、一意に存在しない場合は multiset を使用します。
5. データ ディクショナリを保存する予定で、キーに基づいて値を簡単に検索する必要がある場合は、1 対 1 の状況ではマップを使用し、1 対多の状況ではマルチマップを使用します。

各コンテナの時間計算量分析

  • 先頭と中間のベクトルの挿入と削除の計算量は O(N)、末尾の挿入と削除の計算量は O(1)、ランダム アクセスの計算量は O(1)、検索時間 複雑さは O(N) です。
  • 途中のデキューの挿入と削除の計算量は O(N)、先頭と末尾の挿入と削除の計算量は O(1)、ランダム アクセスの計算量は O(1)、検索時間 複雑さは O(N) です。
  • 任意の位置でのリストの挿入と削除の時間計算量は O(1)、検索の時間計算量は O(N) です。
  • セットとマップは両方とも赤黒ツリーを通じて実装されるため、挿入、削除、検索操作の時間計算量は O(log N) です。

各コンテナの共通性

各コンテナには通常、次の関数があります: デフォルト コンストラクター、コピー コンストラクター、デストラクター、empty()、max_size()、size()、operator=、operator<、operator<=、operator>、operator>= 、operator==、operator !=、swap()。

順次コンテナと連想コンテナは両方とも次の機能を共有します。

  • begin() : コンテナの最初の要素の反復子ポインタを返します。
  • end(): コンテナの最後の要素の 1 ビット後ろにある反復子ポインタを返します。
  • rbegin(): コンテナの最後の要素を指す逆反復子ポインタを返します。
  • rend(): コンテナの最初の要素の 1 ビット前を指す逆反復子ポインタを返します。
  • clear(): コンテナ内のすべての要素を削除します。
  • Erase(it): イテレータポインタ it の要素を削除します。

おすすめ

転載: blog.csdn.net/QtCompany/article/details/131688999