兩數之和(easy)
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
方法一:暴力求解
很自然的方法就是依次遍歷數組中每個數,在剩下的數中找是否存在和爲target的數,返回他們的下標。代碼如下:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
ret.clear();
if(nums.size() == 0){
return ret;
}
for(int i=0; i<nums.size()-1; ++i){
for(int j=i+1; j<nums.size(); ++j){
if(nums[i] + nums[j] == target){
ret.push_back(i);
ret.push_back(j);
return ret;
}
}
}
return ret;
}
};
時間複雜度O(n^2)
方法二:hash表
利用一個哈希表,通過O(n)的空間消耗,將時間複雜度降低到O(n)。
哈希表的key存放數組的值,value是對應的下標。
遍歷數組,在哈希表中查找是否存在和爲target的數,不存在就將當前數加入哈希表,存在就返回value值(即數組中的下標)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
ret.clear();
unordered_map<int, int> hash;
for(int i=0; i <nums.size(); ++i){
int findValue = target-nums[i];
if(hash.find(findValue) != hash.end()){
ret.push_back(i);
ret.push_back(hash[findValue]);
return ret;
}else{
hash[nums[i]] = i;
}
}
return ret;
}
};
關鍵是對unordered_map這個類型的熟悉使用。
時間複雜度降低了很大
三數之和(medium)
在上面的問題基礎上引入三數之和
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
很自然的也想到用遍歷的方法去尋找,自然時間複雜度是O(n^3),但是要求不能包含重複的三元組,這個在判斷上就增加了複雜程度。
看到“避免重複”,可以想到將數組先進行排序,這樣在遍歷的時候,可以通過比較前後兩個值的大小,跳過對重複數字的判斷。
方法一:排序後暴力法
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> result;
result.clear();
for(int i = 0; i<n-1; ++i){
if(i>0 && nums[i]==nums[i-1]){
continue;
}
for(int j=i+1; j<n-1; ++j){
if(j>i+1 && nums[j]==nums[j-1]){
continue;
}
for(int k=j+1; k<n; ++k){
if(k>j+1 && nums[k]==nums[k-1]){
continue;
}
if(nums[i]+nums[j]+nums[k] ==0){
result.push_back({nums[i], nums[j], nums[k]});
}
}
}
}
return result;
}
};
通過nums[j]==nums[j-1]來跳過對重複數字的遍歷,但時間複雜度O(n^3),一般不滿足要求。
方法二:排序後雙指針法
排序後數字是以從小到大的順序排序,那麼在n[i]確定的情況下,如果n[j]+n[k]> -n[i],那麼n[j]+n[k+1]肯定是大於-n[i]的,k只能朝數組左邊移動。所以在從左到右遍歷第二個數的時候,同時從右到左遍歷第三個數,將兩重遍歷合併爲一重遍歷。同時要注意邊界條件是j=k。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> result;
result.clear();
for(int i = 0; i<n-1; ++i){
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int k = n-1;
int target = -nums[i];
for(int j=i+1; j<n-1; ++j){
if(j>i+1 && nums[j]==nums[j-1]){
continue;
}
while(j<k && nums[j]+nums[k]> target){
--k;
}
if(j == k){
break;
}
if(nums[j]+nums[k] == target){
result.push_back({nums[i], nums[j], nums[k]});
}
}
}
return result;
}
};
時間複雜度:排序O(nlogn)+ 遍歷O(n^2),一共O(n^2)。