C++
 Computer >> コンピューター >  >> プログラミング >> C++

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]

  1. C++で二分木内の指定キーの次の右ノードを検索する方法

    問題概要この問題では、二分木(Binary Tree)とキー値が与えられます。目的は、指定されたキーを持つノードの次の右ノードを見つけることです。二分木とは、各ノードが最大2つの子ノード(左の子と右の子)を持つ特殊なデータ構造で、データの格納や効率的な探索に広く活用されています。具体例で理解しよう入力key = 4出力5説明ノード4と同じレベルに位置し、その右隣にある要素は5です。したがって、答えは5となります。解決アプローチこの問題に対するシンプルな解決策は、幅優先探索(レベル順走査)を用いて二分木を走査することです。具体的には、以下の手順で処理を行います。キューを使用してレベル順にノードを

  2. C++で二分木の重複する部分木を検出する方法

    問題の概要二分木が与えられたとき、その中に存在する重複する部分木(duplicate subtrees)をすべて見つける問題を考えてみましょう。ここでいう「重複」とは、構造とノードの値が完全に一致する部分木が2つ以上存在することを意味します。各種類の重複部分木について、代表としてどれか1つの根ノードを返せばよいことになっています。たとえば、次のような二分木があるとします。この木に含まれる重複する部分木は、以下の2つです。値 4 を持つ単一ノードの部分木(2か所に出現)根が 2 で、子に 4 を持つ部分木(2か所に出現)解法のアプローチ:部分木のシリアライズこの問題を効率的に解く鍵となるのは、部