本人一直在努力地积累Leetcode上用Python实现的题,并且会尽力讲清每道题的原理,绝不像其他某些博客简略地带过。
如果觉得讲的清楚,欢迎关注。
给定一个包含 n 个整数的数组 nums
和一个目标值 target
,判断 nums
中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target
相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
思路:很明显。四数之和与三数之和很像,但当我用四指针夹逼来做时发现超时了,但却有大神写的能通过。这说明就算是相同的算法,代码的冗杂程度也会很大程度上影响性能。所以说夹逼遍历,最重要的就是过滤!过滤!过滤!怎样把一定不符合条件的值跳过才是最能提升性能的。
但是先来看看我提交的代码:
1.字典查找法
总思路:把N4拆成2个N2。第一个for循环,先求后2个值可能的取值的所有情况,并且把它们储存在一个字典里,以和作为键。
第二个for,我们遍历前2个值所可能取的各种值,算出和并且检查target - onesum是否在我们的字典里,如果在,就说明我们找到了一个解。其实这种求几数之和的问题,都可以通过这种思路。比如说三数之和,现在我就想到根本不必用指针,算出所有后2个值所加的可能的和,然后用字典存,最后用target-第一个值去检查是否存在这样的键。
class Solution: def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ nums.sort() ans = set() sumans = {} if len(nums) < 4: return [] for i in range(2, len(nums) - 1): for j in range(i+1, len(nums)): onesum = nums[i] + nums[j] if onesum not in sumans: sumans[onesum] = [(i, j)] else: sumans[onesum].append((i, j)) for i in range(len(nums) - 3): for j in range(i+1, len(nums) - 2): onesum = nums[i] + nums[j] if target - onesum in sumans: for k in sumans[target - onesum]: if k[0] > j: ans.add((nums[i], nums[j], nums[k[0]], nums[k[1]])) return [i for i in ans]
虽然说我们写出夹逼的方法,但确实有人写出了而且比我快,我们主要分析一下他们是怎么设置过滤条件的。
2.四指针夹逼法
class Solution: def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ if not nums: return [] _4_sum_list = [] nums.sort() if nums[-1]*4 < target: return [] for i in range(len(nums)-3): if nums[i]*4 > target: break if i==0 or nums[i]!= nums[i-1]: ele = nums[i] target_3_sum = target - ele if nums[-1]*3 < target_3_sum: continue for j in range(i+1,len(nums)-2): ele2 = nums[j] if ele2*3 > target_3_sum: break if j==i+1 or ele2!= nums[j-1]: target_2_sum = target_3_sum - ele2 point_left = j+1 point_right = len(nums)-1 while point_left < point_right: if nums[point_left] + nums[point_right] > target_2_sum: point_right -= 1 elif nums[point_left] + nums[point_right] < target_2_sum: point_left += 1 else: aaa = [ele, ele2,nums[point_left], nums[point_right]] _4_sum_list.append(aaa) point_left += 1 point_right -= 1 while point_left < point_right and nums[point_left] == nums[point_left-1]: point_left += 1 while point_left < point_right and nums[point_right] == nums[point_right+1]: point_right -= 1 return _4_sum_list
首先思路很清晰,i,j,point_left,point_right四个指针。
注意 因为经过了排序,i到point_right递增。
1.如果i也就是第一个指针的四倍大于等于target, break
2.如果最大一项的三倍小于还需要填充的和,则进入下个更大的i。
3.同理它这里的if保证了不重复计算相同的i.
4。剩下来的思路与三数之和大致相同。具体可参考我的另一篇博客。https://mp.csdn.net/postedit/80647482
反思总结:1.用字典查找法。先遍历求出后几个值的可能取值,并用字典去存储,最后去搜索target - nums[i]-nums[j]。
2.夹逼法。定4个指针,后2个指针用来夹逼。最重要的是要会过滤条件,否则它就是暴力法。