LeetCode 两数之和、三数之和、四数之和

LeetCode No.1 两数之和


方法一暴力解决法

遍历每一个元素,查找出两个数之和能够等于target的。
并且用vector存储下来,用于返回对应的下标。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> vec;
        int n = nums.size();
        for(int i =0;i<n;i++){
            for(int j = i;j<n;j++){
                if(nums[i]+nums[j] == target && i!=j ){
                    vec.push_back(i);
                    vec.push_back(j);
                }
            }
        }return vec;
    }
};

时间复杂度O(n²);
空间复杂度O(1);
暴力法虽然思路快,但是也仅仅能应用于两数之和上,若要是三数之和、四数、五数的话,时间复杂度会很大。

LeetCode No.1 暴力解决法用时 556ms。


方法二:哈希表法
为了降低时间复杂度,我们可以利用map来记录对应数值的数标,并且在进行时回头查找是否有符合条件的数出现。
targer - nums[i]寻找的就是第二个可能符合条件的数。
(所有事先存储到map里的数都可能成为第一个符合条件的数)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        vector<int> vec(2);
        for(int i = 0;i<nums.size();i++){
           if(map.count(target - nums[i])){//这里也可以用map.find,但是我喜欢count
               vec[0] = map[target - nums[i]];
               vec[1] = i;
           }map[nums[i]] = i;
        }return vec;
    }
};

时间复杂度O(n);
空间复杂度O(n);
所需要的空间大小为map的元素个数,此处为nums.size()

LeetCode No.1 哈希表法用时 20ms。


优化
在上述哈希表法中利用了vector进行存储和最后的返回,但是返回的仅仅是两个数而已,所以我们尽量争取把这点空间节省掉。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        for(int i=0;i<nums.size();i++){
            if(map.count(target-nums[i]))
                return {map[target-nums[i]],i};
            map[nums[i]]=i;
        }return {};
    }
};

时间复杂度O(n);
空间复杂度O(n);

LeetCode No.1 哈希表法2.0版本用时 8ms。


LeetCode No.15 三数之和

方法:双指针法
当做完了“两数之和”后,第一眼看到“三数之和”后的第一反应就是“这不都是一个题么?!”
没错!“两数之和”、“三数之和”就是一个题,只不过后者的target变成了可变的。vector从一维变成了二维的。
并且第一反应就是双指针法(一头一尾)来解决的。
那么延续上一题的思路写出下面的代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> > vec;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for (int a = 0; a < n-2; ++a) {//a<n、a<n-2都可以
            if (nums[a] > 0) break;
            if (a>0 && nums[a] == nums[a-1]) continue;
            int target = 0 - nums[a];
            int i = a+1;
            int j = n-1;
            while(i<j){
                if(nums[i]>target) break;
                if(nums[i]+nums[j] == target){
                    vector<int> vv;
                    vv.push_back(nums[i]);
                    vv.push_back(nums[j]);
                    vv.push_back(-target);
                    vec.push_back(vv);
                    i++;
                    }
                else if (j<n-1 && nums[j] == nums[j+1] || nums[i]+nums[j]>target) --j;
                else if (i>a+1 && nums[i] == nums[i-1] || nums[i]+nums[j]<target) ++i;
                }
        }
        vec.erase(unique(vec.begin(),vec.end()),vec.end());//[0,0,0,0]
        return vec;
    }
};

但是做完之后发现,其实相对“两数之和”来说,“三数之和”还是多了挺多细节的。

  • 要用双指针法的话就一定要先排好序,sort(nums.begin(),nums.end());是必要的!
    但是注意sort后的第一位要小于0;并且数组里面会有重复的数字,当target选则了相同的数字后会出现多个相同的解,所以直接跳过continue即可。
  • 然后就是双指针法(一头一尾)的模板了:
if(+== 目标) 记录;
else if(+> 目标)--//(j<n-1 && nums[j] == nums[j+1] || nums[i]+nums[j]>target) --j;
else if(+< 目标)++//(i>a+1 && nums[i] == nums[i-1] || nums[i]+nums[j]<target) ++i;

逻辑运算符的顺序不要写错,否则会越界!

  • 最后的ve.erase去重是因为发现[0,0,0,0]的结果有重复。

LeetCode Node No.15 用时 116ms。


优化
仔细想一想,其实target是可以省略的,因为绝大部分的判断好像都和target有关。
并且vector<int>vv也是可以省略的。(真想不明白当时为什么要这个vv)
while循环处参考了LeetCode No.15 pinku-2的题解。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> > vec;
        sort(nums.begin(), nums.end());
        int n = nums.size();
        for (int a = 0; a < n-2; ++a) {
            if (a>0 && nums[a] == nums[a-1]) continue;
            int i = a+1;
            int j = n-1;
            while(i<j) {
                int sum = nums[a]+nums[i]+nums[j];
                if (sum == 0){
                    vec.push_back({nums[a],nums[i],nums[j]});
                    while(i>j && nums[i] == nums[++i]);
                    while(i<j && nums[j] == nums[--j]);
                }else if (sum>0) {
                    --j;
                }else if (sum<0) {
                    ++i;
                }
            }
        }
        return vec;
    }
};

LeetCode Node No.15 用时 48ms(在所有 C++ 提交中击败了97.17%的用户)


LeetCode No.18 四数之和

在“三数之和”上再加一层循环即可。

class Solution{
public: 
	vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int> > res;
        sort(nums.begin(),nums.end());
        int n=nums.size();
        for(int a=0;a<n-3;a++){
        	if(a>0 && nums[a]==nums[a-1]) continue;   
        	for(int b=a+1;b<n-2;b++){
        		if(b>a+1 && nums[b]==nums[b-1])continue;
        		int i=b+1;
                int j=n-1;
        		while(i<j){
        			if(nums[a]+nums[b]+nums[i]+nums[j] == target){
        				res.push_back({nums[a],nums[b],nums[i],nums[j]});
        				while(i>j && nums[i] == nums[++i]);
                        while(i<j && nums[j] == nums[--j]);
					}
        			else if(nums[a]+nums[b]+nums[i]+nums[j]<target)
        			    i++;
        			else if(nums[a]+nums[b]+nums[i]+nums[j]>target)
        			    j--;
				}
			}
		}return res;
    }
};

LeetCode Node No.18 用时 40ms(在所有 C++ 提交中击败了72.17%的用户)

发布了4 篇原创文章 · 获赞 0 · 访问量 64

猜你喜欢

转载自blog.csdn.net/weixin_45181646/article/details/104806206