1. 程式人生 > >LeetCode 15 3sum 三數之和

LeetCode 15 3sum 三數之和

題目連結

https://leetcode-cn.com/problems/3sum/

題意

        很簡單,就是給出一個數組,3個數一組,找到所有和為0的組。並且要求不能重複。或者說找其中3個數其和為0,找出所有的組合。

題解

        很好的一個題了,博主做了3次,前兩次全部TLE,最後才AC了,但是整體思路差不多,就看能不能想到雙指標的操作。前兩次就是太菜了。

        先說一個比較簡單的思路,雙指標的思路也有很大程度的重合,也是博主前兩次TLE的思路:直接爆搜+剪枝。首先陣列是要排序的,那麼我們從最小的數開始暴搜,作為第一個數。然後搜第二個、第三個。如果不剪枝的話絕壁是超時的。比較容易想到的剪枝就是和如果大於0的時候可以直接剪掉了。我們可以當nums[i] > 0、nums[i]+nums[j] > 0分別剪去。然後就是第三個數k的範圍。直接O(N^3)的搜尋肯定不行了,那麼k應該至少從0開始。因為i與j兩者的和必須要<=0才有意義,否則就不存在第三個數了。所以可以預先搜尋0或者第一個大於0的數,作為k的開始。預先搜尋也略坑,我們直接把k當最大值反向搜就可以了。

        其次,一個坑就是不能要求重複。如果我們直接搜而不考慮去重的情況會這樣,例如資料:

-1,-1,0,1

那麼會出現兩組[-1,0,1]這就是重複了。因為我們已經排序,所以去重的方法也非常簡單,例如i,當i > 0時,如果nums[i] = nums[i-1],我們可以continue,跳過這次搜尋,直接i++。這樣就完成了去重。不能利用set去重,這樣就把重複的數變成一個數了,對於:

-1,-1,2

這種類似的資料而言是非常致命的,至少少算了一組。

        上面就是博主TLE兩次的演算法了,很暴力。實際上這樣的演算法時間複雜度可以說是O(N^3)了,雖然看起來剪枝減去了很多,但是TLE還是妥妥的。而用雙指標的話就能將複雜度減為O(n^2),並且仍然能使用上面的剪枝。仍然是遍歷i,但是j與k設定為兩個指標,j = i+1,k = len-1。這樣,如果三數的和為0,那麼就不多說了,j++以及k--,同時加入解集。當三數的和大於0,說明k應該減小,小於0則是j增大。這樣就能在O(n^2)內實現找到所有解。

        當然,對於i、j仍然可以判斷大於0來進行一次剪枝。同時也應該考慮去重的情況。

Java 程式碼

import java.util.*;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if(len < 2) return ans;
        Arrays.sort(nums);
        for(int i = 0;i < len-2;i++){
            if(nums[i] > 0) break;
            if(i > 0){
                if(nums[i] == nums[i-1]) continue;
            }
            int j = i+1;
            int k = len-1;
            while(j < k){
                if(nums[i] + nums[j] + nums[k] == 0){
                    List<Integer> ad = new ArrayList<>();
                    ad.add(nums[i]);
                    ad.add(nums[j]);
                    ad.add(nums[k]);
                    ans.add(ad);
                    k--;
                    while(k > j && nums[k] == nums[k+1]) k--;
                    j++;
                    while(j < k && nums[j] == nums[j-1]) j++;
                }else{
                    if(nums[i] + nums[j] + nums[k] > 0){
                        k--;
                    }else{
                        j++;
                    }
                }
            }
        }
        return ans;
    }
}