JS leetcode 兩數之和解答思路分析


壹 ❀ 引

在學習算法基礎的同時,我還是繼續撿起leetcode的算法題了,珍惜時間,算法每天進步一點點。不得不說,在了解了一些算法概念后,至少有些答案是能看懂了......(慚愧)雖然我很菜,但是多寫多練應該還是會有提升。那么這篇文章就從兩數之和開始。

原題如下:

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

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

示例:

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

請實現如下方法能達到上述效果:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {

};

貳 ❀ 聽風是風不太聰明的實現

在解釋優質答案前,我先說說自己的思路,畢竟不是很好的做法,大家也可以跳過不看。

我想出了兩種做法,先說第一種,由於只有一個數組,但是我們得讓數組中的元素兩兩相加並與目標值判斷是否相等,所以首先想到就是利用for循環嵌套,遍歷同一個數組。

由於不能重復使用同一個元素,所以外層從0開始遍歷時,內層循環的索引起點肯定得是1,當外層是1時,內層索引從2開始。這樣做的目的除了避免同一元素多次使用外,還減少了重復比較次數。我們來大致腦補下,比如外層是0時,會與內層1相加,而如果內層不在外層基礎上加1,就還會出現外層是1時,與內層是0相加比較的情況,這樣就重復了。

所以知道了這些就好做了,直接貼我的實現:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    // 用來裝目標索引的空數組
    var result = [],
        len = nums.length;
    for (var i = 0; i < len; i++) {
        // 注意,這里從i+1開始遍歷
        for (var j = i + 1; j < len; j++) {
            if (nums[i] + nums[j] === target) {
                // 注意,concat返回一個新數組
                result = result.concat(i, j);
                return result;
            };
        };
    }
};

我想到的第二種解決思路其實是在學插入排序時得到的靈感,上面的實現簡單粗暴,無非就是把一個數組當兩個用,第二種思路就是對同一個數組正序倒序同時遍歷比較,當然也得使用循環嵌套。

比如外層循環從0開始,那么內層循環從length-1處倒序遍歷,一直遍歷到0前面一位,期間的元素分別與0的數字相加並與target比較:

直接貼我的第二種思路,由於還是使用了循環嵌套,所以耗時相比第一種也只是提升了一點點,但內存使用卻大了不少,所以也不算很好的實現。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var result = [],
        len = nums.length;
    for (var i = 0; i < len; i++) {
        var len_ = len - 1;
        // 倒序遍歷,一直遍歷到i的前一位
        while (len_ > i) {
            if (nums[i] + nums[len_] === target) {
                result = result.concat(i, len_);
                return result;
            };
            len_--;
        };
    };
};

叄 ❀ 優質解答

那么在說完我的弱智算法,來看看官方的優質解答,直接貼代碼,我們再分析實現:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var temp = [],
        len = nums.length;
    for (var i = 0; i < len; i++) {
        var dif = target - nums[i];
        if (temp[dif] != undefined) {
            return [temp[dif], i];
        };
        temp[nums[i]] = i;
    };
};

這個思路其實就是借助了哈希表,犧牲少量空間大大減少運行用時,畢竟只用了一次遍歷。如果大家對於哈希表很陌生,還是很推薦大家先理解常用數據結構,我在從零開始的算法入門科普(二)一文中有通過圖解簡單科普哈希表的概念,只是在上述代碼在哈希表存值時並未借助哈希函數。

通常來說哈希表常常用來存儲key-value的數據,由於我們最終是要獲取目標元素索引,所以代碼實現中直接使用數組元素作為位置映射,用於存儲當前元素索引。其次,巧妙通過var dif = target - nums[i]分別獲取目標值與當前遍歷元素的差值,如果這個差值作為哈希表的索引能訪問到有效元素,那么說明當前遍歷的i與temp[dif](拿到的是元素在nums中的索引)就是要找的索引。

不看答案我是真的想不上去,表示驚呆了,那么關於兩數之和就說到這了,收獲很大!!!

本文結束!

2020.5.24復習,與哈希表思路相同,不過這次換成了字典,執行時間會略微提升,代碼如下:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var i = 0,
        len = nums.length,
        dict = {};
    for (; i < len; i++) {
        var dif = target - nums[i];
        if (dif in dict) {
            return [dict[dif], i];
        };
        dict[nums[i]] = i;
    };
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM