LeetCode高頻算法面試題 - 001 - 兩數(shù)之和

大家好,我是漫步coding, 最近在整理2022年Redis最新面試題, 大家也可以通過我下面的博客地址在線閱讀,今天講講LeetCode高頻算法面試題 - 兩數(shù)之和。本文首發(fā)于公眾號:漫步coding

題目來源于 LeetCode 上第 1 號問題:兩數(shù)之和。題目難度為 Easy。

題目描述

給定一個整數(shù)數(shù)組 nums 和一個目標(biāo)值 target,請你在該數(shù)組中找出和為目標(biāo)值的那 兩個 整數(shù),并返回他們的數(shù)組下標(biāo)。

你可以假設(shè)每種輸入只會對應(yīng)一個答案。但是,你不能重復(fù)利用這個數(shù)組中同樣的元素。

題目難度: ★

示例:

給定 nums = [2, 7, 11, 15], target = 9

因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

示例 2:

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

示例 3:

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

題目解析

使用查找表來解決該問題。

設(shè)置一個 map 容器 record 用來記錄元素的值與索引,然后遍歷數(shù)組 nums。

  • 每次遍歷時使用臨時變量 complement 用來保存目標(biāo)值與當(dāng)前值的差值
  • 在此次遍歷中查找 record ,查看是否有與 complement 一致的值,如果查找成功則返回查找值的索引值與當(dāng)前變量的值 i
  • 如果未找到,則在 record 保存該元素與索引值 i

代碼實現(xiàn)

tips: 以下代碼是使用Go代碼實現(xiàn)的不同解法, 文章最后可以看C++、C、Java、Python實現(xiàn)

1、暴力解法

最容易想到的方法是枚舉數(shù)組中的每一個數(shù) x,尋找數(shù)組中是否存在 target - x。

當(dāng)我們使用遍歷整個數(shù)組的方式尋找 target - x 時,需要注意到每一個位于 x 之前的元素都已經(jīng)和 x 匹配過,因此不需要再進行匹配。而每一個元素不能被使用兩次,所以我們只需要在 x 后面的元素中尋找 target - x。

func twoSum(nums []int, target int) []int {
    numsLen := len(nums)
    for i, num := range nums {
        for j := i + 1; j < numsLen; j++ {
            if target == num + nums[j] {
                return []int{i, j}
            }
        }
    }
    return []int{}
}

執(zhí)行結(jié)果:

  • 時間復(fù)雜度:O(N^2),其中 N 是數(shù)組中的元素數(shù)量。最壞情況下數(shù)組中任意兩個數(shù)都要被匹配一次。
  • 空間復(fù)雜度:O(1)。

內(nèi)存方面還可以,不過運行時間偏慢, 時間復(fù)雜度太高了。

image.png

2、使用map方式, nums 的值進行倒排索引, 以空間換時間, 時間復(fù)雜度是O(n)

func twoSum(nums []int, target int) []int {
    numsMap := make(map[int]int)

    for index, num := range nums{
        numsMap[num] = index    
    }

    for index, num := range nums {
        if preIndex, ok := numsMap[target-num];  (ok && preIndex != index) {
            return []int{preIndex, index}
        } 
        
    }
    return []int{}
}

執(zhí)行結(jié)果:

時間復(fù)雜度:O(n),我們只遍歷了包含有O(n)個元素的列表兩次。在表中進行的每次查找只花費 O(1)的時間。
空間復(fù)雜度:O(n),所需的額外空間取決于哈希表中存儲的元素數(shù)量,該表最多需要存儲 N個 元素。

image.png

3、使用map方式, nums 的值進行倒排索引, 一遍哈希表

事實證明,我們可以一次完成。在進行迭代并將元素插入到表中的同時,我們還會回過頭來檢查表中是否已經(jīng)存在當(dāng)前元素所對應(yīng)的目標(biāo)元素。如果它存在,那我們已經(jīng)找到了對應(yīng)解,并立即將其返回。

func twoSum(nums []int, target int) []int {
    numsMap := make(map[int]int)
    for index, num := range nums {
        if preIndex, ok := numsMap[target-num]; ok {
            return []int{preIndex, index}
        }
        numsMap[num] = index
    }
    return []int{}
}

執(zhí)行結(jié)果:

時間復(fù)雜度:O(n),我們只遍歷了包含有O(n)個元素的列表一次。在表中進行的每次查找只花費 O(1)的時間。
空間復(fù)雜度:O(n),所需的額外空間取決于哈希表中存儲的元素數(shù)量,該表最多需要存儲 N個元素。

image.png

4、雙指針游標(biāo)

