[LeetCode系列] 15.三數之和

1.題目描述

給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出全部知足條件且不重複的三元組。ios

示例:

給定數組 nums = [-1, 0, 1, 2, -1, -4]
知足要求的三元組集合爲:c++

[算法

[-1, 0, 1],segmentfault

[-1, -1, 2]數組

]優化

2.題目分析

該題與LeetCode第二題兩數之和的思路比較相似。上一篇文章已經詳細的講解了兩數之和問題,可點擊連接複習一下[[LeetCode刷題系列]1.兩數之和](https://segmentfault.com/a/11...spa

讓咱們簡單回顧一下兩數之和問題,找到數組中的兩個元素,使其之和爲一個目標值,即a+b=target. 而這一題的要求是a+b+c=0,咱們能夠對題目稍做轉化,根據移項咱們能夠獲得等式a+b=-c.因而該問題就轉化成了是否能在數組中找到兩個數,使它們之和等於另外一個數的相反數。此時,這個數的相反數就是咱們兩數之和問題中的 target .不一樣的地方是此處的target並非一個固定的數,而多是數組中的每一個數。3d

注意: 該題要求找出全部知足條件且不重複的三元組。爲了不數組中相同元素對算法最終結果的影響,咱們須要先對數組進行排序(C++中能夠利用<algorithm>中的 sort 算法),固然本題的重點不是在如何排序,因此你們須要注意在算法中對重複狀況進行排查。指針

Ⅰ.暴力解法

根據前面的分析咱們能夠知道,咱們能夠遍歷數組中的每一個元素,令它的相反數做爲target, 同時在數組剩下的元素中查找兩個元素的和是否等於target. 同時在第二層第三層嵌套繼續使用暴力解法,這樣,咱們將進行三層循環,該算法的時間複雜度就將爲 $O(n^3)$ . 該算法思路簡單,可是最終時間複雜度較大,有興趣的朋友能夠本身敲一敲代碼。code

Ⅱ.雙指針法

對於暴力解法,咱們有沒有什麼辦法進行優化呢?首先在第一個步驟中,咱們須要遍歷數組中的每一個元素,將其固定,取其相反數做爲咱們的目標值(target),該步驟中 O(n) 的時間複雜度是咱們沒法避免的。那麼咱們能夠在查找剩下的元素的過程當中能夠採起什麼辦法來提升咱們算法的時間複雜度呢?

相信你已經想到了,利用兩數之和問題中講解到的哈希表法,能夠大大下降查找時的時間複雜度。但這裏我要給你們介紹一種更天然,更廣泛,更易理解的方法——雙指針法。

在使用雙指針法以前,你們要記得,咱們須要對數組進行預處理,那就是將數組進行排序。固然對於本題,在以前的分析中咱們已經對數組進行了排序。

當咱們開始遍歷數組的時候,每當咱們固定數組中的一個元素,此時,將雙指針j和k置於右側數組裏的兩端。若是指針所值元素之和大於咱們的target, 就將右側的指針向左移動;同理,若是元素之和小於咱們的target, 就將左側指針向右移動。就這樣,經過指針不斷地向中間移動,(這有點像在高等數學中學過的「夾逼定理」),直到找到每個知足條件的 i,j,k 值,並存入數組中,最後將其返回。

例如,在下圖的數組中,當咱們固定數組中的第一個元素i=0時,取其相反數4做爲咱們的target,指針jk位於右側數組兩端,當指針所指值之和 -3+4=1<target 時,咱們須要挪動指針j, 直到找到一對jk, 使其對應值之和爲 target .

一樣,當i=3時,對應target=1,指針jk首先位於右側數組兩端,其元素和 -1+4=3>1 ,因而咱們將處於右邊的指針向左挪動,直到找到 -1+2=1jk的位置,最後將i,j,k一同返回。

3.手繪講解

  • 對數組進行排序

  • 遍歷數組中每一個元素,而後利用雙指針法進行查找。

注意:

爲了不數組中重複元素的影響,須要用下面的代碼進行排查!

<center>(避免j,k所指元素重複)</center>

<center>(避免i所指元素重複)</center>

代碼實現

#include<iostream>
#include <algorithm> 
#include<vector>
using namespace std;

vector<vector<int>> threeSum(vector<int>&);
int main()
{
  vector<int> test = {-1,-1, -1,0,1,2,-1,-4 };
  vector<vector<int>> ans;
  ans = threeSum(test);
  for (auto x : ans)
  {
    for (auto y : x) cout << y << " ";
    cout << endl;
  }
  return 0;
}

vector<vector<int>> threeSum(vector<int>& nums) {
  vector<vector<int>> res;
  sort(nums.begin(), nums.end());
  for (int i = 0; i < nums.size() - 2; ++i) {
    int front = i + 1;
    int back = nums.size() - 1;
    int sum = nums[front] + nums[back];
    while (front < back) {
      if (sum + nums[i] > 0) back--;
      if (sum + nums[i] < 0) front++;
      if (sum + nums[i] == 0) {
        vector<int> array(3, 0);
        array[0] = nums[i];
        array[1] = nums[front];
        array[2] = nums[back];
        res.push_back(array);
        while (front < back && array[1] == nums[front]) front++;
        while (front < back && array[2] == nums[back]) back--;
      }   
    }
    while (i + 1 < nums.size() && nums[i] == nums[i + 1]) i++;
  }
  return res;
}

高票回答

  • C++實現:

  • Java實現:

  • Python實現:

若是你喜歡個人文章,歡迎掃描下方二維碼關注公衆號瞭解更多LeetCode題解思路和算法知識!

相關文章
相關標籤/搜索