C++で2つのリストの共通要素を求める方法|set_intersectionの使い方
二人がそれぞれ別々のリストに希望する都市を挙げているとしましょう。このとき、両者が共通して選んでいる都市を見つけたい場面はよくあります。プログラミングにおいて「2つの集合に共通する要素」を求める処理は頻出のタスクであり、C++では標準ライブラリのset_intersectionを使うことで簡単に実現できます。
共通要素を求める基本的な考え方
この操作は、数学における「集合の積(インターセクション)」と同じ性質を持っています。2つのリストをそれぞれ集合とみなし、その積集合を求めることで、両方のリストに含まれる要素だけを取り出すことができます。
ただし、std::set_intersectionを使用する際には重要な前提条件があります。それは、入力となる両方の範囲があらかじめソートされている必要があるという点です。そのため、サンプルコードでは最初にsortを使って両リストを昇順に並べ替えています。
サンプルコード
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<string> commonInterest(string set1[], int n1, string set2[], int n2) {
vector<string> v(min(n1, n2));
vector<string>::iterator it;
// 両方のリストをソート
sort(set1, set1 + n1);
sort(set2, set2 + n2);
it = set_intersection(set1, set1 + n1, set2, set2 + n2,
v.begin());
return v;
}
int main() {
string first[] = { "Kolkata", "Hyderabad", "Chennai", "Delhi" };
int n1 = sizeof(first) / sizeof(first[0]);
string second[] = { "Mumbai", "Kolkata", "Durgapur", "Delhi" };
int n2 = sizeof(second) / sizeof(second[0]);
vector<string> v = commonInterest(first, n1, second, n2);
cout << "Common cities: ";
for (int i = 0; i < v.size(); i++)
cout << ' ' << v[i];
cout << endl;
}
実行結果
Common cities: Delhi Kolkata
1つ目のリスト(Kolkata、Hyderabad、Chennai、Delhi)と2つ目のリスト(Mumbai、Kolkata、Durgapur、Delhi)の両方に含まれているのは「Delhi」と「Kolkata」の2つであることが確認できます。なお、set_intersectionはソート済みの範囲を扱うため、結果も昇順に出力されます。
コードのポイント解説
- 結果格納用ベクターのサイズ: 共通要素の数は、小さい方のリストの要素数を超えることはないため、
min(n1, n2)で必要十分なサイズを確保しています。 - 戻り値のイテレータ:
set_intersectionは、書き込んだ最後の要素の次を指すイテレータを返します。実際の共通要素数を取得したい場合は、この戻り値とv.begin()の差分(it - v.begin())を利用します。 - 計算量: ソートにO(N log N)、積集合の計算自体はO(N + M)で行えるため、全体の計算量はO(N log N + M log M)となります。
- 元の配列を変更したくない場合: この例では引数として渡された配列を直接ソートしているため、元の順序が失われます。元のデータを保持したい場合は、事前に
vectorへコピーしてからソートするとよいでしょう。
なお、データが未ソートで元の順序を維持したいケースでは、unordered_setを使って片方のリストの要素を登録し、もう片方のリストを走査しながら存在チェックを行う方法もあります。要件に応じて使い分けることで、より柔軟な実装が可能です。
-
C++で2つの連結リストの交点を見つける方法
連結リストとは連結リスト(Linked List)は線形データ構造の一種です。各ノードは2つの部分で構成されており、一方にはノードの値(データ)が、もう一方には次のノードへのアドレス(ポインタ)が格納されています。ここでは、各ノードがリスト内の他のノードを指すポインタを持つ連結リストを想定します。この問題のタスクは、2つの連結リストが交差するノードを見つけることです。交差していない場合は、NULL(空)を出力として返します。入力例1出力:2解説: 与えられた連結リストは値「2」のノードで交差しているため、「2」を出力として返します。入力例2出力:NULL解説: 共通するノードが存在しないため、
-
C++で解くTwo Sum IV ― 二分探索木(BST)が入力の場合
問題概要 二分探索木(BST)とターゲット値が1つ与えられます。このとき、BST内に「2つの要素の和がターゲット値と等しくなる」ような組み合わせが存在するかどうかを判定するのが本問題です。 例えば、次のような木が入力として与えられた場合を考えてみましょう。 この場合、出力は True(真)となります。 解法のアプローチ この問題は、BSTを中間順(inorder)走査して昇順の配列を作り、その後「双方向ポインタ(two pointer)」を使うことで効率的に解けます。具体的には、以下の手順に従います。 値を格納するための配列 v を定義します。 関数 inorder() を定義します(引