【Python】特定の範囲の要素をまとめて更新するプログラムの書き方
数値のリスト nums と操作のリスト operations が与えられているとします。各操作は [L, R, X] という3つのフィールドを持ち、「インデックス L から R まで(両端を含む)のすべての要素に X を加算する」という意味です。すべての操作を適用し、最終的なリストを返すのがこの問題の目的です。
問題の例
たとえば、入力が次のような場合を考えてみましょう。
nums = [8, 4, 2, -9, 4] operations = [[0, 0, 3], [1, 3, 2], [2, 3, 5]]
このときの出力は [11, 6, 9, -2, 4] になります。初期リストが [8, 4, 2, -9, 4] であり、各操作が以下のように段階的に適用されるためです。
- 最初の操作
[0, 0, 3]を実行 → リストは[11, 4, 2, -9, 4]になる - 次の操作
[1, 3, 2]を実行 → リストは[11, 6, 4, -7, 4]になる - 最後の操作
[2, 3, 5]を実行 → リストは[11, 6, 9, -2, 4]になる
解法のアプローチ:イベント方式(差分記録)
各操作のたびに範囲内の要素を直接書き換えると、操作数や範囲の長さが大きくなった際に計算量が膨張してしまいます。そこで有効なのが「イベント方式」と呼ばれる手法です。区間の始点で加算イベントを、終点+1の位置で減算イベントを記録しておき、あとから先頭から一括して累積することで、範囲更新を効率的に処理できます。
具体的な手順は以下の通りです。
events:= 新しい空のリストを作成するoperations内の各(l, r, inc)について:eventsの末尾に(l, inc)を追加するeventsの末尾に(r + 1, -inc)を追加する
eventsリストをソートするinc := 0、ptr := 0と初期化する- i を 0 から nums のサイズ未満まで繰り返す:
ptr < len(events)かつevents[ptr][0] == iの間、次を繰り返す:inc := inc + events[ptr][1]ptr := ptr + 1
nums[i] := nums[i] + inc
numsを返す
この方法のポイントは、各位置における「現在の累積加算値」を変数 inc で管理しながら、配列を一度だけ走査する点にあります。区間の開始位置で値を増やし、終了位置の直後で減らすことで、どの要素にいくつ加算すべきかを正確に把握できる仕組みです。
Python実装例
理解を深めるために、実際のコードを見てみましょう。
class Solution: def solve(self, nums, operations): events = [] for l, r, inc in operations: events.append((l, inc)) events.append((r + 1, -inc)) events.sort() inc = 0 ptr = 0 for i in range(len(nums)): while ptr < len(events) and events[ptr][0] == i: inc += events[ptr][1] ptr += 1 nums[i] += inc return nums ob = Solution() nums = [8, 4, 2, -9, 4] operations = [ [0, 0, 3], [1, 3, 2], [2, 3, 5] ] print(ob.solve(nums, operations))
入力
nums = [8, 4, 2, -9, 4] operations = [[0, 0, 3], [1, 3, 2], [2, 3, 5]]
出力
[11, 6, 9, -2, 4]
計算量について
N を配列の要素数、M を操作の数とすると、このアルゴリズムの時間計算量は O(M log M + N) です。イベントリストのソートが支配的であり、その後の走査は線形時間で完了します。各操作を素朴に適用する方法(最悪 O(N × M))と比べ、大量のデータでも高速に動作する点が大きなメリットです。
-
指定した範囲内で奇数個の約数を持つ要素の個数を求めるPythonプログラム
本記事では、以下の問題文に対する解決策について詳しく解説します。 問題文 問題 − ある範囲 [n, m] が与えられたとき、その範囲内に存在する「奇数個の約数を持つ数」の個数を求めます。 アプローチ まず押さえておきたい重要な性質として、「完全平方数だけが奇数個の約数を持つ」ということが挙げられます。これは、約数が通常ペア(d と n/d)で現れるのに対し、完全平方数の場合は平方根に相当する約数だけがペアにならず単独で存在するためです。 したがって、この問題は「指定された範囲内に含まれる完全平方数の個数を数える」ことに帰着できます。 なお、n と m はどちらも範囲に含まれるため、境界値での
-
Pythonで整数配列の重複を除去し、個別の要素だけを出力する方法
整数型の配列が与えられ、その中には重複した要素が含まれている場合があります。この記事では、重複を取り除いて個別(ユニーク)な値だけを出力するPythonプログラムを解説します。 実行例 入力:A = [1, 2, 3, 4, 2, 3, 5, 6] 出力:[1, 2, 3, 4, 5, 6] アルゴリズム このプログラムは次の手順で動作します。 配列の要素を入力として受け取ります。 各要素を先頭から順番に1つずつ取り出します。 取り出した要素が、それ以前にすでに出力されたものかどうかを確認します。 初期値0のフラグ変数を用意し、すでに表示済みなら1、未表示なら0のままにします。 フラ