C++で2次元行列を効率的に検索する方法
m × n の行列の中から特定の値を効率的に検索するアルゴリズムを考えます。この行列には、次のような性質があります。
- 各行は左から右に向かって昇順にソートされている
- 各行の先頭の数値は、直前の行の最後の整数よりも大きい
例えば、行列が次のような場合を考えてみましょう。
| 1 | 3 | 5 | 7 |
| 10 | 11 | 16 | 20 |
| 23 | 30 | 34 | 50 |
| 53 | 62 | 78 | 98 |
このとき、検索対象の値が 16 であれば、出力は True になります。
アルゴリズムの考え方
この問題は「2段階の二分探索」によって解くことができます。まず行方向の二分探索で、ターゲット値が存在しうる行を特定し、次にその行の中で列方向の二分探索を行います。計算量は O(log m + log n) となり、全要素を線形に走査する O(m × n) よりも大幅に高速です。
具体的な手順は以下の通りです。
- n := 行数(n が 0 なら false を返す)、m := 列数(m が 0 なら false を返す)
- low := 0、high := n − 1 とする
- low < high の間、次を繰り返す
- mid := low + (high − low + 1) / 2
- mat[mid][0] <= target なら low := mid、そうでなければ high := mid − 1
- rlow := 0、rhigh := m − 1、ans := 0 とする
- rlow <= rhigh の間、次を繰り返す
- mid := rlow + (rhigh − rlow) / 2
- mat[low][mid] == target なら ans := 1 としてループを抜ける
- matrix[low][mid] < target なら rlow := mid + 1
- それ以外の場合は rhigh := mid − 1
- ans を返す
理解を深めるために、以下の実装例を見てみましょう。
実装例
#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
lli n,m;
n = matrix.size();
if(!n)return false;
m = matrix[0].size();
if(!m)return false;
lli low = 0, high = n-1;
while(low<high){
lli mid = low + ( high - low +1)/2;
if(matrix[mid][0]<=target)low = mid;
else high = mid -1;
}
lli rlow = 0, rhigh = m-1;
lli ans = 0;
while(rlow<=rhigh){
lli mid = rlow+(rhigh - rlow)/2;
if(matrix[low][mid] == target){
ans =1;
break;
}else if(matrix[low][mid]<target)rlow=mid+1;
else rhigh= mid-1;
}
return ans;
}
};
main(){
Solution ob;
vector<vector<int>> v = {{1,3,5,7},{10,11,16,20},{23,30,34,50},{53,62,78,98}};
cout << ob.searchMatrix(v, 16);
}入力
[[1,3,5,7],[10,11,16,20],[23,30,34,50],[53,62,78,98]] 16
出力
1
この実装では、まず各行の先頭要素を比較してターゲットが属する行を絞り込み、続いてその行内で通常の二分探索を行うことで、値の存在を効率的に判定しています。行列が空の場合や行・列が 0 の場合も適切に処理されるため、堅牢な実装となっています。
-
C++で二分探索木(BST)を復元するアルゴリズムを解説
問題の概要二分探索木(BST)において、誤って2つのノードの値が入れ替わってしまった状態を考えます。この記事では、入れ替わった2つのノードを特定し、木を正しい二分探索木の状態へ復元する方法をC++で解説します。例えば、次のような木が与えられた場合(左図)、復元後の木は右図のようになります。解決のアプローチ二分探索木には「中順(インオーダー)走査を行うと、ノードの値が昇順に並ぶ」という重要な性質があります。この性質を利用すると、入れ替わったノードを効率的に検出できます。具体的には、以下の手順で解決します。prev、first、second という3つのノード参照を用意します。findProble
-
C++プログラムにおける二分探索(バイナリサーチ)の基本と実装
二分探索(バイナリサーチ)とは二分探索は「半区間探索」「対数探索」「バイナリチョップ」とも呼ばれる検索アルゴリズムで、ソート済みの配列の中から目的の値が存在する位置を効率的に見つけ出します。基本的な仕組みは非常にシンプルです。まず、探したい値(ターゲット値)を配列の中央の要素と比較します。一致しなかった場合は、ターゲット値が存在し得ない半分を丸ごと排除し、残りの半分に対して同様の比較を繰り返します。この「中央との比較」と「範囲の絞り込み」を続け、ターゲット値が見つかるか、検索範囲が空になる(=配列にその値が存在しない)かのどちらかで処理が終了します。アイデア自体は簡単ですが、正しく実装するには