C++ STL コンテナの簡単な説明

STLの簡単な説明

ネット上には参考になる情報がたくさんありますので、

規格を直接見るのが最も正確かつ明確です。

ベクター スタック キュー / priority_queue デキュー

  • 配列
  • マップ/マルチマップ
  • セット/マルチセット
  • 順序なしマップ
  • 順序なしセット
  • ポインタとイテレータについて
  • !!! PBD
  • ……

この記事は、読者が基本的な STL アプリケーションを理解していることを前提としています。

STL のすべては 0" role="presentation">0 から始まります

左を閉じて右から開き始めます。

シーケンスコンテナ

Vector は最も一般的に使用される動的配列です。

  • ベクトル<int> v(n, 0)
  • v.reserve(n) は、push_back を高速化するためにスペースを事前に割り当てます
  • v.push_back(x)
  • v.clear() はポインタを移動するだけで、占有メモリは変更しません。
  • v.shrink_to_fit() は、メモリをちょうど v.size() 個の部分に縮小します。

一般的に使用される複雑さは次のとおりです。

  • ランダムアクセス:O(1)
  • 末尾要素の削除または追加:償却O(1)
  • 要素の挿入または削除: ベクトルの終端までの距離を持つ線形 O(n)

よく使用されるメンバー関数もいくつかあります。

  • v.at(i) は、指定された位置にある要素にアクセスし、範囲外チェックを実行します(何もしないよりは良い)
  • v.front()、v.back() は開始/終了要素にアクセスします。空のコンテナーでの呼び出しは未定義の動作です
  • v.empty() / v.size() / v.resize()
  • の上)

要素を削除/追加するには、その要素が存在することを確認する必要があります。

v.erase(lower_bound(begin(v), end(v), x));
v.insert(lower_bound(begin(v), end(v), x), x);

離散化操作? O(nlogn+n)

sort(begin(v), end(v));
v.erase(unique(begin(v), end(v)), end(v));

begin(v) は v.begin() と同等であり、end(v) も同じです。

deque は謎の両端キューです。

std::vector とは対照的に、両端キューの要素は連続して格納されません。一般的な実装では、個別に割り当てられた一連の固定サイズ配列と追加の登録が使用されます。つまり、添え字アクセスではダブル ポインター逆参照が必要になります。ベクトルへの添え字アクセスは 1 回だけ行われます。

cppreference からの抜粋

つまり、vector との唯一の違いは、O(1) にできる Push_front() / Pop_front() です。

それ以外は Vector とまったく同じです (定数が大きいだけです)。

したがって - そこに

stack と queue および priority_queue はコンテナ アダプタと呼ばれます。

このクラス テンプレートは、基礎となるコンテナーのラッパーのように動作し、特定の関数セットのみを提供します。

デフォルトでは、スタックとキューの両方が最下層として deque を使用するため、定数は大きくなります。

一般に、スタックはベクターに置き換える必要があり、キューは手動で作成できます (キューに追加される要素の数がわからない場合を除く)。

priority_queue の最下層はデフォルトで Vector であり、その定数はまだ非常に大きいです...しかし、代替手段はまだあります (通常は必要ありません)

アルゴリズム ライブラリには make_heap/push_heap/pop_heap/sort_heap 操作がありますが、それらは必要ではないと主張します。

また、存在感が非常に低い3 つの連続したコンテナもあります。

  • array<T, N> は STL の静的配列で、通常はベクトルの場合に使用されます。
  • list / forward_list は隣接リストで使用できますが、vector を使用するほど高速ではありません。

これらの連続コンテナでは、いくつかの不可解な STL 操作が行われる可能性があります。

#define all(x) begin(x), end(x)

auto it = find(all(v), x) O(n) で配列内の最初の x を検索します。

  • 位置、リターンエンドなし(v)。

  • auto it = lower/upper_bound(all(v), x) O(logn)

ソートされた配列で x を検索する

  • の位置。

  • int cnt = count(all(v), x) O(n)

戻り値の x の場合

  • 番号。

  • reverse(all(v)) O(n)
  • フリップ。

  • merge(all(v1), all(v2), back_inserter(res)) O(n+m)
  • 2 つのソートされた配列をマージします。

  • T res = 最大/最小(all(v)) O(n)
  • 最大/最小要素を返します。

  • ペア<T, T> res = minmax(all(v)) O(n)
  • ペアを返します。

  • iota(all(v), x) ループ代入 v[i]=x+i

  • fill(all(v), x) / fill_n(begin(v), siz, x) の割り当て。

関連するコンテナ

2 つのカテゴリに分類されます。

  • 順序付けされたマップとセットとマルチ...:
  • 赤黒い木
  • どちらも O(logn) です
  • 単一操作
  • 順序付けされていない unordered_map と unordered_set および unowned_multi...:
  • ハッシュ表
  • 平均 O(1)

最悪の場合O(n)

注文されたコンテナの操作

  • s. lower_bound(x) は O(logn) です
  • s.find(x) 見つからない場合は、end(s) を返します。
  • マルチセットに対してカウント操作を実行すると、O(logn+s) になります。s は要素の数です。

障害時の手術

  • s.reserve(n) は、複数の挿入の時間を短縮するために n 要素用のスペースを予約します。

イテレーター

イテレータは 4 つありますが、通常は 1 つだけが使用されます: forward iterator

begin(x)、end(x) は、コンテナの先頭と末尾を指すイテレータを返します。

順次コンテナの場合、操作によって返されるイテレータはランダム アクセス イテレータですが、連想コンテナ (およびリスト) は双方向イテレータを返します。

ランダム アクセス反復子の場合は +/- x を実行できますが、双方向反復子は ++it / --it しか実行できません。

空のイテレータ (end(v) など) の動作は未定義であり、RE が実行されるか、何も起こらない可能性があります。

vector<int> v(10, -1);
iota(begin(v), begin(v) + 5, 0);
vector<int>::iterator it = find(begin(v), end(v), 4);

// int* 也可以看作是随机访问迭代器
int a[100];
fill(a, a + 50, 7);
// for (int i = 0; i < 50; ++i) a[i] = 7;
int *it = lower_bound(a, a + 50, 8); // it == a + 50

無効なイテレータ

コドリスの木について書いている人は皆、多かれ少なかれ次のことを知っています。

auto itr = split(r + 1), itl = split(l); // 顺序不能颠倒,否则可能RE

これは、split(r + 1) 操作が l が配置されているノードに影響を及ぼし、反復子が失敗する可能性があるためです。

コンテナを変更しないメソッドは、イテレータや参照を無効にしてはなりません。コンテナの内容を変更するメソッドは、イテレータや参照を無効にする可能性があります。

ベクトルの場合、後続の操作は前の反復子には影響せず、容量が変化すると無効になります。

vector<int> v(10);

auto it = begin(v) + 5;
v.insert(begin(v) + 7); // it 不失效
v.insert(begin(v)); // it 失效
v.resize(5) // it 失效

deque の場合は、コンテンツイテレータが変更されている限り無効になると考えられます。

マップ、セット、マルチ... については、消去またはコンテナがクリアされない限り、常に有効であるとみなされます。

挿入によって再ハッシュが発生しない限り、unowned_... についても同じことが当てはまります。

STL文字列

一行読んでみてください:

string s;
cin >> std::ws;
getline(cin, s);

std::string について...

Vector<char> と同様に、クリア/挿入/ポップ/プッシュバック/消去/サイズ変更/予約/... を実行できます。

s.substr(pos, len) を追加するだけで、部分文字列 [pos,pos+len) または [pos,size())] が返されます。

pos>size() の場合、エラー (RE) が報告され、複雑度は len に比例します。

元の文字列を変更する必要はないが、部分文字列を取得する必要がある場合は、string_view をお勧めします。

string s = "ni hao guai er zi";
string_view v(s);
string_view sub = v.substr(3, 3); // sub == "hao", O(1);
sub.remove_prefix(1); // sub == "ao", O(1);
sub.remove_suffix(1); // sub == "a", O(1);

ただし、一般化された文字列を定義することは可能であると主張します。

basic_string<int> v;
for (int i = 0; i < 5; ++i) v += i;
// v = {0, 1, 2, 3, 4}

/* 以下是 C++17 及以上的行为 */
basic_string_view<int> vi(v);
vi.remove_prefix(2);
for (int x : vi) cout << x << ' ';

この記事の特典として、無料の C++ 学習情報パッケージ、技術ビデオ/コード、主要メーカーからの 1,000 件のインタビュー質問 (C++ の基礎、ネットワーク プログラミング、データベース、ミドルウェア、バックエンド開発、オーディオおよびビデオ開発など) を受け取ることができます。 、Qt開発)↓↓↓ ↓↓↓下記からご覧ください↓↓記事下部をクリックして無料で入手してください↓↓

おすすめ

転載: blog.csdn.net/m0_60259116/article/details/133206556