做算法题,可能与我们平时编写业务逻辑的思维方式可能还不太一样。作算法题需要理解其中的原理,构思分析问题的方法,勤于总结,只有这样才能逐渐养成算法思维,而不是只有纯写业务逻辑的思维。
今天透过一道腾讯的笔试题:合并区间,体会算法思维对程序员的重要性。
合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 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 已刷题目总结: