剑指offer 专项突破版 7、数组中和为 0 的三个数

题目链接

  • 思路一
    BF 三重循环,每个循环对应一个加数。时间复杂度O(n³),空间复杂度O(1)

  • 思路二
    我们此前可以以O(n)的时间复杂度做出两数之和第6题,那么我们可以遍历数组,确定了一个数字k后,两数之和的target便为-k。但是我们的twoSum数组是排序好的,所以我们第一步需要先给数组排好序。此后需要注意的有两点:

    • 我们在做twoSum时找到值就结束了,但是这个题目有可能一个target有多组数都符合(ex:[ -4,-1,-1,0,1,2 ] 当i=-1时,-1,2和0,1都满足)所以在twoSum函数中,while循环的判断条件有所改变 while (left < right),与此同时循环体中还需要过滤掉相同的值,否则就会有重复(ex:[0,0,0,0,0] 当left=1,right=4时,如果不过滤,下一次left=2,right=3,依旧会有重复的答案产生)
      while (left < right && nums[left] == nums[++left]) ;
      注意最后的条件是nums[++left] 如果是nums[left++],会一直为真
    • 还有一个去重点体现在遍历数组中,同样是要过滤掉相同的值
      while (index < nums.length - 1 && nums[index++] == nums[index])

    复杂度分析

    • 时间复杂度:首先排序O(nlogn),然后外层遍历数组O(n),内层调用twoSum函数,复杂度为O(n),所以整体时间复杂度为O(n²)
    • 空间复杂度:没有什么额外空间,O(1)
    public List<List<Integer>> threeSum(int[] nums) {
          List<List<Integer>> result = new ArrayList<>();
    
          if (nums.length >= 3) {
              Arrays.sort(nums);
              int index = 0;
    
              while (index < nums.length - 2) {
    
                  List<List<Integer>> sum = twoSum(nums, index + 1, -nums[index]);
                  if (0 != sum.size()) {
                      result.addAll(sum);
                  }
                  while (index < nums.length && nums[index++] == nums[index]) ;
              }
          }
          return result;
      }
    
    private List<List<Integer>> twoSum(int[] nums, int left, int target) {
          int right = nums.length - 1;
          List<List<Integer>> result = new ArrayList<>();
    
          //遍历一遍 因为可能有多个值
          while (left < right) {
              if (nums[left] + nums[right] > target)
                  right--;
              else if (nums[left] + nums[right] < target)
                  left++;
                  //相等后存储起来 然后索引同时移动
              else {
                  result.add(Arrays.asList(-target, nums[left], nums[right]));
                  while (left < right && nums[left] == nums[++left]) ;
                  while (left < right && nums[right] == nums[--right]) ;
              }
          }
    
          return result;
      }
    

Go版本

func twoSum(nums []int, start int, target int) (result [][]int) {
	left, right := start, len(nums)-1

	for left < right {
		sum := nums[left] + nums[right]
		if sum == target {
			result = append(result, []int{-target, nums[left], nums[right]})
			// 去除重复元素
			for left < right && (nums[left] == nums[left+1] || nums[right] == nums[right-1]) {
				if nums[left] == nums[left+1] {
					left++
				}
				if nums[right] == nums[right-1] {
					right--
				}
			}
			left++
			right--
		} else if sum > target {
			right--
		} else {
			left++
		}
	}
	return result
}

func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	var result [][]int

	for i := 0; i < len(nums)-2; i++ {
		tmp := twoSum(nums, i+1, -nums[i])
		for _, sumArray := range tmp {
			result = append(result, sumArray)
		}

		for i < len(nums)-2 && nums[i] == nums[i+1] {
			i++
		}
	}

	return result
}

可以看作twoSum的强化版,需要注意的是有两次去重,一次是在threeSum里面,target需要去重,另一次是在twoSum里面,sum==target后需要对left和right’对应的元素去重

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值