leetcode-1-15-16-18-Ksum

本文深入探讨了从两数之和到四数之和的问题解决策略,包括排序、哈希表和链式哈希表的应用。详细解释了如何在不同场景下优化算法效率,特别关注了处理重复数字和多组解的复杂情况。通过实例代码展示了每种解法的实现细节,旨在提升读者在面对数组操作和组合问题时的编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

leetcode1—2 sum
给定一个target值,在一个序列里面找到两个和为target的数,按顺序返回它们的下标。给出的序列有且仅有一组这样的数。
解法:
  1. 题目假定只有一个解,大大降低了难度
(1)先排序再移动头尾指针缩小范围O(NlgN)
(2)哈希表、unordered_map O(N)
  1. 数组里没有重复数字
(1)先排序再移动头尾指针缩小范围:与只有一个解的方法一样,只是循环的时候不能break,O(NlgN)
(2)哈希表、unordered_map:也是和只有一个解的方法一样,只是循环的时候不能breakO(N)
  1. 数组里有重复数字
这个是2sum问题的一般情况,也是最为复杂的一种假设,不过可以转换为数组没有重复数组的情况求解,这是大概的思路。
(1)首先用一个新的vector存放去重之后的数组,因为最后要返回的是下标,所以要保存所有数字的下标,这样就要用一个链式哈希表存放数字的下标,可以用<int, list<int>>类型的unordered_map实现。O(N)
(2)接着用数组里没有重复数字的移动头尾指针的方法解决,得到所有的value pair。O(N)
(3)再根据这些value pair在链式哈希表里面找出所有的下标。O( N 2)
/*
 用暴力搜索就要O(n)或者至少是O(nlgn),似乎会超时。
 如果用hash的话就能保证O(n),注意要保存元素的下标,
 所以用unordered_map,还要注意两个下标要按从小到大
 的顺序。
 因为结果只可能有一对下标,所以相同的数字最多有两个,
 由于存入unordered_map的时候相同数字的下标会覆盖,
 所以比较下标的时候保险就是一个用nums的下标,一个用查找
 unordered_map返回的下标。
 */
/*
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> mymap;
        vector<int> inds;
        for (int i = 0; i < nums.size(); i++) {
            mymap.insert(make_pair(nums[i], i));
        }
        for (int i = 0; i < nums.size(); i++) {
            int gap = target - nums[i];
            if (mymap.find(gap) != mymap.end()) {
                if (i > mymap[gap]) {
                    inds.push_back(mymap[gap]);
                    inds.push_back(i);
                    break;
                } else if (i < mymap[gap]){    //一定是两个不同的下标
                    inds.push_back(i);
                    inds.push_back(mymap[gap]);
                    break;
                }
            }
        }
        return inds;
    }
};
 */
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> mymap;
        vector<int> inds;
        for (int i = 0; i < nums.size(); i++) {
            mymap.insert(make_pair(nums[i], i));
        }
        for (int i = 0; i < nums.size(); i++) {
            int gap = target - nums[i];
            if (mymap.find(gap) != mymap.end()) {
                if (i > mymap[gap]) {
                    inds.push_back(mymap[gap]);
                    inds.push_back(i);
                    break;
                }
            }
        }
        return inds;
    }
};
int main(int argc, const char * argv[]) {
    Solution s;
    vector<int> n = {0,4,3,0};
    vector<int> result = s.twoSum(n, 0);
    for (int i = 0; i < result.size(); i++) {
        cout << result[i] << " ";
    }
    cout << endl;
    return 0;
}



leetcode15—3sum
在一个序列里面找出那三个数的和是0的,按顺序返回数字就可以了,不要求返回下标。结果可能会有多组
大概的思路就是将3sum转换为2sum,具体的做法:
  1. 首先排好序O(NlgN)并且去重(因为不需要返回下标)
  2. 遍历数组,寻找该次迭代要放在等号左边的数字;
  3. 确定好放在等号左边的数字之后,对之后的数字算2sum。
