LeetCode(15):三数之和
Medium!
題目描述:
給定一個包含?n?個整數的數組?nums,判斷?nums?中是否存在三個元素?a,b,c ,使得?a + b + c =?0 ?找出所有滿足條件且不重復的三元組。
注意:答案中不可以包含重復的三元組。
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4],滿足要求的三元組集合為: [[-1, 0, 1],[-1, -1, 2] ]解題思路:
這道題讓我們求三數之和,比之前那道Two Sum要復雜一些,考慮過先fix一個數,然后另外兩個數使用Two Sum那種HashMap的解法,但是會有重復結果出現,就算使用set來去除重復也不行,會TLE,看來此題并不是考我們Two Sum的解法。那么我們來分析一下這道題的特點,要我們找出三個數且和為0,那么除了三個數全是0的情況之外,肯定會有負數和正數,我們還是要先fix一個數,然后去找另外兩個數,我們只要找到兩個數且和為第一個fix數的相反數就行了,既然另外兩個數不能使用Two Sum的那種解法來找,如果能更有效的定位呢?我們肯定不希望遍歷所有兩個數的組合吧,所以如果數組是有序的,那么我們就可以用雙指針以線性時間復雜度來遍歷所有滿足題意的兩個數的組合。
我們對原數組進行排序,然后開始遍歷排序后的數組,這里注意不是遍歷到最后一個停止,而是遍歷到倒數第三個就可以停止了。這里我們可以先做個剪枝優化,就是當遍歷到正數的時候就break,為啥呢,因為我們的數組現在是有序的了,如果第一個要fix的數就是正數了,那么后面的數字就都是正數,就永遠不會出現和為0的情況了。然后我們還要加上重復就跳過的處理,處理方法是從第二個數開始,如果和前面的數字相等,就跳過,因為我們不想把相同的數字fix兩次。對于遍歷到的數,用0減去這個fix的數得到一個target,然后只需要再找到兩個數之和等于target即可。我們用兩個指針分別指向fix數字之后開始的數組首尾兩個數,如果兩個數和正好為target,則將這兩個數和fix的數一起存入結果中。然后就是跳過重復數字的步驟了,兩個指針都需要檢測重復數字。如果兩數之和小于target,則我們將左邊那個指針i右移一位,使得指向的數字增大一些。同理,如果兩數之和大于target,則我們將右邊那個指針j左移一位,使得指向的數字減小一些,代碼如下:
C++參考答案一:
1 class Solution { 2 public: 3 vector<vector<int>> threeSum(vector<int>& nums) { 4 vector<vector<int>> res; 5 sort(nums.begin(), nums.end()); 6 for (int k = 0; k < nums.size(); ++k) { 7 if (nums[k] > 0) break; 8 if (k > 0 && nums[k] == nums[k - 1]) continue; 9 int target = 0 - nums[k]; 10 int i = k + 1, j = nums.size() - 1; 11 while (i < j) { 12 if (nums[i] + nums[j] == target) { 13 res.push_back({nums[k], nums[i], nums[j]}); 14 while (i < j && nums[i] == nums[i + 1]) ++i; 15 while (i < j && nums[j] == nums[j - 1]) --j; 16 ++i; --j; 17 } else if (nums[i] + nums[j] < target) ++i; 18 else --j; 19 } 20 } 21 return res; 22 } 23 };或者我們也可以利用set的不能包含重復項的特點來防止重復項的產生,那么我們就不需要檢測數字是否被fix過兩次,不過個人覺得還是前面那種解法更好一些,參見代碼如下:
C++參考答案二:
1 class Solution { 2 public: 3 vector<vector<int>> threeSum(vector<int>& nums) { 4 set<vector<int>> res; 5 sort(nums.begin(), nums.end()); 6 for (int k = 0; k < nums.size(); ++k) { 7 if (nums[k] > 0) break; 8 int target = 0 - nums[k]; 9 int i = k + 1, j = nums.size() - 1; 10 while (i < j) { 11 if (nums[i] + nums[j] == target) { 12 res.insert({nums[k], nums[i], nums[j]}); 13 while (i < j && nums[i] == nums[i + 1]) ++i; 14 while (i < j && nums[j] == nums[j - 1]) --j; 15 ++i; --j; 16 } else if (nums[i] + nums[j] < target) ++i; 17 else --j; 18 } 19 } 20 return vector<vector<int>>(res.begin(), res.end()); 21 } 22 };知識點補充:
C++中set的用法set的特性是,所有元素都會根據元素的鍵值自動排序,set的元素不像map那樣可以同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。set不允許兩個元素有相同的鍵值。使用的時候要包含頭文件“#include<set>”。
set的各成員函數列表如下:
1. begin()--返回指向第一個元素的迭代器
2. clear()--清除所有元素
3. count()--返回某個值元素的個數
4. empty()--如果集合為空,返回true
5. end()--返回指向最后一個元素的迭代器
6. equal_range()--返回集合中與給定值相等的上下限的兩個迭代器
7. erase()--刪除集合中的元素
8. find()--返回一個指向被查找到元素的迭代器
9. get_allocator()--返回集合的分配器
10. insert()--在集合中插入元素
11. lower_bound()--返回指向大于(或等于)某值的第一個元素的迭代器
12. key_comp()--返回一個用于元素間值比較的函數
13. max_size()--返回集合能容納的元素的最大限值
14. rbegin()--返回指向集合中最后一個元素的反向迭代器
15. rend()--返回指向集合中第一個元素的反向迭代器
16. size()--集合中元素的數目
17. swap()--交換兩個集合變量
18. upper_bound()--返回大于某個值元素的迭代器
19. value_comp()--返回一個用于比較元素間的值的函數
1 #include<set> 2 #include<iostream> 3 using namespace std; 4 int main() 5 { 6 int i; 7 int arr[5] = {0,1,2,3,4}; 8 set<int> iset(arr,arr+5); 9 10 iset.insert(5); 11 cout<<"size:"<<iset.size()<<endl; 12 cout<<"3 count = "<<iset.count(3)<<endl; 13 iset.erase(1); 14 15 set<int>::iterator ite1 = iset.begin(); 16 set<int>::iterator ite2 = iset.end(); 17 for(;ite1!=ite2;ite1++) 18 { 19 cout<<*ite1; 20 } 21 cout<<endl; 22 23 ite1 = iset.find(3); 24 if(ite1!=iset.end()) 25 cout<<"3 found"<<endl; 26 27 ite1 = iset.find(1); 28 if(ite1!=iset.end()) 29 cout<<"1 not found"<<endl; 30 }?
轉載于:https://www.cnblogs.com/ariel-dreamland/p/9128122.html
總結
以上是生活随笔為你收集整理的LeetCode(15):三数之和的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: R12.2 新功能:值集安全性设置及效果
- 下一篇: ProceedingJoinPoint