讓我們一起啃演算法----三數之和

三斤和他的喵發表於2020-05-11

三數之和(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 進行排序,接著初始化: a 指向 nums 陣列的第一個元素,b 指向 nums[a] 的後一個元素,c 指向 nums 的最後一個元素。

初始化時,b 始終指向 nums[a] 的後一個元素, c 始終指向 nums 的最後一個元素。

題目要求三數之和為 0,即 nums[b] + nums[c] = 0 - nums[a],我們將 0 - nums[a] 記為 sum

當 nums[b] + nums[c] 大於 sum 時,c 向左移動;當 nums[b] + nums[c] 小於 sum 時,b 向右移動;當 nums[b] + nums[c] 等於 sum 時,這時候 nums[a], nums[b], nums[c] 為一組有效值,記錄下來,b 向右移動,c 向左移動。直到 b 大於等於 c 時,移動 a 指標再重新初始化 b 和 c,重複上面的步驟,直到 a 越界。

a 越界條件: a 大於 nums 陣列倒數第三個索引時即越界。因為我們有三個指標,當 a 為陣列倒數第三個索引時,b為倒數第二個索引,c為倒數第一個索引。

這裡有一個注意點: a,b,c 指標在移動的過程中需要與前一次指向的陣列值對比,如果相等需要繼續移動,直到不相等。例如 nums[b] 為 1,b 向右移動一位,即 b++, 這時候發現 nums[b] 仍為 1,b 需要繼續向右移動一位,直到 nums[b] 不為 1。

上面的操作是為了保證結果中沒有重複的元組。
例如 nums 為 [-1 -1 0 1 2],假設 a 為 0,即 nums[a] 為 -1,會找到一個有效元組 [-1, 0, 1],這時候向右移動 a ,即 a 為 1,nums[a] 為 -1 與上一次值相同,a 需要繼續向右移動,否則又會找到一個有效元組 [-1, 0, 1],這就重複了。同理 b,c 也是如此。

流程圖如下:

程式碼實現

GO語言實現

func threeSum(nums []int) [][]int {
    var result [][]int
    if len(nums) <= 2 {
        return result
    }
    // 需要排序
    sort.Ints(nums)
    a := 0
    // 有三個指標,a 是最左邊的,所以 a 最大值為 nums 倒數第三個索引
    for a <= len(nums)- 3 {

        // 保證 a 在向右移動時指向的值,不與之前的值相等
        if a != 0 && nums[a] == nums[a-1]  {
            a++
            continue
        }

        // b 為 a 的後一個指標
        b := a + 1

        // c 指向 nums 最後一個元素
        c := len(nums) -1

        // sum 的值為後續 nums[b] + nums[c] 的值
        sum := 0 - nums[a]

        // b 必須小於 c
        for b < c {

            // 保證 c 在向左偏移時指向的值,不與之前的相等
            if c < len(nums) - 1 && nums[c] == nums[c + 1] {
                c--
                continue
            }

            // 保證 b 在向右偏移時指向的值,不與之前的相等
            if b > a + 1 && nums[b] == nums[b-1] {
                b++
                continue
            }

            // 比較和
            if nums[b]+nums[c] > sum {
                c--

            } else if nums[b]+nums[c] < sum {
                b++

            } else {
                result = append(result, []int{nums[a], nums[b], nums[c]})
                c--
                b++
            }

        }
        // a 向右移動
        a++
    }
    return result

}

總結

每天進步一點點,加油!
演算法教程專案,每天更新一題,點個 star 支援一下呀
github.com/wx-satellite/learning-a...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

三斤

相關文章