C++で「右側の区間」を見つけるアルゴリズムを解説
問題概要
区間のリストが与えられたとき、各区間 i について、「始点が区間 i の終点以上であるような区間 j」が存在するかどうかを調べます。このとき、区間 j は区間 i の「右側」にあると言います。
各区間 i に対して、条件を満たす区間 j のうち始点が最小となるもののインデックスを記録します。該当する区間が存在しない場合は -1 を格納し、最終的に各区間に対応する値を配列として出力します。
たとえば、入力が [[3,4], [2,3], [1,2]] の場合、出力は [-1, 0, 1] となります。
- [3, 4]: 右側に位置する区間が存在しないため -1
- [2, 3]: 始点が 3 以上の区間のうち最小のものは [3, 4](インデックス 0)
- [1, 2]: 始点が 2 以上の区間のうち最小のものは [2, 3](インデックス 1)
解法のアプローチ
この問題は、std::map(平衡二分探索木)と lower_bound を組み合わせることで効率的に解けます。手順は以下の通りです。
- n を区間配列のサイズとし、サイズ n の配列 ret を作成してすべて -1 で初期化します。さらに、マップ m を用意します。
- i を 0 から区間の個数までループさせます。
- intervals[i][0](始点)がすでに m に登録されている場合はスキップします。
- m[intervals[i][0]] = i + 1 として、始点をキー、インデックス + 1 を値として登録します。
- i を n - 1 から 0 まで逆順にループさせます。
- it を、intervals[i][1](終点)以上のキーのうち最小のものを指すイテレータ(lower_bound の結果)とします。
- 該当する要素が存在しない場合(end() を指す場合)は次の反復へ進みます。
- ret[i] = it の値 − 1 とします。
- ret を返します。
値を「インデックス + 1」として登録しているのは、有効な要素との区別を明確につけるためです。取り出す際に 1 を引けば元のインデックスが復元できます。
計算量は、マップへの挿入と検索がそれぞれ O(log n) であることから、全体で時間計算量 O(n log n)、空間計算量 O(n) となります。
C++ 実装例
以下の実装を見ると、処理の流れがより理解しやすくなります。
#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
cout << "[";
for(int i = 0; i < v.size(); i++){
cout << v[i] << ", ";
}
cout << "]" << endl;
}
class Solution {
public:
vector<int> findRightInterval(vector<vector<int>>& intervals) {
int n = intervals.size();
vector<int> ret(n, -1);
map<int, int> m;
// 始点をキー、インデックス+1を値としてマップに登録
for(int i = 0; i < intervals.size(); i++){
if(m.count(intervals[i][0])) continue;
m[intervals[i][0]] = i + 1;
}
// 各区間の終点以上の最小の始点を二分探索で取得
for(int i = n - 1; i >= 0; i--){
map<int, int>::iterator it = m.lower_bound(intervals[i][1]);
if(it == m.end()) continue;
ret[i] = it->second - 1;
}
return ret;
}
};
main(){
vector<vector<int>> v = {{3,4},{2,3},{1,2}};
Solution ob;
print_vector(ob.findRightInterval(v));
}
入力
[[3,4],[2,3],[1,2]]
出力
[-1, 0, 1]
-
C++で二分木内の指定キーの次の右ノードを検索する方法
問題概要この問題では、二分木(Binary Tree)とキー値が与えられます。目的は、指定されたキーを持つノードの次の右ノードを見つけることです。二分木とは、各ノードが最大2つの子ノード(左の子と右の子)を持つ特殊なデータ構造で、データの格納や効率的な探索に広く活用されています。具体例で理解しよう入力key = 4出力5説明ノード4と同じレベルに位置し、その右隣にある要素は5です。したがって、答えは5となります。解決アプローチこの問題に対するシンプルな解決策は、幅優先探索(レベル順走査)を用いて二分木を走査することです。具体的には、以下の手順で処理を行います。キューを使用してレベル順にノードを
-
C++で二分木の重複する部分木を検出する方法
問題の概要二分木が与えられたとき、その中に存在する重複する部分木(duplicate subtrees)をすべて見つける問題を考えてみましょう。ここでいう「重複」とは、構造とノードの値が完全に一致する部分木が2つ以上存在することを意味します。各種類の重複部分木について、代表としてどれか1つの根ノードを返せばよいことになっています。たとえば、次のような二分木があるとします。この木に含まれる重複する部分木は、以下の2つです。値 4 を持つ単一ノードの部分木(2か所に出現)根が 2 で、子に 4 を持つ部分木(2か所に出現)解法のアプローチ:部分木のシリアライズこの問題を効率的に解く鍵となるのは、部