最强面试手册:图解

问题15:三数之和(3 Sum)

给定一个包含n个整数的数组nums,判断nums中是否存在三个元素a,b,c,使得a+b+c=0,并找出全部知足条件且不重复的三元组。(注:不包含重复的三元组)。面试

例如,给定数组nums=[-1,0,1,2,-1,-4],则知足要求的三元组集合为:
[ [-1, 0, 1], [-1, -1, 2] ]算法

思路分析:

首先对数组进行排序,用于定位基准点。假设以排序后首个固定元素nums[i](i=0)为起始点,再以左指针指向nums[i+1](简略记为nums[l]),右指针指向nums[len-1](简略记为nums[r]),即指向nums[i]右侧元素的两端,计算三数之和是否知足条件为0。如不知足且和小于0,则L++;反之则R--,有序移动两端指针,当L与R碰撞时(L>=R)时本轮遍历结束,右移起始点(i++);如三数之和为0,知足条件则记录并继续移动两端指针。
最强面试手册:图解
图1:引用自知乎的图解数组

因为按小到大排序,所以若是做为起始点的nums[i]>0,则三数之和必然没法等于0,此时可结束循环。若是nums[i]==nums[i−1],则说明该元素重复,根据题意须要跳过,避免输出重复的结果。ide

此外,当三数之和为0时,假设nums[L]==nums[L+1]会致使结果重复,须要跳过该元素(L++);若是nums[R]==nums[R−1]一样会致使结果重复,也须要跳过该元素(R--)。整体来讲,时间复杂度为:O(n2)。测试

代码实现:

public static void sum3(int[] nums) {
  if(nums != null && nums.length > 2) {
   // 初始化并对数组排序
   Arrays.sort(nums);  
   //定位起始点元素右侧的两端指针
   int l = 0, r = 0;
   for (int i = 0; i < nums.length; i++) {
     // 若是起始点元素大于0,因为已排序,
     // 其与右侧两端元素之和必然大于0
      if (nums[i] > 0) break;
     // 若是起始点右移,当前元素与以前相同
     // 则须要跳过避免出现重复结果
     if (i > 0 && nums[i] == nums[i - 1]) continue;
     // 定义起始点元素右侧的两端指针
     // 分别指向其右侧元素数组的左右两端
     l = i + 1;
     r = nums.length - 1;

     while (l < r)
       if (nums[i] + nums[l] + nums[r] == 0) {
         // 当三数之和为0,则标记,并有序移动两端指针
         // 与此同时跳太重复元素
         while (l < r && nums[l] == nums[l + 1]) l++;
         while (l < r && nums[r] == nums[r - 1]) r--;
         l++;
         r--;
       }
         // 如三数之和小于0,则左指针右移,增大三数之和
       else if (nums[i] + nums[l] + nums[r] < 0) l++;
         //如三数之和大于于0,则右指针左移,减少三数之和
       else if (nums[i] + nums[l] + nums[r] > 0) r--;
   }
  }
}

以上仅为参考,解题思路万千并不是惟一,请结合具体案例编写测试用例验证正确性。同时也请进一步思考是否有更优的算法。指针

参考资料:
https://leetcode-cn.com/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/code

相关文章
相关标签/搜索