雙指針的思路

  • 將原數(shù)組排好序, 設(shè)置兩個指針left, right
  • left 從0開始, right 做 len(nums) - 1 開始
  • 當(dāng)nums[left] + nums[right] < target時,我們就沒有必要對所有的right0 ∈(left, right),因為nums[left] + nums[right0] 一定是比target小的, 所以left = left + 1后重新判斷
  • 當(dāng)nums[left] + nums[right] > target時,同樣的,對left0 ∈(left, right),必然有nums[left0] + nums[right] > target, 所以right = right - 1后重新判斷
  • 當(dāng)nums[left] + nums[right] = target時,就是我們想要知道的兩個數(shù)值了
  • 然后利用Map將數(shù)值轉(zhuǎn)換為之前的游標(biāo)
func twoSum(nums []int, target int) []int {
    type Node struct{
        Value int
        Next  *Node
    }
     // 用鏈表是解決數(shù)組重復(fù)問題
    numsMap := make(map[int]*Node)
    var current *Node
    for index, num := range nums{
        if _, ok := numsMap[num]; ok{
            current = numsMap[num]
            for {
                if current.Next == nil{
                    current.Next = &Node{index, nil}
                    break
                }

                current = current.Next
            }
        }else{
            numsMap[num] = &Node{index, nil}
        }
    }

    sort.Ints(nums)
    left := 0
    right := len(nums) - 1
    for {
        if left >= right{
            break
        }

        sum := nums[left] + nums[right]
        if sum == target{
            if nums[left] == nums[right]{
                return []int{numsMap[nums[left]].Value, numsMap[nums[left]].Next.Value}
            }else{
                return []int{numsMap[nums[left]].Value, numsMap[nums[right]].Value}
            }

        }

        if sum < target{
            left ++

        }else{
            right --
        }
    }
    return []int{}
}

執(zhí)行結(jié)果:

因為要排序,時間復(fù)雜度也比較高, 需要Map存儲游標(biāo),空間復(fù)雜度也比較高, 算是一個不太好的實現(xiàn)方式。

image.png

其他語言版本

C++

// 1. Two Sum
// 時間復(fù)雜度:O(n)
// 空間復(fù)雜度:O(n)
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> record;
        for(int i = 0 ; i < nums.size() ; i ++){
       
            int complement = target - nums[i];
            if(record.find(complement) != record.end()){
                int res[] = {i, record[complement]};
                return vector<int>(res, res + 2);
            }

            record[nums[i]] = i;
        }
        return {};
    }
};

執(zhí)行結(jié)果

[圖片上傳失敗...(image-51924b-1653220059347)]

C

// 1. Two Sum
// 時間復(fù)雜度:O(n)
// 空間復(fù)雜度:O(n)
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    int *ans=(int *)malloc(2 * sizeof(int));
    int i,j;
    bool flag=false; 
    for(i=0;i<numsSize-1;i++)
    {
        for(j=i+1;j<numsSize;j++)
        {
            if(nums[i]+nums[j] == target)
            {
                ans[0]=i;
                ans[1]=j;
                flag=true;
            }
        }
    }
    if(flag){
        *returnSize = 2;
    }
    else{
        *returnSize = 0;
    }
    return ans;
}

執(zhí)行結(jié)果

image.png

Java

// 1. Two Sum
// https://leetcode.com/problems/two-sum/description/
// 時間復(fù)雜度:O(n)
// 空間復(fù)雜度:O(n)
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int l = nums.length;
        int[] ans=new int[2];
        int i,j;
        for(i=0;i<l-1;i++)
        {
            for(j=i+1;j<l;j++)
            {
                if(nums[i]+nums[j] == target)
                {
                    ans[0]=i;
                    ans[1]=j;
                }
            }
        }
        
        return ans;
        
    }
}

執(zhí)行結(jié)果

image.png

Python

# 1. Two Sum
# https://leetcode.com/problems/two-sum/description/
# 時間復(fù)雜度:O(n)
# 空間復(fù)雜度:O(n)
class Solution(object):
    def twoSum(self, nums, target):
        l = len(nums)
        print(nums)
        ans=[]
        for i in range(l-1):
            for j in range(i+1,l):
                if nums[i]+nums[j] == target:
                    ans.append(i)
                    ans.append(j)
                    print([i,j])
                    break
        return ans

執(zhí)行結(jié)果

image.png

結(jié)語

綜合來看, 選擇方式3比較好: 使用map方式, nums 的值進行倒排索引, 一遍哈希表, Go的實現(xiàn)在內(nèi)存方面和時間復(fù)雜度都還可以, Python相對表現(xiàn)就差很多。

幾種語言運行效果對比

Go程序在內(nèi)存方面表現(xiàn)最佳, Python在內(nèi)存和運行時間表現(xiàn) 比較其他的語言都比較差。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內(nèi)容