跳至內容
返回

Leetcode 刷題記錄(1)

發佈於:  at  08:00 上午

Two Sum 是刷 Leetcode 的人必刷的經典題目,通常也是第一題。儘管我已經解決過了,但我希望自己可以明確清楚的解釋自己的思路,所以我還是寫了一篇Leetcode 解題文章。那麼我們開始吧

Two Sum

題目描述

給定一個陣列nums與整數target,回傳陣列中兩個加總等於 target 的元素之 index。題目明確寫道,答案只有一個。

解題思路

Solution 1

第一直覺就是暴力解,做法是使用兩層 for 迴圈,一旦找到相等就要立刻return。Java實現如下:

public int[] twoSum(int[] nums, int target) {
    // find two element that sum up equals to target
    for(int i=0;i<nums.length;i++){
        for(int j=i+1;j<nums.length;j++){
            int ans = nums[i]+nums[j];
            if(ans==target) return new int[]{i,j};
        }
    }
    return new int[]{-1,-1};
}

這樣能夠解決問題,時間複雜度是O(n²),最壞情況下需比較約 n²/2 次,故為 O(n²),相當耗時。空間複雜度是O(1),因為並沒有使用更多的資料結構。

Solution 2

接著這題的正確解法是利用HashMap方式,才能將時間複雜度從 O(n²) 降至 O(n)。解決思路如下: 定義一個HashMap保存陣列中的值與index,並且在 target - nums[i] 作為 complement 的結果可以在 HashMap 中被找到的話,就代表找到配對了。Java實現如下

    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();

        for(int i=0;i<nums.length;i++){
            if(!map.containsKey(target-nums[i])){
                map.put(nums[i],i);
            } else {
                return new int[]{map.get(target-nums[i]),i};
            }
        }
        return new int[]{-1,-1};
    }

這個解法的時間複雜度為O(n),因為n個元素只需要跑n次,空間複雜度:O(n),最差情況下n筆資料要存n次。

總結

Two Sum 雖然是一道簡單題,但它其實涵蓋了幾個重要觀念:

這也是為什麼它會成為 Leetcode 最經典的入門題之一。

Two Sum 的精髓在於:與其找兩個數,不如記住已見過的數,再找它的『另一半』。這個思維也適用於許多其他題目。

Remove Duplicates from Sorted Array

題目描述

定義一個從小到大的陣列,在原陣列上原地操作,使前 k 個元素為不重複的有序元素,並回傳 k。

解題思路

Solution 1

我會使用一個for迴圈掃描陣列,並且定義一個pointer值。pointer值初始是0。 陣列中掃描元素同時與上一個做比較,如果與上一個元素相同,代表重複,pointer值+1。 pointer的用途是將當前陣列值移動到當前位置-pointer的新位置。 最後移除後的陣列長度就是nums.length-pointer。

用Java實現的邏輯就是

public int removeDuplicates(int[] nums) {
    int duplicates=0;
    for(int i=1;i<nums.length;i++){
        if (nums[i]==nums[i-1]) {
            duplicates +=1;
        }
        nums[i-duplicates] = nums[i];
    }
    return nums.length-duplicates;
}

Solution 2

除此之外,還可以使用更簡潔的解決方法,邏輯類似,只是slow pointer會在不重複的時候增加,重複時不變動。

public int removeDuplicates(int[] nums) {
    int slow = 1;
    for (int fast = 1; fast < nums.length; fast++) {
        if (nums[fast] != nums[fast - 1]) {
            nums[slow] = nums[fast];
            slow++;
        }
    }
    return slow;
}

總結

兩者都一樣,時間複雜度O(n)、空間複雜度O(1)。

兩種解法的核心思維相同:用一個 pointer 追蹤「下一個有效寫入位置」, 差別在於 Solution 1 以「重複數量」作為偏移量,Solution 2 則是顯式的 fast/slow pointer, 面試時 Solution 2 語意更清楚,建議優先使用。

Best Time to Buy and Sell Stock

題目描述

題目給定一個陣列,元素代表股價,請在陣列中計算出可以獲得的最大利益。

解題思路

利益最大就代表要找出陣列的最小值,以及計算出最小值與元素相比相差最大的,就是最大利潤。 Java解法如下。

    public int maxProfit(int[] prices) {
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;
        for(int price: prices) {
            // calculate min price
            minPrice = Math.min(minPrice,price);
            int profit = price-minPrice;
            maxProfit = Math.max(maxProfit,profit);
        }
        return maxProfit;
    }

總結

時間複雜度為O(n),空間複雜度為O(1)

Best Time to Buy and Sell Stock 的精髓在於: 不需要枚舉所有買賣組合,只要邊掃描邊維護「目前最低價」, 就能在 O(n) 內找到最大利潤。


建議修改
在以下平台分享此文章:

下一篇
Leetcode 刷題記錄(2)