【Python】配列要素とインデックスが一致する最小のインデックスを見つけるプログラム
問題の概要
すべての要素が重複なく、昇順にソートされたリスト nums が与えられます。この中から、nums[i] = i(要素の値とインデックスが一致する)を満たす最小のインデックス i を見つける必要があります。条件を満たすインデックスが存在しない場合は -1 を返してください。さらに、この問題は O(log n) の時間計算量で解くことが求められています。
たとえば、入力が nums = [-4, -1, 2, 3, 8] の場合、出力は 2 となります。nums[2] = 2 と nums[3] = 3 の両方が条件を満たしていますが、より小さい方の「2」が答えになるためです。
解法の考え方:二分探索
O(log n) という計算量の制約を満たすには、先頭から順に調べる線形探索ではなく、二分探索(バイナリサーチ)を活用します。配列が昇順にソートされ、かつ要素がすべて一意であることを利用すると、次のような性質が成り立ちます。
nums[mid] >= midの場合:それより右側のインデックスでは必ず値がインデックスを上回るため、答えは左半分にしか存在しません。探索範囲を左側に狭めます。nums[mid] < midの場合:それより左側に条件を満たすインデックスは存在しないため、探索範囲を右半分に狭めます。
この性質により、各ステップで探索範囲を半分に絞り込みながら、効率的に答えを求めることができます。
アルゴリズムの手順
- 変数
retを-1で初期化し、探索範囲の左端lhsを 0、右端rhsを「リストの長さ − 1」とします。 lhs <= rhsの間、以下を繰り返します。midを(lhs + rhs) // 2(切り捨て除算)として計算します。nums[mid] == midであれば、ret = midとして答えを記録します。nums[mid] >= midであれば、rhs = mid - 1として左側を探索します。- そうでなければ、
lhs = mid + 1として右側を探索します。
- ループ終了後、
retを返します。
Pythonでの実装例
以下が実際の実装コードです。
def solve(nums):
ret = -1
lhs = 0
rhs = len(nums) - 1
while lhs <= rhs:
mid = (lhs + rhs) // 2
if nums[mid] == mid:
ret = mid
if nums[mid] >= mid:
rhs = mid - 1
else:
lhs = mid + 1
return ret
nums = [-4, -1, 2, 3, 8]
print(solve(nums))入力
[-4, -1, 2, 3, 8]
出力
2
計算量のまとめ
- 時間計算量: O(log n) — 各反復で探索範囲が半分になるため。
- 空間計算量: O(1) — 追加のデータ構造を必要としないため。
-
Pythonで配列内の最大要素を見つける方法【初心者向け解説】
本記事では、配列の中から最大の要素を見つけるための解法とアプローチについて詳しく解説します。 問題の概要 配列が入力として与えられたとき、その中から最も大きい要素を見つけ出すことが課題となります。 アプローチ この問題は「線形探索」と呼ばれるシンプルな手法で解決できます。手順は以下の通りです。 まず、変数 max を配列の最初の要素で初期化します。 次に、2番目の要素から配列の末尾まで順番に走査していきます。 走査中の各要素について、現在の max の値と比較します。 要素が max より大きければ、max の値をその要素で更新します。 そうでなければ、そのまま次の要素へ進みます。 この処
-
【Python】配列の全要素の積をnで割った余りを求めるプログラムの書き方
本記事では、以下の問題に対する解決策について詳しく解説します。問題文複数の数値からなる配列と整数 n が与えられたとき、配列内のすべての要素を掛け合わせた結果を n で割った余りを出力する必要があります。アプローチまず、arr[i] % n のように各要素の余りを個別に計算します。次に、その余りを現在の結果に掛け合わせます。掛け算を行うたびに再度剰余演算を適用することで、オーバーフローを回避できます。この手法は、モジュラー算術(合同式)の分配則に基づいています。( a * b) % c = ( ( a % c ) * ( b % c ) ) % c実装例def findremainder(ar