算法-回溯法/雙指針-三數之和

算法-回溯法/雙指針-三數之和

1 題目概述

1.1 題目出處

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

1.2 題目描述

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

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

示例:

給定數組 nums = [-1, 0, 1, 2, -1, -4],

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

2 回溯法

2.1 思路

3個level,層層遞進回溯,並進行相應剪枝。

可惜的是,就算如此仍有超長用例超時。

2.2 代碼

class Solution {
    private List<List<Integer>> resultList = new ArrayList<>();
    public List<List<Integer>> threeSum(int[] nums) {
        if(nums == null || nums.length < 3){
            return resultList;
        }
        // 必須先排序,避免重複組合
        Arrays.sort(nums);
        backtrack(nums, 0, new ArrayList<Integer>(), 0);
        return resultList;
    }

    // 返回是否應剪枝
    private void backtrack(int[] nums, int level, List<Integer> tmpList, int index){
        if(level == 2){
            // 結束條件
            List<Integer> result = new ArrayList(tmpList);
            int sum = result.get(0) + result.get(1);
            for(int i = index; i < nums.length; i++){
                int tmp = sum + nums[i];
                if(tmp == 0){
                    // 找到一個滿足和爲0的就返回了
                    result.add(nums[i]);
                    resultList.add(result);
                    return;
                }
                if(tmp > 0){
                    // 剪枝1
                    // 因爲已經排序,所以sum+nums[i+1]肯定也 > 0
                    return;
                }
            }
            // 最終沒找到和爲0的也返回
            return;
        }
        // 記錄本level已經選過的,選過的不再重複選
        // 也就是說3個level,每個level選的數字不會重複,避免重複組合
        Set<Integer> selected = new HashSet<>();
        for(int i = index; i < nums.length - 1; i++){
            if(selected.contains(nums[i])){
                continue;
            }
            tmpList.add(nums[i]);
            int tmp = 0;
            for(int num : tmpList){
                tmp += num;
            }
            tmp += nums[i + 1];
            if(tmp > 0){
                // 剪枝2
                // 當level小於2時,當前數字和下一個數字之和大於0,則直接剪枝.
                tmpList.remove(level);
                return;
            }
            selected.add(nums[i]);
            backtrack(nums, level + 1, tmpList, i + 1);
            tmpList.remove(level);
        }
    }
}

2.3 時間複雜度

在這裏插入圖片描述

3 雙指針

3.1 思路

利用棧,如果棧爲空或者當前元素比棧頂元素大,就將棧頂元素出棧並計算相應結果,然後繼續比較下一個棧頂元素,直到當前元素不大於棧頂元素或棧爲空。

最後將元素直接放入棧頂。

遍歷完成後,還需要處理棧中元素,剩餘的元素代表右側沒有比他們更高的氣溫了,所以升高氣溫天數都爲0。

3.2 代碼

class Solution {
    private List<List<Integer>> resultList = new ArrayList<>();
    public List<List<Integer>> threeSum(int[] nums) {
        if(nums == null || nums.length < 3){
            return resultList;
        }
        // 必須先排序,避免重複組合
        Arrays.sort(nums);
        for(int i = 0; i < nums.length - 2; i++){
            if(nums[i] > 0){
                break;
            }
            int p = i + 1;
            int q = nums.length - 1;
            if(i > 0 && nums[i] == nums[i-1]){
                // 避免重複組合
                continue;
            }
            while(p < q){
                int sum = nums[i] + nums[p] + nums[q];
                if(sum == 0) {
                    List<Integer> result = new ArrayList<>();
                    result.add(nums[i]);
                    result.add(nums[p]);
                    result.add(nums[q]);
                    resultList.add(result);
                    while(++p < q && nums[p] == nums[p - 1]);
                    while(p < --q && nums[q] == nums[q + 1]);
                } else if(sum > 0){
                    q--;
                } else {
                    p++;
                }
            }
        }
        return resultList;
    }
}

3.3 時間複雜度

在這裏插入圖片描述
O(N^2)

3.4 空間複雜度

O(1)

4 數組模擬棧

4.1 思路

思路和前面棧方法相同,只不過不用Stack類而是自己用數組模擬棧,減少了很多不必要的複雜操作開銷。

4.2 代碼

class Solution {
    public int[] dailyTemperatures(int[] T) {
        int[] result = new int[T.length];
        // 該int數字0位表示在原氣溫數組中位置,1位表示氣溫值
        int[][] tmp = new int[T.length][2];
        int[] top;
        // 標記tmp中當前應處理的行下標
        int offset = -1;
        for(int i = 0; i < T.length; i++){
            while(offset > -1){
                top = tmp[offset];
                if(T[i] <= top[1]){
                    break;
                }
                offset--;
                // 升高所需天數
                result[top[0]] = i - top[0];  
            }
            // 最後將當前數字入棧
            tmp[++offset] = new int[]{i, T[i]};
        }
        return result;
    }
}

4.3 時間複雜度

在這裏插入圖片描述
O(N)

4.4 空間複雜度

O(N)

5 倒序查找

5.1 思路

要求右側第一個溫度跟高的元素,所以其實從右往左找更方便。

而且,我們還加入一個快速判定方法,這裏假設初始時j=i+1:

  • T[i] < T[j]
    顯然result[i] = j - I;
  • T[i] >= T[j]且result[j] == 0,即i元素溫度不低於j元素,但j元素右側沒有更高的溫度,則i元素顯然也不能在右側升高溫度:
    result[i] = 0;
  • T[i] >= T[j]且result[j] > 0,即i元素溫度不低於j元素,且j元素右側有更高的溫度,此時可通過j = result[j] + j快速找到第一個比j高的溫度的下標,然後讓i和這個新下表元素比較

5.2 代碼

class Solution {
    public int[] dailyTemperatures(int[] T) {
        int[] result = new int[T.length];
        result[T.length - 2] = 0;
        for(int i = T.length - 2; i >= 0; i--){
            int j = i + 1;
            // 往右找第一個比i還大的
            while(true){
                if(T[i] < T[j]){
                    result[i] = j - i;
                    break;
                }else if(result[j] == 0){
                    // 注意此時T[i] >= T[j]
                    result[i] = 0;
                    break;
                } else{
                    // 注意此時T[i] >= T[j]且result[j] > 0
                    // 找到第一個比j高的溫度的下標
                    j = result[j] + j;
                }
            }
        }
        return result;
    }
}

5.3 時間複雜度

在這裏插入圖片描述
O(N)

5.4 空間複雜度

O(1)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章