1. 程式人生 > 實用技巧 >一文秒殺三個經典面試求和問題

一文秒殺三個經典面試求和問題

一文帶你秒殺三個求和問題

今天為大家帶來三道求和問題,通過文字,圖畫,動圖為大家解析,很容易就能讀懂,每道題目都是經典題,大家快來打卡吧。

題目來源:leetcode 1.兩數之和(簡單) 15.三數之和(中等) 18.四數之和(中等)

兩數之和

題目描述:

給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。

你可以假設每種輸入只會對應一個答案。但是,陣列中同一個元素不能使用兩遍。

示例:

給定 nums = [2, 7, 11, 15], target = 9

因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

題目很容易理解,即讓檢視陣列中有沒有兩個數的和為目標數,如果有的話則返回兩數下標,我們為大家提供兩種解法雙指標(暴力)法,和雜湊表法

雜湊表

解析

雜湊表的做法很容易理解,我們只需通過一次迴圈即可,假如我們的 target 值為 9,當前指標指向的值為 2 ,我們只需從雜湊表中查詢是否含有 7,因為9 - 2 =7 。如果含有 7 我們直接返回即可,如果不含有則將當前的2存入雜湊表中,指標移動,指向下一元素。注: key 為元素值,value 為元素索引。

動圖解析:

是不是很容易理解,下面我們來看一下題目程式碼。

題目程式碼:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i = 0; i < nums.length; i++){
            //如果存在則返回
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i};
            }
            //不存在則存入
            map.put(nums[i],i);

        }
        return new int[0];

    }
}

雙指標(暴力)法

解析

雙指標(L,R)法的思路很簡單,L指標用來指向第一個值,R指標用來從第L指標的後面查詢陣列中是否含有和L指標指向值和為目標值的數。見下圖

例:綠指標指向的值為3,藍指標需要在綠指標的後面遍歷查詢是否含有 target - 3 = 2的元素,若含有返回即可。

題目程式碼

class Solution {
    public int[] twoSum(int[] nums, int target) {
        if(nums.length < 2){
            return new int[0];
        }
        int[] rearr = new int[2];
        //查詢元素
        for(int i = 0; i < nums.length; i++){
            for(int j = i+1; j < nums.length; j++ ){
                if(nums[i] + nums[j] ==target){
                    rearr[0] = i;
                    rearr[1] = j;
                }
            }
        }
        return rearr;
    }
}

三數之和

題目描述:

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

注意:答案中不可以包含重複的三元組。

示例:

給定陣列 nums = [-1, 0, 1, 2, -1, -4],

滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]

這個題目算是對剛才題目的升級,剛才題目我們是隻需返回一個例子即可,但是這個題目是讓我們返回所有情況,這個題目我們需要返回三個數相加為 0 的所有情況,但是我們需要去掉重複的三元組(算是該題的核心),所以這個題目還是挺有趣的,大家記得打卡呀。

雜湊表:

解析

我們這個題目的雜湊表解法是很容易理解的,我們首先將陣列排序,排序之後我們將排序過的元素存入雜湊表中,我們首先通過兩層遍歷,確定好前兩位數字,那麼我們只需要雜湊表是否存在符合情況的第三位數字即可,跟暴力解法的思路類似,很容易理解,但是這裡我們需要注意的情況就是,例如我們的例子為[-2 , 1 , 1],如果我們完全按照以上思路來的話,則會出現兩個解,[-2 , 1 , 1]和[1 , 1, -2]。具體原因,確定 -2,1之後發現 1 在雜湊表中,存入。確定 1 ,1 之後發現 -2 在雜湊表中,存入。所以我們需要加入一個約束避免這種情況,那就是我們第三個數的索引大於第二個數時才存入。

上面這種情況時是不可以存入的,因為我們雖然在雜湊表中找到了符合要求的值,但是 -2 的索引為 0 小於 2 所以不可以存入。

題目程式碼

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
       if(nums.length < 3){
           return new ArrayList<>();
       }
       //排序
       Arrays.sort(nums);
       HashMap<Integer,Integer> map = new HashMap<>();
       List<List<Integer>> resultarr = new ArrayList<>();
       //存入雜湊表
       for(int i = 0; i < nums.length; i++){
           map.put(nums[i],i);
       }
       Integer t;
       int target = 0;
       for(int i = 0; i < nums.length; ++i){            
            target = -nums[i];
            //去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            for(int j = i + 1; j < nums.length; ++j){
                if(j > i+1 && nums[j] == nums[j-1]){
                    continue;
                }             
                if((t = map.get(target - nums[j])) != null){
                    //符合要求的情況,存入
                    if(t > j){                      
                       resultarr.add(new ArrayList<>
                       (Arrays.asList(nums[i], nums[j], nums[t])));

                    } 
                    else{
                        break;
                    }                                                  
                }
            }
       }
       return resultarr;
    }
}

多指標

解析:

如果我們將上個題目得指標解法稱做是雙指標的話,那麼這個題目用到的方法就是三指標,因為我們是三數之和嘛,一個指標對應一個數,下面我們看一下具體思路,其實原理很簡單,我們先將陣列排序,直接 Arrays.sort() 解決,排序之後處理起來就很容易了。下面我們來看下三個指標的初始位置。

初始情況見上圖,我們看當前情況,三數之和為 -3 ,很顯然不是 0 ,那麼我們應該怎麼做呢?

