C++で解く「Contains Duplicate III」問題:multisetを使った効率的な判定方法
整数型の配列が与えられ、配列内に異なる2つのインデックス i と j が存在するかどうかを判定する問題を考えます。条件は「nums[i] と nums[j] の絶対差が t 以下」かつ「i と j の絶対差が k 以下」であることです。例えば、入力が [1,2,3,1]、k = 3、t = 0 の場合、条件を満たすペアが存在するため true を返します。
解法アプローチ
この問題は、平衡二分探索木(C++では multiset)を利用することで効率的に解けます。手順は以下の通りです。
- 空の集合 s を用意し、n を nums 配列のサイズとします。
- i を 0 から n − 1 までループさせます。
- x を「nums[i] 以上の値を持つ最小の要素」(lower_bound の結果)とします。
- x が集合の末尾ではなく、その値が nums[i] + t 以下であれば true を返します。
- x が集合の先頭要素でない場合は、次の処理を行います。
- x を1つ前の要素へ移動します。
- その値 + t が nums[i] 以上であれば true を返します。
- nums[i] を s に挿入します。さらに i >= k の場合、範囲外となった nums[i − k] を s から削除します。
- ループ完了後に該当するペアが見つからなければ、false を返します。
計算量について
multiset の挿入・検索・削除の各操作は O(log k) で実行できるため、全体の時間計算量は O(n log k)、空間計算量は O(min(n, k)) となります。これにより、素朴な全ペア探索(O(n²))よりも大幅に高速化できます。
C++での実装例
以下の実装コードを見ると、動作の理解がより深まるでしょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
multiset <int> s;
int n = nums.size();
for(int i = 0; i< n; i++){
multiset <int> :: iterator x = s.lower_bound(nums[i]);
if(x != s.end() && *x <= nums[i] + t ) return true;
if(x != s.begin()){
x = std::next(x, -1);
if(*x + t >= nums[i])return true;
}
s.insert(nums[i]);
if(i >= k){
s.erase(s.lower_bound(nums[i - k]));
}
}
return false;
}
};
main(){
Solution ob;
vector<int> v = {1,2,3,1};
cout << (ob.containsNearbyAlmostDuplicate(v, 3,0));
}入力
[1,2,3,1] 3 0
出力
1
-
C++で解くパス合計III:DFSで二分木のルートからリーフまでの経路を探索
整数のキーを持つノードで構成される二分木が与えられたとき、合計値が指定した値と一致する「ルートからリーフ(葉)までの経路」をすべて見つける問題を考えます。経路は必ず根から始まり、葉で終わる必要があります。 問題の例 たとえば、次のような二分木 [5,4,8,11,null,13,4,7,2,null,null,5,1] があり、目標の合計値が 22 だとします。 このとき、条件を満たす経路は次の 2 本です。 [[5, 4, 11, 2], [5, 8, 4, 5]] 解法のアプローチ:DFS(深さ優先探索)+バックトラック この問題は、少し手を加えた DFS(深さ優先探索)関数で効率的に解
-
【C++】二分木にサイズ2以上の重複する部分木が存在するかを判定する方法
問題の概要 二分木が与えられたとき、その木の中にサイズ2以上の同一の部分木(重複部分木)が存在するかどうかを判定するのが本記事のテーマです。例として、次のような二分木を考えてみます。 この木には、サイズ2の同一の部分木が2つ含まれています。このように、同じ構造・同じノード値を持つ部分木が複数存在するかどうかを効率よくチェックする必要があります。 アプローチ:シリアライズとハッシュの活用 この問題は、部分木のシリアライズ(文字列化)とハッシュテーブルを組み合わせることで効率的に解くことができます。基本的なアイデアは以下の通りです。 各ノードから再帰的に部分木を文字列としてシリアライズし、ハ