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

JavaScriptで回転ソート配列から最小要素を二分探索で効率的に求める方法

問題の概要

整数の配列を引数として受け取るJavaScript関数を作成する必要があります。

この配列は、まず昇順にソートされ、その後任意の数だけ回転(ローテート)されたものです。私たちの関数は、この配列の中から最小の要素を見つけて返す必要があります。

ただし、重要な条件があります。それは線形時間未満(O(n)より速い)で処理を完了しなければならないという点です。この要件を満たすために、二分探索アルゴリズムを応用した手法を使用します。

入力配列が次の場合:

const arr = [6, 8, 12, 25, 2, 4, 5];

この配列は [2, 4, 5, 6, 8, 12, 25] を3要素分回転させたものなので、出力は 2 となります。

解決のアプローチ

回転ソート配列には「回転点(ピボット)」と呼ばれる境界が存在し、そこを境に並び順が崩れています。最小値は必ずこの回転点上にあるため、二分探索を工夫することで O(log n) の時間計算量で最小値を特定できます。

基本的な考え方は以下の通りです:

  • 中央の要素を確認し、探索範囲のどちら側に最小値があるかを判定する
  • 左端の値が中央の値より小さく、中央の値が右端以下なら、整列済み部分にいるので左側へ絞り込む
  • 左端の値が中央の値より大きければ、回転点は左側にあるので左へ移動する
  • 重複した値(左端・中央・右端がすべて同じ)の場合は、線形に範囲を縮めて無限ループを防ぐ

実装コード

以下が実際のコードです:

const arr = [6, 8, 12, 25, 2, 4, 5];
const findMin = (arr = []) => {
    let temp;
    let min = 0;
    let max = arr.length - 1;
    let currentMin = Number.POSITIVE_INFINITY;
    while (min <= max) {
        temp = (min + max) >> 1;
        currentMin = Math.min(currentMin, arr[temp]);
        if (arr[min] < arr[temp] && arr[temp] <= arr[max] || arr[min] > arr[temp]) {
            max = temp - 1;
        } else if (arr[temp] === arr[min] && arr[min] === arr[max]) {
            let guessNum = arr[temp];
            while (min <= max && arr[min] === guessNum) {
                min++;
            }
        } else {
            min = temp + 1;
        }
    }
    return currentMin;
};
console.log(findMin(arr));

コードのポイント解説

  • currentMin には初期値として Number.POSITIVE_INFINITY を設定し、走査中に遭遇した最小値を常に記録します。
  • (min + max) >> 1 はビットシフト演算による中央インデックスの計算で、オーバーフローを気にせず高速に中央値を求められます。
  • 重複要素への対応により、LeetCodeの「Find Minimum in Rotated Sorted Array II」のような最悪ケースでも正しく動作します。

実行結果

コンソール出力は以下の通りです:

2

このように、二分探索を応用することで、回転されたソート配列から最小要素を O(log n) の時間計算量で効率的に求めることができます。配列のサイズが大きい場合でも高速に動作するため、実務においても有用なテクニックです。

  1. 【JavaScript入門】配列内で最初の非連続な数値を見つける方法

    はじめに本記事では、JavaScriptを使って「数値の配列の中から、直前の要素と連続していない最初の数値」を見つける方法を解説します。アルゴリズムの練習やコーディング面接の対策としても役立つ基本的な問題です。 問題の定義数値の配列を受け取るJavaScript関数を作成する必要があります。この関数は、直前の要素に対して +1 となっていない(連続していない)最初の要素を返さなければなりません。 言い換えると、隣り合う要素同士の差が1以外になる箇所が現れたとき、その箇所の後ろ側の要素を返すという処理です。なお、そのような要素が必ず配列内に1つ以上存在するものとします。 サンプルコード以下は、実

  2. C++でソート済み・回転配列から最大要素を効率的に求める方法

    問題の概要 昇順にソートされた重複のない要素を持つ配列が、ある未知の位置で回転されているとします。この記事では、二分探索の考え方を活用し、O(log n) の計算量で配列内の最大要素を効率的に見つける C++ プログラムを紹介します。 例 たとえば、入力配列が {30, 40, 50, 10, 20} の場合、最大要素は 50 になります。 アルゴリズム 回転されたソート済み配列には、次のような重要な性質があります。最大要素は「隣接する次の要素が自分より小さい」という条件を満たす唯一の要素です。もし次の要素が自分より小さい要素が存在しなければ、配列は回転されていないことになり、最後の要素が最大