LeetCode 15 - 3数之和 - 解题思路记录 - GoLang

435 阅读4分钟

我们先来看一下这个题目

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:

The solution set must not contain duplicate triplets.

Example:

Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

题目的在描述,要求出3数之和,并且这三个数字不能在一个数组中重复出现过。

比如例子中的 [-1, -1, 2 ] 出现过后,则不能再出现[-1, 2, -1]这样的顺序不同但数字一样的元素

好的,我们就从上面的例子开始讲解 [-1, 0, 1, 2, -1, -4]这6个数字是一个无须的数字,那么对于我们之间开始操作似乎有点难度, 但要是这些数字都是从小到大排好顺序的话,是不是更容易判断出数字的大小呢? 所以我们先对着6个数字先排序一次

好,我们排序好了以后,我们下一步该是什么呢?

我们想想题目的条件是什么?

  1. 三个数字之和
  2. 不能重复

也就是说,我们一次循环最少要获得三个数字,并且判断这3个数字的和是不是我们想要的值
说到这里,应该很多人都有一些思路了,那么我们现在上代码

func threeSum(nums []int) [][]int {
 ans := make([][]int, 0)
 for i := 0; i < len(nums)-2; i++ {
  if nums[i] > 0 {
   break
  }
  if i == 0 || nums[i] != nums[i-1] {
   start := i + 1
   end := len(nums) - 1
   for start < end {
    if nums[i]+nums[start]+nums[end] == 0 {
     ans = append(ans, []int{nums[i], nums[start], nums[end]})
     start += 1
     end -= 1
     for start < end && nums[start] == nums[start-1] {
      start += 1
     }
     for start < end && end+1 < len(nums) && nums[end] == nums[end+1] {
      end -= 1
     }
    } else if nums[i]+nums[start]+nums[end] < 0 {
     start += 1
    } else {
     //大于0的情况下
     end -= 1
    }

   }
  }
 }
 return ans
}

代码偏长,但是没关系,一点点来理解这些代码的目的是什么
就如我们刚刚所说,我们每一次的判断都必须要获取到3个值,也就是代码中的

start := i + 1
end := len(nums) - 1

这两行代码,那这两行代码的用意是什么? 我们来画个图

这下子,我们每个循环就有了3个数字了,分别是i, start, end这3个指针指向的地方
然后我们再看看, 这两句话上面还有一个 if的作用是什么

if i == 0 || nums[i] != nums[i-1] {
}

就是这个if的作用, 我们看到i==0 就说明是第一个嘛,只要nums[i]<0这个条件就肯定满足,否则在上一个if的时候就会被break掉
那么nums[i] != nums[i-1] 这一句什么时候满足?我们再来看一张图

所以,我们的nums[i] 一定不能跟nums[i-1]一致
那么接下来,我们再内层的循环

for start < end {
 if nums[i]+nums[start]+nums[end] == 0 {
  ans = append(ans, []int{nums[i], nums[start], nums[end]})
  start += 1
  end -= 1
  for start < end && nums[start] == nums[start-1] {
   start += 1
  }
  for start < end && end+1 < len(nums) && nums[end] == nums[end+1] {
   end -= 1
  }
 } else if nums[i]+nums[start]+nums[end] < 0 {
  start += 1
 } else {
  //大于0的情况下
  end -= 1
 }

}

循环的开始,我们就是 start<end 这个条件是必须的,否则 就不是最后的3个数字了 接下来到我们一个更核心的判断这个判断有个存在的条件

  1. 3个数字的和>0
  2. 3个数字的和<0
  3. 3个数字的和=0

我们先说case 3 , 3个数字等于0的情况下该如何处理

if nums[i]+nums[start]+nums[end] == 0 {
 ans = append(ans, []int{nums[i], nums[start], nums[end]})
 start += 1
 end -= 1
 for start < end && nums[start] == nums[start-1] {
  start += 1
 }
 for start < end && end+1 < len(nums) && nums[end] == nums[end+1] {
  end -= 1
 }
}

再来画个图 解释一下

可以看到我们红色位置的地方,如果满足条件,那么start向后移动,end向前移动,并且要保持start<end的条件
然后我们来看 start ++ 和end -- 后的两个for是要做什么用的?

for start < end && nums[start] == nums[start-1] {
 start += 1
}
for start < end && end+1 < len(nums) && nums[end] == nums[end+1] {
 end -= 1
}

我们刚才说过,题目的条件是什么? 不能重复, 而这2个for循环就是不重复的关键
前面的 start < end 大家都知道了就不再重复了
后面的nums[start] == nums[start-1] 这个条件是什么意思,再来画个图

start的例子跟end的例子是一样,只不过一个继续往前,一个继续往后,为了就是要保证,我们指向的值,不能跟之前那个值重复,否则就会将重复的值,放进数组中。
到这里,其实很多人都知道核心的步骤是什么了,其实就是这几步
然而刚才的<0跟>0的例子,无非就是start继续往后,跟end继续往前,然后再一次循环判断这3个数字是否一致。 好的,今天的题15就解说到这里,希望自己加油,坚持下去。