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

【C++】ターゲットに最も近い3つの数の合計を求めるアルゴリズム

問題の概要

n個の整数を含む配列 nums と、1つのターゲット値 target が与えられます。この中から3つの整数を選び、その合計がターゲットに最も近くなるような組み合わせを見つけ、その合計値を返すことが目的です。なお、各入力には必ず解が1つだけ存在すると仮定してよいものとします。

例えば、配列が [-1, 2, 1, -4]、ターゲットが 1 の場合、最適な組み合わせは [-1, 2, 1] で、その合計は 2 となります。これがターゲットに最も近い合計値です。

解法のアプローチ

この問題は、配列をソートした上で「双方向ポインタ(Two Pointers)」というテクニックを使うことで効率的に解けます。手順は以下の通りです。

  • 配列 nums をソートし、答えを格納する変数 ans を 0、最小差分 diff を無限大(INT_MAX)、要素数 n をそれぞれ初期化する
  • i を 0 から n−1 までループさせる
    • left := i + 1、right := n − 1 として設定する
    • left < right の間、以下を繰り返す
      • temp := nums[left] + nums[right] + nums[i] を計算する
      • |target − temp| が diff より小さければ、ans := temp、diff := |target − temp| に更新する
      • temp が target と一致すれば即座に temp を返す。temp > target なら right を1減らし、そうでなければ left を1増やす
  • ループ終了後、ans を返す

この手法により、すべての組み合わせを総当たりする O(n³) の計算量を、O(n²) まで大幅に削減できます。

C++での実装例

以下に実際のC++コードを示します。

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int ans = 0;
        int diff = INT_MAX;
        int n = nums.size();
        for(int i = 0; i < n; i++){
            int left = i + 1;
            int right = n - 1;
            while(left < right){
                 int temp = nums[left] + nums[right] + nums[i];
                 if(abs(target - temp) < diff){
                     ans = temp;
                     diff = abs(target - temp);
                 }
                 if(temp == target)return temp;
                 else if(temp > target) right--;
                 else left++;
            }
        }
        return ans;
    }
};
main(){
    Solution ob;
    vector<int> v = {-1,2,1,-4};
    cout << ob.threeSumClosest(v, 1);
}

入力例

[-1,2,1,-4]
1

出力例

2

計算量の分析

時間計算量:O(n²) — 外側のループで1点を固定し、内側で双方向ポインタによる線形走査を行うためです。

空間計算量:O(1) — 追加のデータ構造を使用せず、定数個の変数のみで処理が完結するためです。

  1. C++で解く積配列パズル ― 除算なし・O(1)の追加メモリで実現する方法

    問題の概要今回は配列に関する興味深いパズルを取り上げます。n個の要素を持つ配列が与えられたとき、同じくn個の要素を持つ別の配列を作成します。ただし、新しい配列のi番目の要素には、元の配列のi番目の要素を除いた残りすべての要素の積を格納する必要があります。この問題には次の2つの制約があります。除算演算子(/)を使用してはならない出力用の配列以外、追加のメモリ領域はO(1)に抑えることもし除算が許されるなら話は簡単です。配列全体の積を事前に計算しておき、それを各要素で割った値を順に格納すればよいからです。しかし、配列に0が含まれると除算が使えない、積が大きくなるとオーバーフローの恐れがあるといった

  2. C++で組み合わせをすべて生成する方法【バックトラッキング解説】

    問題概要2つの整数 n と k が与えられたとき、1 から n までの数字の中から k 個を選んで作れるすべての組み合わせを求めます。例えば、n = 4、k = 2 の場合、答えは [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] となります。解法の考え方:バックトラッキングこの種の問題は、バックトラッキング(探索の巻き戻し)と呼ばれる手法で効率的に解くことができます。再帰関数を使って候補の数字を一つずつ選びながら組み合わせを構築し、条件を満たした時点で結果を保存していきます。アルゴリズムの手順再帰関数 solve() を用意します。引数は n、k、現在の組み合わせを