Pythonで重複する区間をマージするアルゴリズムの実装方法
はじめに
区間(インターバル)のコレクションが与えられたとき、重なり合っているすべての区間を1つに統合(マージ)する問題を考えてみましょう。
例えば、区間が [[1,3], [2,6], [8,10], [15,18]] のように与えられた場合、マージ後の結果は [[1,6],[8,10],[15,18]] となります。これは、[1,3] と [2,6] の2つの区間が互いに重なっているため、これらを統合して [1,6] とするからです。
解法のアプローチ
この問題は以下の手順で解くことができます。
- 区間リストの長さが0の場合、空のリストを返す
- クイックソートなどのソート手法を使って、区間リストを開始位置の昇順に並べ替える
- 空のスタックを用意し、最初の区間
intervals[0]をスタックに挿入する iを1から区間数-1まで繰り返し処理する- スタックの先頭要素を
last_elementとして取得する last_elementの終了位置がintervals[i]の開始位置以上である場合(=重なっている場合):last_elementの終了位置を、「intervals[i]の終了位置」と「last_elementの終了位置」の最大値に更新する- スタックから先頭要素を取り出し、更新した
last_elementを再びプッシュする
- 重なっていない場合は、
intervals[i]をそのままスタックにプッシュする
- スタックの先頭要素を
- 最後にスタックを結果として返す
それでは、実際の実装を見てみましょう。
実装例
class Solution(object): def merge(self, intervals): """ :type intervals: List[Interval] :rtype: List[Interval] """ if len(intervals) == 0: return [] self.quicksort(intervals,0,len(intervals)-1) stack = [] stack.append(intervals[0]) for i in range(1,len(intervals)): last_element= stack[len(stack)-1] if last_element[1] >= intervals[i][0]: last_element[1] = max(intervals[i][1],last_element[1]) stack.pop(len(stack)-1) stack.append(last_element) else: stack.append(intervals[i]) return stack def partition(self,array,start,end): pivot_index = start for i in range(start,end): if array[i][0]<=array[end][0]: array[i],array[pivot_index] =array[pivot_index],array[i] pivot_index+=1 array[end],array[pivot_index] =array[pivot_index],array[end] return pivot_index def quicksort(self,array,start,end): if start<end: partition_index = self.partition(array,start,end) self.quicksort(array,start,partition_index-1) self.quicksort(array, partition_index + 1, end) ob1 = Solution() print(ob1.merge([[1,3],[2,6],[8,10],[15,18]]))
入力
[[1,3],[2,6],[8,10],[15,18]]
出力
[[1, 6], [8, 10], [15, 18]]
補足:より簡潔な実装
上記の実装では自前のクイックソートを実装していますが、Pythonでは組み込みの sort() メソッドを使うことで、コードをより簡潔にできます。
def merge(intervals):
if not intervals:
return []
intervals.sort(key=lambda x: x[0]) # 開始位置でソート
merged = [intervals[0]]
for current in intervals[1:]:
last = merged[-1]
if current[0] <= last[1]: # 重なっている場合
last[1] = max(last[1], current[1])
else:
merged.append(current)
return merged計算量について
このアルゴリズムの計算量は以下の通りです。
- 時間計算量: O(n log n) — ソートが支配的なコストとなるため
- 空間計算量: O(n) — 結果を格納するためのリストが必要
区間のマージ問題は、カレンダーの予定管理や会議室の予約システムなど、実務でもよく登場する重要なアルゴリズムです。ぜひマスターしておきましょう。
-
Pythonで雨水をトラップするアルゴリズムを解説【スタックを使った実装】
問題の概要n個の非負整数からなる配列を考えます。この配列は、各バーの幅が1である「標高マップ」を表しており、雨が降ったあとにこの地形へ最大でどれだけの水を溜められるかを計算するのが目的です。いわゆる「Trapping Rain Water(雨水をトラップする)」として知られる有名なアルゴリズム問題です。イメージは以下のようになります。上の図では水たまり(青い部分)が6マスあるため、答えは6になります。スタックを使った解法の考え方この問題はスタックを利用すると効率的に解けます。各位置のインデックスをスタックで管理し、現在のバーがスタックの頂点にあるバーより高い場合には、その間に水が溜まっている可
-
Pythonでマージソートを実装する方法を徹底解説!サンプルコード付き
この記事では、マージソート(Merge Sort)のアルゴリズムを使って配列を並べ替えるPythonプログラムについて、実際のコード例を交えながら詳しく解説します。 問題設定 課題 − 与えられた配列を、マージソートの考え方を用いて昇順に並べ替えます。 マージソートは分割統治法に基づく整列アルゴリズムです。まず配列を半分ずつ再帰的に分割し、要素が1つになった時点でそれを「ソート済み」とみなします。その後、隣り合う部分配列同士を先頭から比較しながら統合(マージ)していくことで、最終的に配列全体が整列されます。 Pythonでの実装例 # マージ関数 def merge(arr, l, m,