力扣之兩數之和

水冗水孚發表於2022-07-03

題目描述

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

你可以假設每種輸入只會對應一個答案。但是,陣列中同一個元素在答案裡不能重複出現。

你可以按任意順序返回答案。

示例 1:

輸入:nums = [2,7,11,15], target = 9
輸出:[0,1]
解釋:因為 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

輸入:nums = [3,2,4], target = 6
輸出:[1,2]

示例 3:

輸入:nums = [3,3], target = 6
輸出:[0,1]
力扣原題目地址:https://leetcode.cn/problems/...

我們來看一下解決方案...

四種方案,層層遞進,主要還是思路...

方案一,直接兩層迴圈判斷值是否相等

var twoSum = function (nums, target) {
                    for (let i = 0; i < nums.length; i++) {
                        // j=i+1即:下一項==當前項+1 因為要不斷和下一項相加看看是否等於target
                        for (let j = i + 1; j < nums.length; j++) { 
                            if (nums[i] + nums[j] === target) {
                                return [i, j]
                            }
                        }
                    }
                };
這種方式可以理解為暴力破解的方式,要迴圈兩次時間複雜度為O(n) * O(n)即為O(n²),太慢了,我們們看看有沒有優化方式

方案二,使用陣列判斷另一個值是否存在

思路:我們已經知道兩數之和的target了,同時在遍歷的時候,也能拿到一個數nums[i],至於另外一個數,我們們可以總和減去一個數即:otherVal = target - nums[i],然後再看看另外一個數otherVal是否在這個陣列裡面,當然不能直接看,直接看又得迴圈一遍了,所以我們們可以新定義一個陣列,把陣列中的樹一次追加進去,直到發現新陣列中的某一項中有另外一個數,也就是說新陣列中的這一項加上nums[i]剛好是等於target,這樣的話,就完成任務了,程式碼如下:

var twoSum = function (nums, target) {
                    let arr = [] // 1. 定義一個陣列用來另存nums陣列中的資料
                    for (let i = 0; i < nums.length; i++) { // 2. 迴圈得到每一項每個 一個數
                        let otherVal = target - nums[i] // 3. 通過一個數 計算出 另一個數
                        if (arr.includes(otherVal)) { // 4. 一開始陣列中肯定不包含另一個數
                            return [arr.indexOf(otherVal), i] // 6. 直到找到 
                        } else {
                            arr.push(nums[i]) // 5. 不包含沒事,那就存一份吧,方便後續匹配
                        }
                    }
                };

注意arr中的陣列中的每一項,從左往右,是和nums陣列中的每一項值以及索引是保持一致的,所以使用arr.indexOf(otherVal)找到的索引就是在nums中的索引

如果大家在力扣中按照這種方式提交的話,會發現這種方式的效能甚至還不如暴力破解的效能好呢!為何呢?原因是:arr.includesarr.indexOf這兩個方法都會遍歷陣列的,所以使用陣列不是太好。那能不能使用物件呢?可以的,我們來看看物件的寫法

方案三,使用物件判斷另外一個值是否存在

var twoSum = function (nums, target) {
                let obj = {} // 1. 定義一個物件用來另存nums陣列中的資料
                for (let i = 0; i < nums.length; i++) { // 2. 迴圈得到每一項每個 一個數
                    let otherVal = target - nums[i] // 3. 通過一個數 計算出 另一個數
                    if (Object.values(obj).includes(otherVal)) { // 4. 一開始物件中的value陣列肯定不包含另一個數
                        // 6. values陣列值中包含這個數的話,就查詢這個值所對應的索引即可
                        let k = (Object.values(obj)).findIndex((item) => { return item == otherVal })
                        return [k, i]
                    } else {
                        obj[i] = nums[i] // 5. 不包含沒事,就用物件存一份,物件的key是索引i,物件的value是索引項i對應的值nums[i],如此方便後續匹配
                    }
                }
            };

思路和方案二使用陣列的方式基本一樣,都是通過差值,找找有沒有另外一項,雖然能解決問題,但是這種寫法是目前三種方案中最差的寫法,因為使用了Object.values、includes、findIndex方法,所以效率非常低。提交力扣勉強能夠通過,此處放上一張圖,大家就知曉了

擊敗百分之五,基本上是倒數了,尷尬,不過沒事,我們們要得主要是思路

方案四,使用Map集合判斷另外一個值是否存在

首先,此方案是最優解,時間空間複雜度都處理的不錯,再說Map集合的解決方案之前,我們先來複習一下Map集合的基本功能,以便於更好的理解後續程式碼

Map知識的簡單複習

let map = new Map() // new例項化一個Map物件
// 新增key value
map.set('name', '孫悟空')
map.set('age', '500')
map.set('home', '花果山水簾洞')
console.log(map.has('name'));// 是否存在key等於name的這個屬性名 // true
console.log(map.get('name'));// 通過key去取其value
console.log(map.has('score'));// 是否存在key等於score的這個屬性名 // true
console.log('map集合', map);

我們來看看列印出來的'map集合'的結果:


仔細一下,呦,這map集合和物件長得差不多啊。我們可以簡單的理解為Map集合是特殊的物件儲存形式,詳細的理解Map集合可以看一下mdn官方 https://developer.mozilla.org...

使用Map集合儲存陣列資料

假設我們有一個陣列是:let nums = [19,24,25,28,30]

  • 我們來看一下使用物件是如何模擬儲存這個資料的
let obj = {
    0:19,
    1:24,
    2:25,
    3:28,
    4:30
}
// 使用物件儲存,物件的key鍵就是陣列的下標索引,物件的value值就是陣列的索引值。當然陣列也可以理解為是一種特殊的物件
  • 我們再來看一下使用Map集合來儲存這個陣列
let map = new Map()
map.set(0, 19)
map.set(1, 24)
map.set(2, 25)
map.set(3, 28)
map.set(4, 30)
console.log('map集合', map);

看看列印'map集合':

這裡一定要反過來哦,陣列的第0項是19,可以map.set(0, 19)也可以map.set(19, 0)具體使用那種方式,看實際需求,本題目中使用map.set(19, 0)方式更加合適

好了,看到這裡大家再看底下的具體程式碼,結合註釋應該就更好理解了

Map集合解決兩數之和問題程式碼

var twoSum = function (nums, target) {
    let map = new Map() // 1. 建立一個map集合用來儲存資料
    for (let i = 0; i < nums.length; i++) { // 2. 迴圈得到每一項每個 一個數
        let otherVal = target - nums[i] // 3. 得到另外一個值
        if (map.has(otherVal)) { // 4. 一開始肯定是不存在的
            return [map.get(otherVal), i] // 6. 存在的話,就得到對應索引值,並返回
        } else {
            map.set(nums[i], i) // 5. 不存在就在map集合存一份如:map.set(19, 0)
        }
    }
};

這種方式還不錯,畢竟Map集合的效率要高一些。注意區分:陣列方式、物件方式、以及Map集合方式它們的判斷是否存在、找索引的區別方式,這一點很重要!

總結

關於兩數之和的解法,其實還有別的方式,本文這四種方式,也只是拋磚引玉,給大家捋一下思路,畢竟工作中,有思路了,問題基本上就解決80%了。

好記性不如爛筆頭,記錄一下吧^_^

相關文章