双指针解决力扣两/三数之和问题
生活随笔
收集整理的這篇文章主要介紹了
双指针解决力扣两/三数之和问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
雙指針解決力扣兩/三數之和問題
文章目錄
- 雙指針解決力扣兩/三數之和問題
- 一、問題描述
- 二、分析
- 1.暴力
- 2.排序+雙指針法
- 3.hash法
- 三、問題描述
- 四、分析
- 方法一:排序 + 雙指針
- 五、代碼
一、問題描述
二、分析
1.暴力
暴力算法時間復雜度O(n2),空間復雜度O(1)
class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> ans;for(int i = 0;i < nums.size();i++){for(int j = i + 1;j < nums.size();j++){if(nums[i] + nums[j] == target){ans.push_back(i);ans.push_back(j);return ans;}}}return ans;} };2.排序+雙指針法
- 這里先將數組排序好O(nlogn),再利用雙指針法遍歷一遍O(n)得到結果。
- 為了保存下標信息另開了一個數組
- 時間復雜度O(nlogn),空間復雜度O(n)
3.hash法
- 利用map數組構造映射,遍歷nums[i]時,看target-nums[i]是否存在hash表中即可
- 時間復雜度O(n),空間復雜度O(n)
三、問題描述
四、分析
- 本題與 兩數之和 類似,是非常經典的面試題,但是做法不盡相同。
方法一:排序 + 雙指針
- 題目中要求找到所有「不重復」且和為 0 的三元組,這個「不重復」的要求使得我們無法簡單地使用三重循環枚舉所有的三元組。
這是因為在最壞的情況下,數組中的元素全部為 0,即
[0, 0, 0, 0, 0, …, 0, 0, 0]
- 任意一個三元組的和都為 0。如果我們直接使用三重循環枚舉三元組,會得到 O(N3)O(N^3)O(N3)個滿足題目要求的三元組(其中 N 是數組的長度)時間復雜度至少為 O(N3)O(N^3)O(N3)
- 在這之后,我們還需要使用哈希表進行去重操作,得到不包含重復三元組的最終答案,又消耗了大量的空間。
- 這個做法的時間復雜度和空間復雜度都很高,因此我們要換一種思路來考慮這個問題。
- 「不重復」的本質是什么?我們保持三重循環的大框架不變,只需要保證:
- 第二重循環枚舉到的元素不小于當前第一重循環枚舉到的元素;
- 第三重循環枚舉到的元素不小于當前第二重循環枚舉到的元素。
- 也就是說,我們枚舉的三元組 (a, b, c)滿足a≤b≤c,保證了只有 (a, b, c)這個順序會被枚舉到,而 (b, a, c)、(c, b, a)等等這些不會,這樣就減少了重復。
- 要實現這一點,我們可以將數組中的元素從小到大進行排序,隨后使用普通的三重循環就可以滿足上面的要求。
- 同時,對于每一重循環而言,相鄰兩次枚舉的元素不能相同,否則也會造成重復。舉個例子,如果排完序的數組為
- 我們使用三重循環枚舉到的第一個三元組為(0,1,2),如果第三重循環繼續枚舉下一個元素,那么仍然是三元組(0,1,2),產生了重復。
- 因此我們需要將第三重循環「跳到」下一個不相同的元素,即數組中的最后一個元素 3,枚舉三元組 (0, 1, 3)。
- 下面給出了改進的方法的偽代碼實現:
- 這種方法的時間復雜度仍然為 O(N^3),畢竟我們還是沒有跳出三重循環的大框架。
- 然而它是很容易繼續優化的,可以發現,如果我們固定了前兩重循環枚舉到的元素 a 和 b,那么只有唯一的 c 滿足 a+b+c=0。
- 當第二重循環往后枚舉一個元素 b'時,由于 b’ > b ;那么滿足 a+b’+c’=0的 c' 一定有 c' < c,即 c'在數組中一定出現在 c 的左側。
- 也就是說,我們可以從小到大枚舉 b,同時從大到小枚舉 c,即第二重循環和第三重循環實際上是并列的關系。
- 有了這樣的發現,我們就可以保持第二重循環不變,而將第三重循環變成一個從數組最右端開始向左移動的指針,從而得到下面的偽代碼:
- 這個方法就是我們常說的「雙指針」,當我們需要枚舉數組中的兩個元素時,如果我們發現隨著第一個元素的遞增,第二個元素是遞減的,那么就可以使用雙指針的方法,將枚舉的時間復雜度從O(N^2) 減少至 O(N)。
- 為什么是 O(N)呢?這是因為在枚舉的過程每一步中,「左指針」會向右移動一個位置(也就是題目中的 b),而「右指針」會向左移動若干個位置,這個與數組的元素有關,但我們知道它一共會移動的位置數為 O(N),均攤下來,每次也向左移動一個位置,因此時間復雜度為 O(N)。
- 注意到我們的偽代碼中還有第一重循環,時間復雜度為 O(N)O(N),因此枚舉的總時間復雜度為 O(N^2)
- 由于排序的時間復雜度為 O(NlogN),在漸進意義下小于前者,因此算法的總時間復雜度為 O(N^2)
- 上述的偽代碼中還有一些細節需要補充,例如我們需要保持左指針一直在右指針的左側(即滿足 b≤c),具體可以參考下面的代碼,均給出了詳細的注釋。
五、代碼
class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size();sort(nums.begin(), nums.end());vector<vector<int>> ans;// 枚舉 afor (int first = 0; first < n; ++first) {// 需要和上一次枚舉的數不相同if (first > 0 && nums[first] == nums[first - 1]) {continue;}// c 對應的指針初始指向數組的最右端int third = n - 1;int target = -nums[first];// 枚舉 bfor (int second = first + 1; second < n; ++second) {// 需要和上一次枚舉的數不相同if (second > first + 1 && nums[second] == nums[second - 1]) {continue;}// 需要保證 b 的指針在 c 的指針的左側while (second < third && nums[second] + nums[third] > target) {--third;}// 如果指針重合,隨著 b 后續的增加// 就不會有滿足 a+b+c=0 并且 b<c 的 c 了,可以退出循環if (second == third) {break;}if (nums[second] + nums[third] == target) {ans.push_back({nums[first], nums[second], nums[third]});}}}return ans;} }; 超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的双指针解决力扣两/三数之和问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 力扣--91. 解码方法
- 下一篇: 力扣四数之和