X sum 類型題解題套路
刷leetcode大概碰到的第一道就是Two Sum了,當時迅速解完的一絲得意,不過也在后面看到3Sum,4Sum以后被耗光了,實際上,這類題是遵循某種規律的,下面就詳細來說說。
入門級別
從最簡單的Two Sum說起,題目詳見leetcode 1.Two Sum。
要從數組中找到某兩個相加為target
的元素的下標,常規做法想到的是O(n^2)
的做法,兩個for
循環,對每個數組元素a
都遍歷位置在它后面的元素看是否有元素和a
相加結果為target
。
一種用空間換時間的做法就是,只需要一次遍歷數組,在遍歷的過程中把當前元素和target之間差的值作為key
,當前元素下標i
記錄到哈希表中,在每次訪問元素前首先需要在該哈希表中查找當前元素是否已經作為之前某個元素的diff
存放在哈希表中了,如果是,直接返回答案即可。
注:在C++中推薦使用
unordered_map
作為哈希表(替代原來的hash_map
)
有必要提到的一點是,如果這道題不是要求返回相加為target
的數組的下標,而是返回所有相加為target
的數組集合(改成這樣是為了和下面的3Sum和4Sum做鋪墊),也可以按照以下思路來求解。
先對數組按照從小到大的順序排序,然后使用兩個指針分別指向數組的頭和尾,分為以下三種情況:
- 若nums[start]+nums[end]<target,表示nums[start]過小,所以進行
start++
操作。 - 若nums[start]+nums[end]>target,表示nums[end]過大,所以進行
end--
操作。 - 如果nums[start]+nums[end]==target,則找到了一組結果,同時
start++
,end—
,并跳過循環重復值,避免結果冗余。
這樣最終得到的就是結果相加為target的數組集合。
最后提到的這種思路的具體應用可參考leetcode 167. Two Sum II - Input array is sorted
3Sum
在Two Sum的基礎上引入3Num,題目詳見leetcode 15. 3Sum
要在給定數組中找到所有三個元素相加為0的組合,且結果不能有重復。
常規做法,需要三層循環,時間復雜度為O(n^3)
實際上按照Two Sum最后給出的拓展思路,可以很容易將這道題轉換為,從數組中選出一個元素nums[i],然后就變成了target為-nums[i]的Two Sum問題。排序后按之前的思路進行即可。由于數組是排序后的,所以在最外層循環的時候只需要循環nums中小于0的部分元素即可。一定要注意跳過重復值,避免結果中出現冗余。這種做法,預處理數組(即對數組進行排序)的時間復雜度為O(nlogn),而遍歷只需要兩層循環,時間復雜度為O(n^2)
即這也是該解法最終的時間復雜度。
3Sum問題還有一變形,詳見3Sum Closest,大致思路類似,就不贅述了,主要就是保存和target差距diff,最終得到diff最小的一個解,不過要注意的是由于這里target不為0了,所以在最外層循環時也就不能只處理小于0的部分了。
4Num
在3Sum的基礎上引入4Sum問題,題目詳見leetcode 18. 4Sum
給定一個數組,返回所有四個元素相加為target的不重復組合,實際上和3Sum的套路完全一樣,就是把4Sum轉換為3Sum再轉換為2Sum,最終返回正確答案。利用這種方法就可以把原本暴力解的復雜度為O(n^4)
轉化為最終的復雜度為O(n^3)
總結
實際上X Sum問題沒有想象中的那么難,只要掌握的它的規律,進行一個預排序后,就可以一層層的向下轉換,即將X Sum轉為(x-1) Sum...最終轉為2Sum后得到最終答案,不過一定要注意跳過重復元素,否則會造成最終結果的冗余。