來源:力扣(LeetCode)
連接:https://leetcode-cn.com/probl...數組
給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出全部知足條件且不重複的三元組。學習
注意:答案中不能夠包含重複的三元組。指針
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4],code
知足要求的三元組集合爲:排序
[ [-1, 0, 1], [-1, -1, 2] ]
題解:three
看到這一題,通常的第一反應都是暴力法,三層循環嵌套,二話不說就是玩命給他幹出來。ip
let nums = [-1, 0, 1, 2, -1, -4]; var threeSum = function(nums) { let arr = []; for(let i = 0; i < nums.length-1;i++){ for(let j = i+1;j<nums.length;j++){ for(let m = j+1;m<nums.length;m++){ if(nums[i]+nums[j]+nums[m] === 0){ arr.push([nums[i],nums[j],nums[m]]) } } } } return arr; }; console.log(threeSum(nums));
可是呢,這個方法雖然簡單粗暴,可是存在着很大的問題,第一點就是當數據量特別大的時候,會致使超時。第二點就是存在着重複的問題,得出來的結果須要去重。第三點,時間複雜度O(n^3)。內存
因此呢,必需要找一個更簡單的方法。leetcode
個人思路跟題解中的第一個大佬是差很少的,只不過沒有那麼打籃球聚會什麼的修飾。get
首先把這條題簡化成在一個數組中尋找兩個數字和爲某個值的全部可能。那麼你們第一時間也會用暴力法來作,以下:
let nums = [-1, 0, 1, 2, -1, -4]; var twoSum = function (nums, sum) { let arr = []; for (let i = 0; i < nums.length - 1; i++) { for (let j = i + 1; j < nums.length; j++) { if (nums[i]+nums[j] === sum) { arr.push([nums[i], nums[j]]) } } } return arr; }; console.log(twoSum(nums,1));//[[-1,2],[0,1],[2,-1]]
這裏就會出現重複的問題,那麼咱們就能夠結合前一次咱們學習的雙指針的方法來算這一題。在數組首尾放上一個指針,進行相加,而後與結果比較,移動相應的指針。以下:
let nums = [-1, 0, 1, 2, -1, -4]; var twoSum = function (nums, sum) { let arr = []; let left = 0,right = nums.length-1; while(left < right){ if(nums[left]+nums[right]>sum){ nums[left]>nums[right] ? left++ : right--; }else if(nums[left]+nums[right]<sum){ nums[left]<nums[right] ? left++ : right--; }else{ arr.push([nums[left],nums[right]]); nums[left]<nums[right] ? left++ : right--; } } return arr; }; console.log(twoSum(nums,1));//[[-1,2],[0,1]]
這種狀況就算是基本完成了,不過這裏面會有一個重複比較的問題,只要存在相同的值,那麼就會有這個問題。解決辦法呢就是在最開始就將數組排序,從小到大排列,在比較完一次以後,移動指針的時候進行一個判斷,若是移動完以後的值跟以前的值是同樣的,那麼繼續移動指針,直到不能移動爲止。數組從小到大排序以後,比較起來也更好判斷移動哪根指針;
let nums = [-1, 0, 1, 2, -1, -4]; var twoSum = function (nums, sum) { nums = nums.sort((a,b)=>{return a-b}); console.log(nums);//[-4, -1, -1, 0, 1, 2] let arr = []; let left = 0,right = nums.length-1; while(left < right){ if(nums[left]+nums[right]>sum){ //大於和值的時候,右邊right值爲大的數,移動一格,尋找更小的數 do{--right}while(nums[right]===nums[right+1]) }else if(nums[left]+nums[right] < sum){ //小於和值的時候,左邊left值爲小的數,移動一格尋找更大的數 do{++left}while(nums[left]===nums[left-1]); }else{ //等於和值的時候,左右都移動一格,省去重複的值 arr.push([nums[left],nums[right]]); do{--right}while(nums[right]===nums[right+1]); do{++left}while(nums[left]===nums[left-1]) } } return arr; }; console.log(twoSum(nums,1));//[[-1,2],[0,1]]
就這樣,數組中兩數和爲某個值的問題就迎刃而解了。那麼咱們如今是三個值的和,其實這個反而沒有那麼複雜。
對數組進行一次循環,每一個值爲nums[i]
,而後在剩下的值裏面尋找和爲0-nums[i]
的全部可能。同時爲了防止重複操做,在循環的時候,先後值相等時候,直接跳過。
並且在最外層循環的時候,由於數組已經從小到大排序過了,因此當循環到大於0的值的時候,直接進行下一次循環。最終的代碼以下:
/** * @param {number[]} nums * @return {number[][]} */ var threeSum = function(nums) { nums = nums.sort((a,b)=>{return a-b}); let arr = []; for(let i = 0; i < nums.length;i++){ if(nums[i]>0 || nums[i] === nums[i-1])continue; let sum = 0 - nums[i]; let left = i+1,right = nums.length-1; while(left < right){ if(nums[left]+nums[right]>sum){ //大於和值的時候,右邊right值爲大的數,移動一格,尋找更小的數 do{--right}while(nums[right]===nums[right+1]) }else if(nums[left]+nums[right] < sum){ //小於和值的時候,左邊left值爲小的數,移動一格尋找更大的數 do{++left}while(nums[left]===nums[left-1]); }else{ // console.log(nums[i], nums[left], nums[right]); //等於和值的時候,左右都移動一格,省去重複的值 arr.push([nums[i],nums[left],nums[right]]); do{--right}while(nums[right]===nums[right+1]); do{++left}while(nums[left]===nums[left-1]) } } } return arr; };
執行結果:
執行用時 :192 ms, 在全部 JavaScript 提交中擊敗了95.77%的用戶
內存消耗 :46.3 MB, 在全部 JavaScript 提交中擊敗了86.32%的用戶