数组和链表代表着计算机最基本的两种存储形式:顺序存储和链式存储,所以他俩可以算是最基本的数据结构。数组是一种基础数据结构,可以用来处理常见的排序和二分搜索问题,典型的处理技巧包括双指针、滑动窗口等,数组是数据结构中的基本模块之一。因为字符串是由字符数组形成的,所以二者是相似的。
双指针⼜分为中间向两端扩散的双指针、两端向中间收缩的双指针、快慢指针。
双指针是一种思想,一种技巧或一种方法,并不是什么特别具体的算法,在二分查找等算法中经常用到这个技巧。具体就是用两个变量动态存储两个或多个结点,来方便我们进行一些操作。通常用在线性的数据结构中,比如链表和数组,有时候也会用在图算法中。
在我们遇到像数组,链表这类数据结构的算法题目的时候,应该要想得到双指针的套路来解决问题。特别是链表类的题目,经常需要用到两个或多个指针配合来记忆链表上的节点,完成某些操作。链表这种数据结构也是树形结构和图的原型,所以有时候在关于图和树形结构的算法题目中也会用到双指针。
双指针顾名思义,就是同时使用两个指针,在序列、链表结构上指向的是位置,在树、图结构中指向的是节点,通过或同向移动,或相向移动来维护、统计信息。在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(碰撞指针或者叫左右指针)的指针进行扫描,从而达到相应的目的。
换言之,双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。
快慢指针也是双指针,但是两个指针从同一侧开始遍历数组,将这两个指针分别定义为快指针(fast)和 慢指针(slow),两个指针以不同的策略移动,直到两个指针的值相等(或其他特殊条件)为止,如 fast 每次增长两个,slow 每次增长一个。
利用快慢指针可以用来解决某些算法问题,比如:
快慢指针在链表的详细使用,可以阅读下一篇文章——Python数据结构与算法篇(六)-- 链表的应用
对撞指针(或者称作左右指针)是指在数组中,将指向最左侧的索引定义为左指针(left),最右侧的定义为右指针(right),然后从两头向中间进行数组遍历。一般都是排好序的数组或链表,否则无序的话这两个指针的位置也没有什么意义。特别注意两个指针的循环条件在循环体中的变化,小心右指针跑到左指针左边去了。常用来解决的问题有:
题库列表:
26. 删除有序数组中的重复项 (快慢指针)
27. 移除元素 (快慢指针)
80. 删除有序数组中的重复项 II (快慢指针)
283. 移动零(快慢指针)
26. 删除有序数组中的重复项
题目描述:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != nums[slow]:
slow += 1 # 每个元素只出现一次,先 slow+1 再覆盖
nums[slow] = nums[fast]
fast += 1
return slow + 1
27. 移除元素
题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O ( 1 ) O(1) O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
1. 快慢指针
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != val: # 删除指定元素,直接覆盖掉
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
这里和有序数组去重的解法有一个细节差异,我们这里是先给 nums[slow]
赋值然后再给 slow++,这样可以保证 nums[0…slow-1] 是不包含值为 val 的元素的,最后的结果数组长度就是 slow。
2. 单指针
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
'''拷贝覆盖'''
ans = 0
for num in nums:
if num!= val:
nums[ans] = num
ans += 1
return ans
80. 删除有序数组中的重复项 II
题目描述:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O ( 1 ) O(1) O(1) 额外空间的条件下完成。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow, fast = 1, 2 # 使得出现次数超过两次的元素只出现两次
while fast < len(nums):
if nums[fast] != nums[slow-1]:
slow += 1
nums[slow] = nums[fast]
fast += 1
return slow + 1
通用解法:
为了让解法更具有一般性,我们将原问题的 「最多保留 1 位」修改为「最多保留 k 位。
对于此类问题,我们应该进行如下考虑:
由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留。
对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留。
此时,初始化时指针 slow 指向数组的起始位置(nums[k-1]),指针 fast 指向指针 slow 的后一个位置(nums[k])。随着指针 fast 不断向后移动,将指针 fast 指向的元素与指 slow 指向的元素进行比较:
283. 移动零
题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
1. 快慢指针
写法一:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != 0: # 把前面为0的项覆盖掉
nums[slow] = nums[fast]
slow += 1
fast += 1
nums[slow:] = [0] * (len(nums)-slow) # 把后面填充为 0
return nums
写法二:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
slow, fast = 0, 0
while fast < len(nums):
if nums[fast]:
if nums[slow] == 0: # 快指针不为零,慢指针为零,进行交换
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
fast += 1
return nums
题库列表:
88. 合并两个有序数组:如何将数组所有元素整体后移,防止数组覆盖?
167. 两数之和 II - 输入有序数组(有序数列的首尾双指针)
125. 验证回文串
344. 反转字符串
151. 反转字符串中的单词
345. 反转字符串中的元音字母
11. 盛最多水的容器:经典题目
42. 接雨水:经典题目
75. 颜色分类(左右指针,三色旗)
844. 比较含退格的字符串(左右指针)
88. 合并两个有序数组
题目描述:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
1. 左右指针
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
nums1[m:] = nums2 # 直接合并后排序
nums1.sort()
"""
p0, p1, p2 = m-1, n-1, m+n-1
while p0 >= 0 or p1 >= 0:
if p0 == -1: # num1 已经循环结束
nums1[p2] = nums2[p1]
p1 -= 1
elif p1 == -1: # # num2 已经循环结束
nums1[p2] = nums1[p0]
p0 -= 1
elif nums1[p0] > nums2[p1]:
nums1[p2] = nums1[p0]
p0 -= 1
else:
nums1[p2] = nums2[p1]
p1 -= 1
p2 -= 1
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
p0, p1, p2 = m-1, n-1, m+n-1
while p0 >= 0 and p1 >= 0:
if nums1[p0] > nums2[p1]: # num1 更大
nums1[p2] = nums1[p0]
p0 -= 1
else:
nums1[p2] = nums2[p1]
p1 -= 1
p2 -= 1
# 最后拼接没有遍历完的数组,由于直接在 nums1 上操作,只需要在nums2没遍历完,拼接起来就可以了
if p1 >= 0:
nums1[:p2+1] = nums2 [:p1+1]
167. 两数之和 II - 输入有序数组
题目描述:给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left, right = 0, len(numbers)-1
while left < right:
two_sum = numbers[left] + numbers[right]
if two_sum == target:
return [left+1, right+1] # 题目要求下标从1开始
elif two_sum > target: # 两数之和大于目标值,右边的值太大了,right--
right -= 1
else: # 两数之和小于目标值,左边值太小,left++
left += 1
125. 验证回文串
题目描述:如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串。字母和数字都属于字母数字字符。
import re
class Solution:
def isPalindrome(self, s: str) -> bool:
'''
# 思路一:正则表达式
if not s:
return True
s = s.lower()
pattern = re.compile(r'[^a-z0-9]') # 正则表达式,把数字和字母都剔除掉
new_str = pattern.sub('', s)
return new_str == new_str[::-1]
# 字符串预处理
new_str = ''.join(ch.lower() for ch in s if ch.isalnum())
return new_str == new_str[::-1]
'''
# 左右指针
s = s.lower()
left, right = 0, len(s) - 1
while left < right:
# 两个循环找到左侧和右侧为字母或者数字的位置
while left < len(s) - 1 and not s[left].isalnum():
left += 1
while right > 0 and not s[right].isalnum():
right -= 1
if left >= right: # 判断移动过后的left,right是否满足left在左,right在右的相对位置
break
else:
if s[left] != s[right]: # 如果左右指针所指不同,则肯定不构成回文
return False
else: # 左右指针各前进一步
left += 1
right -= 1
return True
这里使用了正则表达式移除所有非字母数字字符,然后判断新的字符串是否是回文,也可以使用双指针,直接一次遍历,遇到字母数字字符就进行判断。
151. 反转字符串中的单词
题目描述:给你一个字符串 s,请你反转字符串中 单词 的顺序。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
1. 双指针
class Solution:
def reverseWords(self, s: str) -> str:
s = s.strip() # 删除首尾空格
i = j = len(s) - 1
res = []
while i >= 0:
while i >= 0 and s[i] != ' ': i -= 1 # 搜索首个空格
res.append(s[i + 1: j + 1]) # 添加单词
while s[i] == ' ': i -= 1 # 跳过单词间空格
j = i # j 指向下个单词的尾字符
return ' '.join(res) # 拼接并返回
2. 分割 + 倒序
class Solution:
def reverseWords(self, s: str) -> str:
return ' '.join(s.strip().split()[::-1])
344. 反转字符串
题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O ( 1 ) O(1) O(1) 的额外空间解决这一问题。
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
return s.reverse()
s[:] = s[::-1]
"""
# 左右指针法
left, right = 0, len(s)-1
while left <= right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
return s
345. 反转字符串中的元音字母
题目描述:给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’,且可能以大小写两种形式出现不止一次。
class Solution:
def reverseVowels(self, s: str) -> str:
str_set = set("aeiouAEIOU")
head, tail = 0, len(s) - 1
str_list = list(s)
while head < tail:
if str_list[head] in str_set and str_list[tail] in str_set:
str_list[head], str_list[tail] = str_list[tail], str_list[head]
head += 1
tail -= 1
elif str_list[head] in str_set and str_list[tail] not in str_set:
tail -= 1
elif str_list[head] not in str_set and str_list[tail] in str_set:
head += 1
else:
head += 1
tail -= 1
return ''.join(str_list)
11. 盛最多水的容器
题目描述:给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
class Solution:
def maxArea(self, height: List[int]) -> int:
# 双指针,移动小的那一边
head, tail = 0, len(height)-1
res = 0
while head < tail:
if height[head] < height[tail]:
res = max(res, height[head]*(tail-head))
head += 1
else:
res = max(res, height[tail]*(tail-head))
tail -= 1
return res
42. 接雨水
题目描述:
class Solution:
def trap(self, height: List[int]) -> int:
ans = 0
left, right = 0, len(height) - 1
leftMax = rightMax = 0
while left < right:
leftMax = max(leftMax, height[left])
rightMax = max(rightMax, height[right])
if height[left] < height[right]:
ans += leftMax - height[left]
left += 1
else:
ans += rightMax - height[right]
right -= 1
return ans
75. 颜色分类
题目描述:给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
1. 单指针
class Solution:
def sortColors(self, nums: List[int]) -> None:
i = 0
length = len(nums)
for j in range(length):
if nums[j] == 0:
nums[i], nums[j] = nums[j], nums[i]
i += 1
for k in range(i, length):
if nums[k] == 1:
nums[k], nums[i] = nums[i], nums[k]
i += 1
2. 左右指针
class Solution:
def sortColors(self, nums: List[int]) -> None:
# 定义三个变量,p0 表示数组最左边0的区域,p1是数组最右边2的区域
i, p0, p1 = 0, 0 , len(nums)-1
while i <= p1:
# 如果当前指向的是 0,就把这个元素交换到数组左边
# 也就是跟 p0 指针交换,之后cur,p0 就往前一动一位
if nums[i] == 0:
nums[i], nums[p0] = nums[p0], nums[i]
p0 += 1
i += 1
# 如果当前指向的是2,就把这个元素交换到数组右边
# 也就是跟p2指针交换,注意此时cur指针就不用移动了
# 因为右边的一片区域都是2,只要把元素交换过去就可以了,cur不用移动
elif nums[i] == 2:
nums[i], nums[p1] = nums[p1], nums[i]
p1 -= 1
# 如果是1的话,就不用交换
else:
i += 1
844. 比较含退格的字符串
题目描述:给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。如果对空文本输入退格字符,文本继续为空。
1. 双指针
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
s_skip_num = 0 # 记录s的#数量
t_skip_num = 0 # 记录t的#数量
i = len(s) - 1
j = len(t) - 1
while True:
while (i >= 0): # 从后向前,消除S的#
if (s[i] == '#'):
s_skip_num += 1
else:
if (s_skip_num > 0):
s_skip_num -= 1
else:
break;
i -= 1
while (j >= 0): # 从后向前,消除T的#
if (t[j] == '#'):
t_skip_num += 1
else:
if (t_skip_num > 0):
t_skip_num -= 1
else:
break
j -= 1
# 后半部分
# 消除完了,接下来比较s[i] != t[j]
if (i < 0 or j < 0):
break # s 或者 t 遍历到头了
if (s[i] != t[j]):
return False
i -= 1
j -= 1
# 说明S和T同时遍历完毕
if (i == -1 and j == -1):
return True
return False
2. 栈模拟法
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
return self.back_strip(s) == self.back_strip(t)
def back_strip(self, s):
s_list = []
for item in s:
if item == '#':
if s_list:
s_list.pop()
else:
s_list.append(item)
return ''.join(s_list)
3. 移除元素
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
list_s, list_t = list(s), list(t)
idx_s, idx_t = 0, 0
for i in range(len(list_s)):
if list_s[i] != '#':
list_s[idx_s] = list_s[i]
idx_s += 1
else:
idx_s -= 1 if idx_s > 0 else 0
for i in range(len(list_t)):
if list_t[i] != '#':
list_t[idx_t] = list_t[i]
idx_t += 1
else:
idx_t -= 1 if idx_t > 0 else 0
return list_s[:idx_s] == list_t[:idx_t]
977. 有序数组的平方
题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
"""先平方后排序,每次利用已排序的先验信息,最大值必定在两端出现"""
length = len(nums)
left, right = 0, length-1
result = []
while left <= right:
if nums[left] ** 2 < nums[right] ** 2:
result.append(nums[right] ** 2)
right -= 1
else:
result.append(nums[left] ** 2)
left += 1
return result[::-1]
题库列表:
5. 最长回文子串
15. 三数之和
16. 最接近的三数之和
18. 四数之和
56. 合并区间:数组类操作
5. 最长回文子串
题目描述:给你一个字符串 s,找到 s 中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
思路分析:找回⽂串的难点在于,回⽂串的的⻓度可能是奇数也可能是偶数,解决该问题的核⼼是从中⼼向两端扩散的双指针技巧。如果回⽂串的⻓度为奇数,则它有⼀个中⼼字符;如果回⽂串的⻓度为偶数,则可以认为它有两个中⼼字符。
class Solution:
"""找到以i和i+1为中心的回文字符串"""
def longestPalindrome(self, s: str) -> str:
res = ""
for i in range(len(s)):
s1 = self.palindrome(s, i, i)
s2 = self.palindrome(s, i, i+1)
res = s1 if len(s1) > len(res) else res
res = s2 if len(s2) > len(res) else res
return res
def palindrome(self, s, l, r):
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1
r += 1
return s[l+1:r]
15. 三数之和
题目描述:给你一个整数数组 nums ,判断是否存在三元组 [ n u m s [ i ] , n u m s [ j ] , n u m s [ k ] ] [nums[i], nums[j], nums[k]] [nums[i],nums[j],nums[k]] 满足 i ! = j 、 i ! = k i != j、i != k i!=j、i!=k 且 j ! = k j != k j!=k ,同时还满足 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] = = 0 nums[i] + nums[j] + nums[k] == 0 nums[i]+nums[j]+nums[k]==0。请你返回所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组。
from collections import defaultdict
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums_sorted = sorted(nums)
result = []
for i in range(len(nums_sorted)):
if nums_sorted[i] > 0: # 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
return result
'''
# 错误去重a方法,将会漏掉-1,-1,2 这种情况
if (nums[i] == nums[i + 1]) {
continue;
}
'''
if i>0 and nums_sorted[i] == nums_sorted[i-1]: # 去重
continue
left = i+1
right = len(nums_sorted)-1
while left < right:
'''
# 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0, 0, 0 这种三元组
while left < right and nums_sorted[left] == nums_sorted[left+1]: left += 1
while left < right and nums_sorted[right] == nums_sorted[right-1]: right -= 1
'''
if nums_sorted[i]+nums_sorted[left]+nums_sorted[right] == 0:
result.append([nums_sorted[i], nums_sorted[left], nums_sorted[right]])
# 在要增加 left,减小 right,但是不能重复,比如: [-2, -1, -1, -1, 3, 3, 3],
# i = 0, left = 1, right = 6, [-2, -1, 3] 的答案加入后,需要排除重复的 -1 和 3
while left < right and nums_sorted[left] == nums_sorted[left+1]:
left += 1
while left < right and nums_sorted[right] == nums_sorted[right-1]:
right -= 1
# 找到答案时双指针同时收缩
left += 1
right -= 1
elif nums_sorted[i]+nums_sorted[left]+nums_sorted[right] > 0:
right -= 1
else:
left += 1
return result
16. 最接近的三数之和
题目描述:给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在恰好一个解。
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums_sorted = sorted(nums)
diff = float('inf') # 初始化,因为找最小值,因此把初始值设置成实数的最大值
length = len(nums) # 排序是前提
for i in range(length):
if i > 0 and nums_sorted[i] == nums_sorted[i-1]: # 常见的剪枝操作
continue
left = i+1 # 双指针:指针对撞
right = length-1
while left < right:
temp = nums_sorted[left] + nums_sorted[right] + nums_sorted[i]
if abs(temp-target) < diff:
diff = abs(temp-target)
ans = temp
# 不管是变小还是变大,尝试的作用是让 temp 与 target 更接近,即 temp 与 target 的绝对值之差越来越小
if temp > target: # 如果大了,尝试右边界收缩一格,让 temp 变小
right -= 1
elif temp < target: # 如果小了,尝试左边界收缩一格,让 target 变大
left += 1
else: # 如果已经等于 target 的话, 肯定是最接近的,根据题目要求,返回这三个数的和
return target
return ans
18. 四数之和
题目描述:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
你可以按 任意顺序 返回答案 。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums_sorted = sorted(nums)
result = []
length = len(nums)
for i in range(length):
# 剪枝处理
if nums_sorted[i] > target and nums_sorted[i] >= 0:
break
# 对 nums_sorted[i] 去重
if i > 0 and nums_sorted[i] == nums_sorted[i-1]:
continue
for j in range(i+1, length): # 比原来多一层循环
# 二级剪枝处理
if nums_sorted[i] + nums_sorted[j] > target and nums_sorted[i] + nums_sorted[j] >= 0:
break
# 对 nums_sorted[j] 去重
if j > i+1 and nums_sorted[j] == nums_sorted[j-1]:
continue
left = j + 1
right = length - 1
while left < right:
if nums_sorted[i] + nums_sorted[j] + nums_sorted[left] + nums_sorted[right] == target:
result.append([nums_sorted[i], nums_sorted[j], nums_sorted[left], nums_sorted[right]])
while left < right and nums_sorted[left] == nums_sorted[left+1]:
left += 1
while left < right and nums_sorted[right] == nums_sorted[right-1]:
right -= 1
left += 1
right -= 1
elif nums_sorted[i] + nums_sorted[j] + nums_sorted[left] + nums_sorted[right] > target:
right -= 1
else:
left += 1
return result
56. 合并区间
题目描述:以数组 intervals 表示若干个区间的集合,其中单个区间为 i n t e r v a l s [ i ] = [ s t a r t i , e n d i ] intervals[i] = [starti, endi] intervals[i]=[starti,endi]。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
result= []
intervals.sort(key=lambda x:x[0])
for interval in intervals:
# 如果列表为空,或者当前区间与上一区间不重合,直接添加
if not result or result[-1][1] < interval[0]:
result.append(interval)
else:
# 否则的话,我们就可以与上一区间进行合并
result[-1][1] = max(result[-1][1], interval[1])
return result
左右指针与快慢指针暂时告一段落,但还有很多自己不满意的地方,后面在学习中持续补充,谢谢大家的鼓励和支持!