【C++】vector等から重複した要素を削除する std::unique の使い方

目次

はじめに

C++で、vector などのコンテナから、重複している要素を全て取り除き、ユニークな(一意な)要素だけの状態にしたい、という場面は頻繁にあります。

この目的のために、標準ライブラリ <algorithm> には std::unique という関数が用意されています。しかし、unique は少し特殊な動作をし、要素を本当に削除するわけではありませんunique は、重複している要素をコンテナの後方に移動させ、「ここからが重複部分の始まりです」という位置を示すイテレータを返すだけです。

実際にコンテナからこれらの不要な要素を完全に削除するには、unique が返したイテレータをコンテナの .erase() メソッドに渡す必要があります。

この記事では、この sortunique.erase という一連の流れ(Erase-Removeイディオム)を解説します。


【重要】事前条件: unique の前には sort が必要

std::unique は、連続して重複している要素しか検出しません。{1, 5, 1, 5} のような、重複が離れているコンテナに unique をそのまま適用しても、何も起こりません。

そのため、全ての重複要素を正しく処理するには、unique を呼び出す前に、必ずコンテナを std::sort でソートし、同じ値が隣り合うようにしておく必要があります。


重複要素を削除するサンプルコード

このコードは、vector に格納された重複を含む数値を、sort で並べ替え、unique で重複を後ろに集め、.erase で完全に削除します。

完成コード

#include <iostream>
#include <vector>
#include <algorithm> // sort, unique

using namespace std;

// vectorの内容を表示するヘルパー関数
void print_vector(const string& title, const vector<int>& vec) {
    cout << title << ": { ";
    for (int val : vec) {
        cout << val << " ";
    }
    cout << "}" << endl;
}

int main() {
    vector<int> numbers = {10, 30, 20, 10, 30, 30, 40, 20};
    
    print_vector("初期状態", numbers);

    // 1. まず、sort()で要素を並べ替える
    sort(numbers.begin(), numbers.end());
    print_vector("sort()後", numbers);
    // -> { 10 10 20 20 30 30 30 40 }

    // 2. unique()で、連続した重複要素を後ろに移動させる
    auto last = unique(numbers.begin(), numbers.end());
    print_vector("unique()後", numbers);
    // -> { 10 20 30 40 | 20 30 30 40 } のような状態になる
    //                ^ lastイテレータはここを指す

    // 3. .erase()で、unique()が返した位置から末尾までを削除する
    numbers.erase(last, numbers.end());
    print_vector("erase()後", numbers);
    // -> { 10 20 30 40 }

    return 0;
}

実行結果

初期状態: { 10 30 20 10 30 30 40 20 }
sort()後: { 10 10 20 20 30 30 30 40 }
unique()後: { 10 20 30 40 20 30 30 40 }
erase()後: { 10 20 30 40 }

コードの解説

sort(numbers.begin(), numbers.end());

まずコンテナをソートし、{10, 10, 20, 20, 30, 30, 30, 40} のように、同じ値が隣り合うようにします。

auto last = unique(numbers.begin(), numbers.end());

unique は、範囲内の重複した要素を、範囲の末尾に移動させます(値は不定)。そして、重複がなくなった範囲の次の要素を指すイテレータを返します。 unique() 実行後の vector の中身は { 10, 20, 30, 40, ?, ?, ?, ? } のようになり(?の部分は移動してきた元の値)、last| の位置を指すイテレータになります。vectorsize() は、この時点ではまだ変わりません。

numbers.erase(last, numbers.end());

.erase(開始イテレータ, 終了イテレータ) は、指定された範囲の要素をコンテナから物理的に削除します。 last(重複部分の開始位置)から、numbers.end()(コンテナの末尾)までを削除することで、不要な要素がコンテナから完全に取り除かれます。


まとめ

今回は、C++でコンテナから重複要素を削除するための標準的なイディオム(定石)を解説しました。

  1. std::sort() でコンテナをソートする。
  2. std::unique() で連続した重複を末尾に移動させ、重複部分の開始イテレータを取得する。
  3. コンテナの .erase() メソッドで、unique が返したイテレータから末尾までを削除する。

この sort-unique-erase の3ステップは、vector から重複を削除する際の決まった手順ですので、ぜひ覚えておきましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次