コミック:合計が配列内の「特定の値」である3つの数値を見つける方法は?

少し前に、LeetCodeに古典的なアルゴリズムの問​​題[2つの数値の合計の問題]を導入しました

今回は、問題を少し拡張して、合計が「特定の値」になる配列内の3つの数値を見つけようとします

トピックの特定の要件は何ですか?次の整数配列が与えられます:

13などの特定の値を任意に選択し、3つの数値の合計が13に等しいすべての組み合わせを見つけるように依頼します

5 + 6 + 2 = 13、5 + 1 + 7 = 13、3 + 9 + 1 = 13であるため、最終的な出力結果は次のようになります。

【5、6、2】

【5、1、7】

【3、9、1】


Xiao Huiのアイデアは、元の「3つの数の合計の問題」を「2つの数の合計の問題」にn回変換することです。

上記の配列を例として取り上げ、13の特定の値を選択し、XiaoHuiの特定のアイデアを示します。

最初のラウンドでは、配列の最初の要素5にアクセスし、問題を次の要素から合計8(13-5)になる2つの数値を見つけることに変換します。

合計が8である2つの数字を見つける方法は?前回の発言によると、ハッシュテーブルを使用して効率的に解決できます。

2回目のラウンドでは、配列の2番目の要素12にアクセスし、問題を次の要素から合計1(13-12)になる2つの数値を見つけるように変換します。

第3ラウンドでは、配列の3番目の要素6にアクセスし、問題を次の要素から合計7(13-6)になる2つの数値を見つけることに変換します。

類推すると、アレイ全体を常にトラバースすることは、2つの数値の合計の問題をn回解決することと同じです。

    public static List<List<Integer>> threeSum(int[] nums, int target) {
        List<List<Integer>> resultList = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            Map<Integer, Integer> map = new HashMap<>();
            int d1 = target - nums[i];
            //寻找两数之和等于d1的组合
            for (int j = i+1; j < nums.length; j++) {
                int d2 = d1 - nums[j];
                if (map.containsKey(d2)) {
                    resultList.add(Arrays.asList(nums[i], d2, nums[j]));
                }
                map.put(nums[j], j);
            }
        }
        return resultList;
    }

上記のコードでは、各ラウンドで「2つの数値の合計問題」を解く時間の複雑さはO(n)であり、合計n回の反復であるため、解の合計時間の複雑さはO(n²)です。

スペースの複雑さに関しては、同じハッシュテーブルが繰り返し作成され、ハッシュテーブルには最大でn-1のキーと値のペアがあるため、ソリューションのスペースの複雑さはO(n)です。

前の配列を例として使用して、配列を昇順で並べ替えます。

これは少し抽象的です。詳しく説明しましょう。

最初のラウンドでは、配列の最初の要素1にアクセスし、問題を次の要素から合計12(13-1)になる2つの数値を見つけることに変換します。

合計が12である2つの数字を見つける方法は?2つのポインターを設定します。ポインターjは残りの要素の左端の要素2を指し、ポインターkは右端の要素12を指します。

2つのポインターの対応する要素の合計2+ 12 = 14> 12を計算すると、結果が大きすぎます。

配列は昇順で配置されるため、kの左側の要素はk未満である必要があります。したがって、ポインターkを左に移動します。

2つのポインターの対応する要素の合計、2 + 9 = 11 <12を計算します。今回は結果が小さすぎます。

jの右側の要素はjより大きくなければならないので、ポインタjを1つ右に移動します。

2つのポインターの対応する要素の合計3+ 9 = 12を計算します。これは、要件を満たしています。

:我々が正常に一致する組み合わせの集合たので1、3、9

しかし、これで終わりではありません。他の組み合わせを探し続ける必要があります。ポインタkを左に動かし続けます。

 2つのポインターの対応する要素の合計を計算します(3 + 7 = 10 <12)。結果は小さすぎます。

したがって、ポインタjを右に移動します。

2つのポインターの対応する要素の合計5+ 7 = 12を計算し、要件を満たすグループを見つけます。

1、5、7

引き続き検索を行い、ポインタkを左に移動します。

 2つのポインターの対応する要素の合計を計算します(5 + 6 = 11 <12)。結果は小さすぎます。

したがって、ポインタjを右に移動します。

この時点で、2つのポインターは一致しており、移動を続けると、以前に見つかった組み合わせが繰り返される可能性があるため、サイクルを直接終了します。

2回目のラウンドでは、配列の2番目の要素2にアクセスし、問題を変換して、次の要素から合計11(13-2)になる2つの数値を見つけます。

まだ2つのポインターを設定しています。ポインターjは残りの要素の左端の要素3を指し、ポインターkは右端の要素12を指します。

 2つのポインターの対応する要素の合計を計算します(3 + 12 = 15> 11)。結果が大きすぎます。

ポインタkを左に移動します。

 2つのポインターの対応する要素の合計を計算します(3 + 9 = 12> 11)。結果はまだ大きすぎます。

ポインタkを左に移動し続けます。

 2つのポインターの対応する要素の合計を計算します(3 + 7 = 10 <11)。結果は小さすぎます。

ポインタjを右に移動します。

 2つのポインターの対応する要素の合計を計算します(5 + 7 = 12> 11)。結果が大きすぎます。

ポインタkを左に移動します。

 2つのポインターの対応する要素の合計5+ 6 = 11を計算して、要件を満たすグループを見つけます。

2、5、6

引き続き検索を行い、ポインタkを左に移動します。

この時点で、2つのポインターが再び重なり、サイクルが終了します。

この考えによれば、私たちはアレイ全体をトラバースしてきました。

このように、2つのポインターを使用して配列の両端を指し、常に近づいて中央に調整し、一致する組み合わせを見つけます。これは、「クランプ方法」とも呼ばれるダブルポインター方法です。

    public static List<List<Integer>> threeSumv2(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        //大循环
        for (int i = 0; i < nums.length; i++) {
            int d = target - nums[i];
            // j和k双指针循环定位,j在左端,k在右端
            for (int j=i+1,k=nums.length-1; j<nums.length; j++) {
                // k指针向左移动
                while (j<k && (nums[j]+nums[k])>d) {
                    k--;
                }
                //双指针重合,跳出本次循环
                if (j == k) {
                    break;
                }
                if (nums[j] + nums[k] == d) {
                    List<Integer> list = Arrays.asList(nums[i], nums[j], nums[k]);
                    resultList.add(list);
                }
            }
        }
        return resultList;
    }

上記のコードは表面に3つのループがありますが、各ラウンドでのポインターjとkの移動数は合計でn-1回になるため、ソリューションの全体的な時間の複雑さはO(n²)です。

最も重要なことは、このソリューションは追加のセットを使用しない(ソートは入力配列で直接実行される)ため、スペースの複雑さはO(1)のみであるということです。

- - -終わり - - -

この記事が好きな友達は、公式アカウントプログラマーのXiaohuiをフォローして、 もっとエキサイティングなコンテンツを見てください。

点个[在看],是对小灰最大的支持!

おすすめ

転載: blog.csdn.net/bjweimengshu/article/details/109040017