2021/5/23 刷题笔记三数之和与双指针法
Posted 黑黑白白君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021/5/23 刷题笔记三数之和与双指针法相关的知识,希望对你有一定的参考价值。
三数之和
【题目】
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0?请你找出所有和为 0 且不重复的三元组。
- 注意:答案中不可以包含重复的三元组。
示例 1:
- 输入:nums = [-1,0,1,2,-1,-4]
- 输出:[[-1,-1,2],[-1,0,1]]
示例 2:
- 输入:nums = []
- 输出:[]
示例 3:
- 输入:nums = [0]
- 输出:[]
提示:
- 0 <= nums.length <= 3000
- -105 <= nums[i] <= 105
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
【我的方法】(超时。。。)
1、给数组排序。
2、使用回溯法,注意三元组不能重复,所以在添加到res前还需要判断是否已存在。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res=[]
if len(nums)<3:
return []
def backtrack(sub,temp,tol):
if len(temp)==3:
if tol==0:
temp.sort()
if temp not in res:
res.append(temp)
return
elif len(temp)<3:
n=len(sub)
for i in range(n):
if tol+sub[i]<=0:
backtrack(sub[i+1:],temp+[sub[i]],tol+sub[i])
else:
return
nums.sort()
backtrack(nums,[],0)
return res
【其他方法】
- 排序+双指针
1、不重复:
- 实际上就是枚举的三元组 (a, b, c)满足 a≤b≤c,保证了只有 (a, b, c)这个顺序会被枚举到,而 (b, a, c)、(c, b, a)等等不会,这样就减少了重复。
- 可以通过将数组中的元素从小到大进行排序,然后使用普通的三重循环。
- 同时对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。
- 此时时间复杂度为O(N^3)
2、循环优化:
- 如果固定了前两重循环枚举到的元素 a 和 b,那么只有唯一的 c 满足 a+b+c=0。所以可以从小到大枚举 b,同时从大到小枚举 c,即第二重循环和第三重循环实际上是并列的关系。
- 使用双指针法,即左指针向右移动一个位置,而右指针会向左移动若干个位置。
- 此时时间复杂度降为O(N^2)
借鉴该思路写出的代码:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums)<3:
return []
res=[]
nums.sort()
i=0
n=len(nums)
while(i<n-2):
j=i+1
k=n-1
while(j<k):
if (nums[i]+nums[j]+nums[k])==0:
res.append([nums[i],nums[j],nums[k]])
now=nums[j]
j+=1
k-=1
while(nums[j]==now and j<k): # 避免枚举和之前一样的数
j+=1
elif (nums[i]+nums[j]+nums[k])<0:
j+=1
else:
k-=1
now=nums[i]
i+=1
while(nums[i]==now and i<n-2): # 避免枚举和之前一样的数
i+=1
return res
# 执行用时:1544 ms, 在所有 Python3 提交中击败了17.88%的用户
# 内存消耗:17.3 MB, 在所有 Python3 提交中击败了93.48%的用户
参考代码:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
ans = list()
# 枚举 a
for first in range(n):
# 需要和上一次枚举的数不相同
if first > 0 and nums[first] == nums[first - 1]:
continue
# c 对应的指针初始指向数组的最右端
third = n - 1
target = -nums[first]
# 枚举 b
for second in range(first + 1, n):
# 需要和上一次枚举的数不相同
if second > first + 1 and nums[second] == nums[second - 1]:
continue
# 需要保证 b 的指针在 c 的指针的左侧
while second < third and nums[second] + nums[third] > target:
third -= 1
# 如果指针重合,随着 b 后续的增加
# 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if second == third:
break
if nums[second] + nums[third] == target:
ans.append([nums[first], nums[second], nums[third]])
return ans
# 执行用时:844 ms, 在所有 Python3 提交中击败了58.27%的用户
# 内存消耗:17.5 MB, 在所有 Python3 提交中击败了70.77%的用户
双指针
1、什么是双指针(对撞指针、快慢指针)
双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
-
什么时候优先想到用双指针来解决问题?
- 当遇到有序数组时:双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。例如:
- 给定一个有序递增数组,在数组中找到满足条件的两个数,使得这两个数的和为某一给定的值。对于这种问题,常见的算法思路不外乎遍历,回溯,但这里,双指针遍历法是一个很有效的方法。
- hoare的双向扫描快速划分法。
- 求单链表的中间元素。
- 当遇到有序数组时:双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。例如:
2、对撞指针用法
对撞指针是指在有序数组中,将指向最左侧的索引定义为左指针(left),最右侧的定义为右指针(right),然后从两头向中间进行数组遍历。
3、快慢指针用法
两个指针从同一侧开始遍历数组,将这两个指针分别定义为快指针(fast)和慢指针(slow),两个指针以不同的策略移动,直到两个指针的值相等(或其他特殊条件)为止,如fast每次增长两个,slow每次增长一个。
【部分内容参考自】
- 三数之和:https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/
- 算法一招鲜——双指针问题:https://zhuanlan.zhihu.com/p/71643340
- 【算法总结–数组相关】双指针法的常见应用。:https://blog.csdn.net/ohmygirl/article/details/7850068
以上是关于2021/5/23 刷题笔记三数之和与双指针法的主要内容,如果未能解决你的问题,请参考以下文章