青銅三人行之兩數之和

先說一個消息,爲了方便互相交流學習,青銅三人行建了個微信羣,感興趣的夥伴可以掃碼加下面的小助手抱你入羣哦!
青銅三人行小助手

哈嘍,大家好,歡迎來到青銅三人行的每週一題現場。在接下來的時間裏,我們三人(Helen、書香、曾大師)會在每週選擇一道編程算法題來完成,和大家一起探討一下解題的思路。所謂每週一題,代碼無敵,歡迎各位小夥伴們一起進入我們的刷題之旅~

兩數之和

青銅三人行——每週一題@兩數之和

力扣題目

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

你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。

//給定 nums = [2, 7, 11, 15], target = 9
//因爲 nums[0] + nums[1] = 2 + 7 = 9
//所以返回 [0, 1]

解法一

拿到題目,Helen 心想,這次題目難道不大。略一思忖,要在數組中找到滿足某個條件的兩個數,一個雙重循環搞定即可:

function twoSum(num, target) {
    for (const index in num) {
        for (const _index in num) {
            if (index !== _index && num[index] + num[_index] === target) {
                return [index, _index];
            }
        }
    }
}
​
//作者:Helen
//鏈接:https://leetcode-cn.com/circle/discuss/5cC2dU/view/p3MA3g/
//來源:力扣(LeetCode)
//著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

在這裏插入圖片描述
順利通過題目!但是效率似乎並不理想…

解法二

接下來就要優化算法,Helen 審視代碼,發覺影響效率的主要原因恐怕是在於雙重循環所造成的 O(n²) 複雜度。要想提高效率,恐怕就要在一重循環裏搞定題目。但如何在一次迭代中找到兩個數的關係呢?確實頗費考慮… 算法領域中,空間與時間通常如同魚和熊掌一般不可兼得。空間換時間…Helen 靈光一現,對了,一次迭代中表現兩個數的關係,可以在 map 結構中用查找 key 的方式呀。考慮至此,信手寫出了第二版代碼:

function twoSum(nums, target) {
    const numsMap = {};
    for (const index in nums) {
        numsMap[nums[index]] = index;
    }
    for (const index in nums) {
        const complement = target - nums[index];
        if (numsMap[complement] && numsMap[complement] !== index) {
            return [index, numsMap[complement]];
        }
    }
}
​
//作者:Helen
//鏈接:https://leetcode-cn.com/circle/discuss/5cC2dU/view/p3MA3g/
//來源:力扣(LeetCode)
//著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

如此一來,時間複雜度減爲 O(n), 速度果然大大提高:

在這裏插入圖片描述

書香作爲一個函數式編程的擁護者,平日裏對 map, filter, reduce 等方法都記在心裏。看到這個代碼,心想恐怕在循環中對數組的頻繁引用是一個可以優化的點,於是利用 JavaScript 中內置的 reduce 方法稍作修改:

const twoSum = function(nums, target) {
    const objNums = nums.reduce((acc, num,index) => {
        acc[num]=index; 
        return acc},{});
​
    for (let i=0; i<nums.length;i++) {
        const num = nums[i];
        const other = objNums[target-num];
        if(other!==undefined && other!==i){
            return [i,other]
        }
    }
    return;
};
​
//作者:demongodYY
//鏈接:https://leetcode-cn.com/circle/discuss/5cC2dU/view/8eOrHo/
//來源:力扣(LeetCode)
//著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

時間和空間上居然都有所提高,看來 JavaScript 對內置方法的優化果然到位:
在這裏插入圖片描述

解法三

於此同時,Helen 則進一步對代碼進行了優化。題目要求只需要找到滿足條件的兩個數,那麼有可能在沒有遍歷完的時候就能找到呀。如此一來,就不必提前將整個數組轉換成 map 結構,而是邊轉換邊查找,在找到滿足條件的時候即可返回:

function twoSum(nums, target) {
    const numsMap = {};
    for (const index in nums) {
        const complement = target - nums[index];
        if (numsMap[complement]) {
            return [ index, numsMap[complement]];
        }
        numsMap[nums[index]] = index;
    }
}
​
//作者:Helen
//鏈接:https://leetcode-cn.com/circle/discuss/5cC2dU/view/p3MA3g/
//來源:力扣(LeetCode)
//著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

如此一來,代碼性能大大地得到了優化:
在這裏插入圖片描述

extra

最後,由曾大師爲我們在 go 語言中展現了一把對內存的極致管理,也體現了對於不同編程語言特性的優化差別:

func twoSum(nums []int, target int) []int {
    for i := 0; i < len(nums); i++ { 
        for j := i+1; j < len(nums); j++ {
            if nums[i]+nums[j] == target {
                return []int{i,j}
            }
        }
    }
    return []int{}
}
​
//作者:glowd
//鏈接:https://leetcode-cn.com/circle/discuss/5cC2dU/view/omqRef/
//來源:力扣(LeetCode)
//著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

天啊,內存消耗僅爲 2.9MB,在所有 Go 提交中擊敗了 100% 的用戶!
在這裏插入圖片描述

結尾

OK,這就是咱們青銅三人行的第一次分享的全部內容啦,雖然很多地方還不完善,但也希望憑藉一點微薄的力量,提起大家對編程算法題的興趣。

如果看到了這次分享,你有一些靈感的話,請立即拿起手中的鍵盤,打開 leetcode 的網站找到題目先刷一遍,並與我們或者身邊的小夥伴們分享你的思路~

下週見!


三人行

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