做算法題,可能與我們平時編寫業務邏輯的思維方式可能還不太一樣。作算法題需要理解其中的原理,構思分析問題的方法,勤於總結,只有這樣才能逐漸養成算法思維,而不是隻有純寫業務邏輯的思維。
今天透過一道騰訊的筆試題:合併區間,體會算法思維對程序員的重要性。
合併區間
給出一個區間的集合,請合併所有重疊的區間。
示例 1:
輸入: [[1,3],[2,6],[8,10],[15,18]]
輸出: [[1,6],[8,10],[15,18]]
解釋: 區間 [1,3] 和 [2,6] 重疊, 將它們合併爲 [1,6].
示例 2:
輸入: [[1,4],[4,5]]
輸出: [[1,5]]
解釋: 區間 [1,4] 和 [4,5] 可被視爲重疊區間。
補全下面代碼:
class Solution(object):
def merge(self, intervals):
說明,這道題看似簡單,實際需要考慮周全。比如出現這樣的區間:
輸入爲 []
[[1,4],[2,4]]
邊界情況 [[1,3],[3,4]]
複雜情況 [[1,2],[3,4],[5,6],[0,10],[7,12]]
同時要求時間複雜度儘可能低。
2 分析
S1:首先考慮構成本題的基本結構:如何判斷兩個線段是否有交集:
兩個線段有交集的四種情況:
統一滿足以上不等式條件:
兩個線段無交集的情況:
統一滿足以上不等式條件:
S2:合併一堆線段
如下一堆線段,圖中標號爲線段的編號:
爲了使得操作更加有序,第一感覺按照線段的左端點從小到大排序:
這樣確保第個線段左端點不大於第個線段左端點。
S3:設置數組 dp
設數組dp[i]
表示合併第i
條線段後的解,即此時數組dp
內的各條線段都已經不能再合併。
下面就變爲:
已知dp[i]
,如何合併第i+1
條線段,進而推導出 dp[i+1]
。
若數組dp
中最後一個元素的左端點不小於第i+1
條線段的右端點,則可與之合併,即滿足:
否則無法合併,直接加入到dp
中。
轉化爲代碼:
if item[0] <= dp[-1][1]:
dp[-1][1] = max(dp[-1][1],item[1]) # 合併第i+1段
else:
dp.append(item) # 無法合併,直接加入到dp中
dp
數組的初始值爲排序後的第一條線段。
S4:轉化代碼
class Solution(object):
def merge(self, intervals):
if len(intervals)==0: return intervals
a = sorted(intervals,key=lambda x: (x[0],x[1]))
dp = [a[0]]
for item in a[1:]:
if item[0] <= dp[-1][1]:
dp[-1][1] = max(dp[-1][1],item[1])
else:
dp.append(item)
return dp
一般排序時間複雜度爲 O(nlogn),後面 for 循環時間複雜度爲 O(n),所以綜合時間複雜度爲 O(nlogn)。
本題目先試用排序降低一部分分析難度,後面使用動態規劃思想,問題變化爲:已知 dp[i] 如何求出 dp[i+1]問題,並且經過排序後是否合併只與dp[-1]有關。
歡迎加入星球,從零學程序員必備算法,每天在星球內記錄學習過程、學習星友超讚的回答,還會不定期送精華資料!打卡 300 天,退還除平臺收取的其他所有費用。
長按二維碼,查看我的星球
Day1-Day35 已刷題目總結: