双指针经典题目,用二分查找解决将three sum转换成two sum的问题再解决。
题目
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
示例: 给定数组 nums = [-1 , 0 , 1 , 2 , -1 , -4 ], 满足要求的三元组集合为: [ [-1 , 0 , 1 ], [-1 , -1 , 2 ] ]
考察知识点
排序、双指针、数组
核心思想
方法一
先排序,后用双指针解决将three sum转换成two sum的问题。
先对数组进行排序,然后使用双指针进行处理,排序之后可以帮助排除一些情况。 算法流程 1、特判,对于数组长度 n
,如果数组为 null
或者数组长度小于 3,返回 [][]。 2、对数组进行排序。 3、遍历排序后数组:
- 若 nums[i]>0
:因为已经排序好,所以后面不可能有三个数加和等于 0,直接返回结果。
- 对于重复元素:跳过 ,避免出现重复解 - 令左指针L=i+1
,右指针 R=n-1
,当L<R
时,执行循环: - 当 nums[i]+nums[L]+nums[R]==0
,执行循环,判断左界和右界是否和下一位置重复 ,去除重复解。并同时将 L,R
移到下一位置,寻找新的解 - 因为整体左小右大 ,所以若和大于 0,说明 nums[R]
太大,R
左移, nums[R]
减小。 - 同上,若和小于 0,说明 nums[L]
太小,L
右移, nums[L]
增大。
做这个题目的关键,是要把握规律,有时候,提前排序,可以减少很多可能性,提升性能。
定义三个指针,i,j,k。遍历i,那么这个问题就可以转化为在i之后的数组中寻找nums[j]+nums[k]=-nums[i]
,这个问题,也就将三数之和问题转变为二数之和。(可以使用双指针)
方法二
先排序,然后用二分查找解决将three sum转换成two sum的问题
Python版本
先排序后双指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution : def threeSum (self, nums: list ) -> list: _len = len (nums) res=[] if (not nums or _len<3 ): return [] nums.sort() for i in range (_len): if (nums[i]>0 ): return res if (i>0 and nums[i]==nums[i-1 ]): continue L = i + 1 R = _len - 1 while (L<R): if (nums[i]+nums[L]+nums[R]==0 ): res.append([nums[i],nums[L],nums[R]]) while (L<R and nums[L]==nums[L+1 ]): L = L + 1 while (L<R and nums[R]==nums[R-1 ]): R = R - 1 L = L + 1 R = R - 1 elif (nums[i]+nums[L]+nums[R]>0 ): R=R-1 else : L=L+1 return res print("leet code accept!!!" ) Input = [[-1 , 0 , 1 , 2 , -1 , -4 ]] Answer = [ [ [-1 , 0 , 1 ], [-1 , -1 , 2 ] ] ]if __name__ == "__main__" : solution = Solution() for i in range (len (Input)): print("-" *50 ) result = solution.threeSum(Input[i]) print(result) print(Answer[i])
一个基于bisect实现二分查找的方法
前期的预处理很重要,构建了输入字符和出现次数的关系。减少不必要的计算。预处理和二分查找使得这个算法的运行速度在前98.18% 。但是,可读性太差了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import bisectclass Solution : def threeSum (self, nums: list ) -> list: ans = [] counts = {} for i in nums: counts[i] = counts.get(i, 0 ) + 1 nums = sorted (counts) for i, num in enumerate (nums): if counts[num] > 1 : if num == 0 and counts[num] > 2 : ans.append([0 , 0 , 0 ]) elif -num * 2 in counts: ans.append([num, num, -2 * num]) if num < 0 : two_sum = -num left = bisect.bisect_left(nums, (two_sum - nums[-1 ]), i + 1 ) for i in nums[left: bisect.bisect_right(nums, (two_sum // 2 ), left)]: j = two_sum - i if j in counts and j != i: ans.append([num, i, j]) return ans
执行用时 :432 ms, 在所有 Python3 提交中击败了98.18% 的用户
内存消耗 :33 MB, 在所有 Python3 提交中击败了5.01%的用户