日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

双指针解决力扣两/三数之和问题

發布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 双指针解决力扣两/三数之和问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

雙指針解決力扣兩/三數之和問題

文章目錄

  • 雙指針解決力扣兩/三數之和問題
    • 一、問題描述
    • 二、分析
      • 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)
class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> ret(2);vector<int> temp(nums);sort(nums.begin(),nums.end());int left = 0;int right = nums.size() - 1;while(left < right){if(nums[left] + nums[right] == target){int flag = 1;for(size_t i = 0;i < temp.size();i++){if(temp[i] == nums[left] && flag){ret[0] = i;flag = 0;}else if(temp[i] == nums[right]){ret[1] = i;}}break;}else if(nums[left] + nums[right] > target){right--;}else {left++;}}return ret;} };

3.hash法

  • 利用map數組構造映射,遍歷nums[i]時,看target-nums[i]是否存在hash表中即可
  • 時間復雜度O(n),空間復雜度O(n)
class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> ans;map<int,int>hashmap;for(int i = 0;i < nums.size();i++){if(hashmap[target - nums[i]] && hashmap[target - nums[i]] != i + 1){//防止利用同個元素ans.push_back(i);ans.push_back(hashmap[target - nums[i]] - 1);return ans;}hashmap[nums[i]] = i + 1;//將hash表對應下標+1,防止處理下標為0的情況}return ans;} };

三、問題描述

四、分析

  • 本題與 兩數之和 類似,是非常經典的面試題,但是做法不盡相同。

方法一:排序 + 雙指針

  • 題目中要求找到所有「不重復」且和為 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, 2, 2, 3]
  • 我們使用三重循環枚舉到的第一個三元組為(0,1,2),如果第三重循環繼續枚舉下一個元素,那么仍然是三元組(0,1,2),產生了重復。
  • 因此我們需要將第三重循環「跳到」下一個不相同的元素,即數組中的最后一個元素 3,枚舉三元組 (0, 1, 3)。
  • 下面給出了改進的方法的偽代碼實現:
nums.sort() for first = 0 .. n-1// 只有和上一次枚舉的元素不相同,我們才會進行枚舉if first == 0 or nums[first] != nums[first-1] thenfor second = first+1 .. n-1if second == first+1 or nums[second] != nums[second-1] thenfor third = second+1 .. n-1if third == second+1 or nums[third] != nums[third-1] then// 判斷是否有 a+b+c==0check(first, second, third)
  • 這種方法的時間復雜度仍然為 O(N^3),畢竟我們還是沒有跳出三重循環的大框架。
  • 然而它是很容易繼續優化的,可以發現,如果我們固定了前兩重循環枚舉到的元素 a 和 b,那么只有唯一的 c 滿足 a+b+c=0。
  • 當第二重循環往后枚舉一個元素 b'時,由于 b’ > b ;那么滿足 a+b’+c’=0的 c' 一定有 c' < c,即 c'在數組中一定出現在 c 的左側。
  • 也就是說,我們可以從小到大枚舉 b,同時從大到小枚舉 c,即第二重循環和第三重循環實際上是并列的關系。
  • 有了這樣的發現,我們就可以保持第二重循環不變,而將第三重循環變成一個從數組最右端開始向左移動的指針,從而得到下面的偽代碼:
nums.sort() for first = 0 .. n-1if first == 0 or nums[first] != nums[first-1] then// 第三重循環對應的指針third = n-1for second = first+1 .. n-1if second == first+1 or nums[second] != nums[second-1] then// 向左移動指針,直到 a+b+c 不大于 0while nums[first]+nums[second]+nums[third] > 0third = third-1// 判斷是否有 a+b+c==0check(first, second, third)
  • 這個方法就是我們常說的「雙指針」,當我們需要枚舉數組中的兩個元素時,如果我們發現隨著第一個元素的遞增,第二個元素是遞減的,那么就可以使用雙指針的方法,將枚舉的時間復雜度從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年碼齡,通宵達旦的技術人生

總結

以上是生活随笔為你收集整理的双指针解决力扣两/三数之和问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。