三數之和

pardon110發表於2021-01-10

雜湊與雙指標兩種思路實現,關鍵在去重

題面

n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有和為 0 且不重複的三元組。

雜湊

  • 充分複用排序,雜湊計數資訊
  • 三數之和,三數要麼不同,要麼有相同
  • 計數區分可否相同,雜湊裁定第三數是否存在,排序縮小其範圍
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    length := len(nums)
    if length < 3 || nums[length-1] < 0 || nums[0] > 0 {
        return nil
    }
    ans := [][]int{}
    hash := make(map[int]int)
    for _, v := range nums {
        hash[v]++
    }

    for i:=0;i<length;i += hash[nums[i]] {
        for l:= i+hash[nums[i]];l<length && nums[l] < -nums[i]-nums[l];l +=hash[nums[l]]{ // 三數不同
            if _, ok := hash[-nums[i]-nums[l]];ok{
                ans = append(ans, []int{nums[i],nums[l],-nums[i]-nums[l]})
            }
        }
        if hash[nums[i]] > 1 {  // 三數存在相同
            if _,ok :=  hash[-nums[i]*2];ok {
                if nums[i] == 0 && hash[0] < 3 { continue }  
                ans = append(ans, []int{nums[i],nums[i], -nums[i]*2})
            }
        }
    }
    return ans
}

雙指標

  • 排序+迴圈遍歷去重
    func threeSum(nums []int) [][]int {
      sort.Ints(nums)
      length := len(nums)
      if length < 3 || nums[length-1] < 0 || nums[0] > 0 {
          return nil
      }
      ans := [][]int{}
      for i:=0;i<length;i++ {
          if i==0 || nums[i] != nums[i-1] { // 非重複項為首
              for l,r:= i+1,length-1;l<r;{
                  if nums[l]+nums[r] == -nums[i] { // 左右邊界同時收縮
                      ans = append(ans,[]int{nums[i],nums[l],nums[r]})
                      for l<r && nums[l] == nums[l+1]{l++}  // 去重
                      for l<r && nums[r] == nums[r-1]{r--}  // 去重
                      l++
                      r--
                  }else if nums[l]+nums[r] < -nums[i] { // 左邊界收縮
                      l++
                  }else{ // 右邊界收縮
                      r--
                  }
              }
          }
      }    
      return ans
    }

二合一

  • 化為兩數之和,雜湊計數跳躍
  • 維護解空間的有序性,化繁為簡,相同或不同
    func threeSum(nums []int) [][]int {
      sort.Ints(nums)
      length := len(nums)
      if length < 3 || nums[length-1] < 0 || nums[0] > 0 {
          return nil
      }
      ans := [][]int{}
      hash := make(map[int]int)
      for _, v := range nums {
          hash[v]++
      }
      for i:=0;i<length;i+=hash[nums[i]] {
          for l,r:= i+hash[nums[i]],length-1;l<r;{
              if nums[l]+nums[r] == -nums[i] { // 左右邊界同時收縮
                  ans = append(ans,[]int{nums[i],nums[l],nums[r]})
                  l +=hash[nums[l]]
                  r -=hash[nums[r]]
              }else if nums[l]+nums[r] < -nums[i] { // 左邊界收縮
                  l +=hash[nums[l]]
              }else{ // 右邊界收縮
                  r -=hash[nums[r]]
              }
          }
          if hash[nums[i]] > 1 {  // 存在相同元素
              if _, ok := hash[-nums[i]*2];ok && nums[i] <= -nums[i]*2{ // 保證有序去重
                  if nums[i] == 0 && hash[0] < 3 {continue}  // 排除特例
                  ans = append(ans, []int{nums[i],nums[i],-nums[i]*2})
              }
          }
      }    
      return ans
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章