去重:
A + B + C = target   —>  B + C = target - A
target - A = B + X
target - B = A + X
已经放过等号左边的数字就不要再放在等号右边了,也就是说算2sum的时候不要考虑那些已经放过等号左边的数字。注意数组里可能有重复的数字。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> perm;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            //这里也是去重,target不会是重复的数字
            while (i >0 && nums[i - 1] == nums[i]) {
                i++;
            }
            int target = 0 - nums[i];
            int left = i + 1, right = int(nums.size()) - 1;
            while (left < right) {
                if (nums[left] + nums[right] < target) {
                    left++;
                } else if (nums[left] + nums[right] > target) {
                    right--;
                } else {
                    vector<int> match = {nums[i], nums[left], nums[right]};
                    perm.push_back(match);
                    //去重,下一个一定是不一样的数
                    while (nums[left + 1] == nums[left]) {
                        left++;
                    }
                    while (nums[right - 1] == nums[right]) {
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }
        return perm;
    }
};
int main(int argc, const char * argv[]) {
    vector<int> test = {-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0};
    Solution s;
    vector<vector<int>> res = s.threeSum(test);
    for (int i = 0; i < res.size(); i++) {
        for (int j = 0; j < 3; j++) {
            cout << res[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}



leetcode16 — 3sum closet
给定一个target,在一个序列里面找出三个数的和是最接近那个target的要求返回这三个数的和就可以了,给出的序列有且仅有一组这样的数。
想了一下,思路应该和3um的一样,只是要多一个全局变量来记录差值绝对值和对应的sum,最后返回sum就好了。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int error = 2147483647;
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            //这里也是去重,target不会是重复的数字
            while (i >0 && nums[i - 1] == nums[i]) {
                i++;
            }
            int t = target - nums[i];
            int left = i + 1, right = int(nums.size()) - 1;
            while (left < right) {
                if (nums[left] + nums[right] < t) {
                    int less = t - nums[left] - nums[right];
                    if (less < error) {
                        error = less;
                        sum = nums[i] + nums[left] + nums[right];
                    }
                    left++;
                } else if (nums[left] + nums[right] > t) {
                    int larger = nums[left] + nums[right] - t;
                    if (larger < error) {
                        error = larger;
                        sum = nums[i] + nums[left] + nums[right];
                    }
                    right--;
                } else {
                    return target;
                }
            }
        }
        return sum;
    }
};

int main(int argc, const char * argv[]) {
    vector<int> test = {-1, 2, 1, -4};
    int tartest = 1;
    Solution s;
    cout << s.threeSumClosest(test, tartest) << endl;
    return 0;
}



leetcode18 — 4sum
给定一个target值,在一个序列里面找出4个sum为target的数字,按顺序返回数字就可以了,不要求返回下标,结果也是可能有多组。
采取跟3sum一样的思路,多加一层去重,具体做法:
  1. 首先排好序O(NlgN)(因为不需要返回)
  2. 遍历数组,寻找该次迭代要放在4sum等号左边的数字并且去重(第一次去重),确定好放在等号左边的数字之后,对之后的数字算3sum;
  3. 遍历数组,寻找该次迭代要放在3sum等号左边的数字并且去重(第二次去重),确定好放在等号左边的数字之后,对之后的数字算2sum;
参考博客还有一种做法,利用哈希,大致思路就是用两个pair代替4个数字,直接将4sum转成2sum:
  1. 首先枚举出所有可能的pair,用每一pair的sum作为key插入到链式哈希表(unordered_map)
  2. 中O( N2)
  3. 然后就可以用2sum的哈希解法来做了,注意最后还要判断2pair的下标是不是合法的,想(1,2,2,3)这种就是不合法的O( N2)
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> perm;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            //第一次去重
            while (i >0 && nums[i - 1] == nums[i]) {
                i++;
            }
            int target3 = target - nums[i];
            for (int j = i + 1; j < nums.size(); j++) {
                //第二次去重,注意j > i + 1而不是j > 1
                while (j > i + 1 && nums[j - 1] == nums[j]) {
                    j++;
                }
                int target2 = target3 - nums[j];
                int left = j + 1, right = int(nums.size()) - 1;
                while (left < right) {
                    if (nums[left] + nums[right] < target2) {
                        left++;
                    } else if (nums[left] + nums[right] > target2) {
                        right--;
                    } else {
                        vector<int> match = {nums[i], nums[j], nums[left], nums[right]};
                        perm.push_back(match);
                        //去重,下一个一定是不一样的数
                        while (nums[left + 1] == nums[left]) {
                            left++;
                        }
                        while (nums[right - 1] == nums[right]) {
                            right--;
                        }
                        left++;
                        right--;
                    }
                }
            }
        }
        return perm;
    }
};
int main(int argc, const char * argv[]) {
    vector<int> n = {-1,0,1,2,-1,-4};
    Solution s;
    vector<vector<int>> res =  s.fourSum(n, -1);
    for (int i = 0; i < res.size(); i++) {
        for (int j = 0; j < 4; j++) {
            cout << res[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}


用红黑树的做法就没仔细看了
参考这篇博客,写得很详细: Summary for LeetCode 2Sum, 3Sum, 4Sum, K Sum


leetcode31 — 3sum smaller要付钱,就先这样。

考察能否合理利用排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值