圖解兩數之和:雜湊表法

王亮hengg發表於2020-08-10

image

兩數之和是一道非常經典,也非常高頻的面試題,題目大意如下:

給定一個整數陣列nums 和一個目標值target,請你在該陣列中找出和為目標值的那兩個整數,並返回他們的陣列下標。
case:
給定 nums = [2, 1, 7, 11, 15], target = 9
因為 nums[0] + nums[2] = 2 + 7 = 9
所以返回 [0, 2]

今天我們就一起探討一下這道題的解法。

太長不看版

  • 可以通過暴力運算,遍歷nums中的每一個元素,查詢陣列剩餘部分是否有匹配的值;
  • 更高效的方式是利用雜湊表key唯一且訪問快的特性,建立map儲存未命中的值。遍歷nums中的元素,查詢map上是否有匹配的目標值,否則將當前元素儲存到map上。

暴力運演算法

暴力運演算法很簡單,通過首層for迴圈遍歷陣列中的每個元素curr,再通過另外一層for迴圈尋找target - curr值。

程式碼如下:

image

雙層for迴圈導致暴力運演算法時間複雜度為 O(n2),在2020年的今天著實不能令大部分人滿意。

雜湊表法

雜湊表(也叫雜湊表)是一種資料結構,其定義如下:

雜湊表是根據關鍵字(Key value)而直接訪問在記憶體儲存位置的資料結構。

我們可以把它理解為詞典,詞典裡的每個詞條都是唯一的,在這個詞條後面記錄著詞條的含義。就像查詞典時我們能夠很快速的定位到詞條,雜湊表的訪問速度也非常快,其時間複雜度為O(1)。

JavaScript中常規的鍵值對物件就是雜湊表的實現。

接下來我們就結合題目中的case通過圖解的方式來說明雜湊表法如何計算兩數之和。

0.初始化

初始化時我們會拿到nums陣列[2, 1, 7, 11, 15]target9,同時map初始化為空物件{}用於存放未匹配成功的鍵值對:


image


接下來將遍歷nums陣列。

1.開始遍歷陣列

遍歷開始,此時:

  • 當前map為空{}
  • 當前索引值index0;
  • 當前索引對應的數值curr2;
  • 我們希望找到target - currneed值為7

image


因為此時map{},所以map[need]值為undefined


image


2.儲存未匹配成功的值至map

因為所求為兩數之和,所以哪怕nums[0]的值等於target,迭代第一步必然匹配失敗。


image


此時將未能成功匹配的currindex存放到map中。這一步非常重要,是整個解法的關鍵:

map[curr] = index;

這裡使用curr做為key,因為我們需要返回的結果是配對成功的數字其下標所構成的陣列,匹配時是在map查詢數字、返回下標。

3.繼續遍歷陣列

匹配未成功,重複第一步,繼續遍歷陣列:


image


同樣未找到期望值need,繼續將currindex寫入map


image


繼續重複此步驟直至匹配成功。

4.匹配成功!

繼續遍歷nums,此時need2curr7,終於在map中查詢到了need,隔空會師成功!


image


返回結果:[map[need], index]map[need]在前的原因是map裡儲存的值,其下標一定在curr對應的下標之前。

5.完整示例程式碼

image

  • 雜湊解法每次查詢的時間複雜度是O(1),n次查詢的時間複雜度O(n);
  • 空間複雜度也是O(n),map上最多存n個元素。

小結

  • 雙層for迴圈暴力運算簡單直觀,時間複雜度O(n2)、空間複雜度O(1);
  • 雜湊表法時間複雜度和空間複雜度都是O(n);
  • 考察點是對雜湊表這種資料結構的熟悉程度;
  • 多一種解法就多一分勝算;
  • 整體難度不高。

一入JS深似海,希望這個專欄能在你乘風破浪的旅途中有所幫助。歡迎關注我的公眾號:「JS漫步指南」,更多精彩等待您發現!

image

相關文章