兩數之和是一道非常經典,也非常高頻的面試題,題目大意如下:
給定一個整數陣列nums
和一個目標值target
,請你在該陣列中找出和為目標值的那兩個整數,並返回他們的陣列下標。
case:
給定nums = [2, 1, 7, 11, 15], target = 9
因為nums[0] + nums[2] = 2 + 7 = 9
所以返回[0, 2]
之前我們探討了這個問題的暴力運演算法和雜湊表法,今天我們使用雙指標法來解決它。
太長不看版
- 首先排序陣列;
- 使用
left
,right
兩個指標; - 比較
target
與left
值加right
值的和,移動對應的指標; - 雙指標解法的時間複雜度取決於對應的排序演算法,空間複雜度為O(n)。
什麼是雙指標?
雙指標和快速排序、氣泡排序等具體演算法不同。它更接近於一種思(tào)路,一種使用兩個指標互相配合來儲存節點以便於運算的技巧。
雙指標法適用於陣列、連結串列等線性資料結構,常用的思路有:碰撞指標、滑動視窗、快慢指標等。
在兩數之和這個case中我們使用碰撞指標的方式來實現,其它兩種套路會在後續文章中介紹。
0.排序
所謂碰撞指標,是指在有序陣列中定義left
(陣列起始位置)、right
(陣列終止位置)兩個指標,在遍歷時根據對應條件的不同來判斷應該移動哪個指標,進而從陣列兩端遍歷陣列。
所以在兩數之和中我們需要先將目標陣列進行排序:
排序演算法的時間複雜度決定了整個計算的時間複雜度。因為雙指標遍歷的複雜度是O(n)。
1.建立雙指標
在排好序的陣列(以下簡稱陣列)兩端分別建立left
、right
指標:
2.左移右指標
此時left
值與right
值之和(以下簡稱sum
)大於target
,此時應該將right
左移一位,減小sum
使其更接近target
。
從這裡就可以看出,為什麼對有序陣列才適用碰撞指標。
3.繼續遍歷陣列
在這個case中我們需要繼續遍歷陣列,直到right
指標指向7,此時sum
小於target
:
4.右移左指標
與步驟2類似,當sum
小於target
時我們需要右移左指標,增加sum
值使得兩者更加接近:
5.匹配成功!
在當前case中,left
指向2時sum
與target
相等,匹配成功!
此時返回left
值和right
值在原陣列中的下標即可:
6.完整示例程式碼
示例程式碼如下:
需要注意的是,由於JavaScript引用型別的特性,我們首先拷貝了nums
,才使用Array.sort
對拷貝陣列進行排序。
另外,對於nums=[1,2,2,3],target=4
這種case,其期望的返回值是[1,2]
而不是[1,1]
或者[2,2]
。所以這裡我們使用了Array.lastIndexOf()
這個API。
小結
- 採用碰撞雙指標進行運算;
- 碰撞雙指標運算的時間複雜度取決於具體的排序演算法,空間複雜度為O(n);
- 碰撞指標需要對陣列進行排序;
- 排序時注意不要汙染原陣列;
- 返回結果需要考慮陣列含有相同項;
再複習一下暴力運演算法和雜湊表法:
- 雙層
for
迴圈暴力運算簡單直觀,時間複雜度O(n2)、空間複雜度O(1); - 雜湊表法時間複雜度和空間複雜度都是O(n);
- 考察點是對雜湊表這種資料結構的熟悉程度;
- 多一種解法就多一分勝算;
- 整體難度不高。
一入JS深似海,希望這個專欄能在你乘風破浪的旅途中有所幫助。歡迎關注我的公眾號:「JS漫步指南」,更多精彩等待您發現!