我們設想一下,我們當前的三數之和為 -3 < 0 那麼我們如果移動橙色指標的話則會讓我們的三數之和變的更小,因為我們的陣列是有序的,所以我們移動橙色指標(藍色不動)時和會變小,如果移動藍色指標(橙色不動)的話,三數之和則會變大,所以這種情況則需要向右移動我們的藍色指標,找到三數之和等於 0 的情況進行儲存,如果三數之和大於 0 的話,則需要移動橙色指標,途中有三數之和為 0 的情況則儲存。直至藍橙兩指標相遇跳出該次迴圈,然後我們的綠指標右移一步,繼續執行上訴步驟。但是這裡我們需要注意的一個細節就是,我們需要去除相同三元組的情況,我們看下面的例子。

這裡我們發現 0 - 1 + 1 = 0,當前情況是符合的,所以我們需要存入該三元組,存入後,藍色指標向後移動一步,橙色指標向前移動一步,我們發現仍為 0 -1 + 1 = 0 仍然符合,但是如果繼續存入該三元組的話則不符合題意,所以我們需要去重。這裡可以藉助HashSet但是效率太差,不推薦。這裡我們可以使用 while 迴圈將藍色指標移動到不和剛才相同的位置,也就是直接移動到元素 0 上,橙色指標同樣也是。則是下面這種情況,這樣我們就實現了去重,然後繼續判斷當前三數之和是否為 0 。

動圖解析:

題目程式碼:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> arr = new ArrayList<List<Integer>>();
        if(nums.length < 3){
            return arr;
        }
        //排序
        Arrays.sort(nums);
        if(nums[0] > 0){
            return arr;
        }
        for(int i = 0; i < nums.length-2; i++){
            int target = 0 - nums[i];
            //去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            int l = i+1;
            int r = nums.length - 1;
            while(l < r){
                if(nums[l] + nums[r] == target){
                    //存入符合要求的值
                    arr.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r])));
                    //這裡需要注意順序
                    while(l < r && nums[l] == nums[l+1]) l++; 
                    while(l < r && nums[r] == nums[r-1]) r--;            
                    l++;
                    r--;                   
                }
                else if(nums[l] + nums[r] > target){
                    r--;
                }
                else{
                    l++;
                }
            }
        }
        return arr;
    }
}

四數之和

題目描述

給定一個包含 n 個整數的陣列 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。

注意:

答案中不可以包含重複的四元組。

示例:

給定陣列 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

滿足要求的四元組集合為:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]

我們已經完成了兩數之和和三數之和,這個題目應該就手到擒來了,因為我們已經知道這類題目的解題框架,兩數之和呢,我們就先固定第一個數 ,然後移動指標去找第二個符合的,三數之和,固定一個數,雙指標去找符合情況的其他兩位數,那麼我們四數之和,也可以先固定兩個數,然後利用雙指標去找另外兩位數。所以我們來搞定他吧。

多指標:

解析:

三數之和是,我們首先確定一個數,然後利用雙指標去找另外的兩個數,我們在這個題目裡面的解題思路是需要首先確定兩個數然後利用雙指標去找另外兩個數,和三數之和思路基本一致很容易理解。我們具體思路可以參考下圖。

這裡需要注意的是,我們的 target 不再和三數之和一樣為 0 ,target 是不固定的,所以解題思路不可以完全照搬上面的題目。另外這裡也需要考慮去重的情況,思路和上題一致。

上圖則為我們查詢到一個符合條件的四元組的情況,查詢成功之後,下一步移動藍色指標,重新定義綠藍指標,繼續執行上面步驟。

動圖解析:

題目程式碼:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
           if(nums.length < 4){
               return new ArrayList<>(); 
           }
           Arrays.sort(nums);
           List<List<Integer>> arr = new ArrayList<>();
           for(int i = 0; i < nums.length-3; ++i){
               if(i > 0 && nums[i] == nums[i-1]){
                   continue;
               }
               for(int j = i+1; j < nums.length-2; j++){

                   if(j > i+1 && nums[j] == nums[j-1]){
                       continue;
                   }
                   int l = j+1;
                   int r = nums.length-1;
                   while(l < r){
                       int sum = nums[i] + nums[j] + nums[l] + nums[r];
                       if(sum == target){
                           //存入
                           arr.add(new ArrayList<>
                           (Arrays.asList(nums[i], nums[j], nums[l], nums[r])));
                           //去重
                           while(l < r && nums[l] == nums[l+1]){
                               l++;
                           }
                           while(l < r && nums[r] == nums[r-1]){
                               r--;
                           }
                           l++;
                           r--;
                       }
                       else if(sum > target){
                             r--;
                       }
                       else{
                             l++;
                       }
                   }
               }
           }
           return arr;
    }
}

通過上面的三個例子,大家是不是把此類求和問題摸的透透的啦,如果能感覺到這個文章寫的很用心的話,能給您帶來一丟丟幫助的話,能麻煩您給這個文章點個贊嗎?這樣我就巨有動力寫下去啦。

另外大家如果需要其他精選演算法題的動圖解析,大家可以微信關注下袁廚的演算法小屋,我是袁廚一個酷愛做飯所以自己考取了廚師證的菜雞程式設計師,會一直用心寫下去的,感謝支援!