第零章:一个方法团灭 nSum 问题

labuladong一个方法团灭 nSum 问题

1. 两数之和

方法1:利用字典去重

  • 利用字典保存遍历过程变量,若满足条件,则返回结果
class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        dic=dict()
        for i,j in enumerate(nums):
            if target-j in dic:
                return [i, dic[target-j]]
            dic[j]=i
class Solution {
public:
    map<int, int>m;
    vector<int>result;
    vector<int> twoSum(vector<int>& nums, int target) {
        int sz=nums.size();
        for (int i = 0; i < sz; ++i) {
            if (m.count(target-nums[i])){
                result.push_back(i);
                result.push_back(m[target-nums[i]]);
            }
            m.insert(make_pair(nums[i],i)); // 或者 m[nums[i]]=i;
        }
    return result;
    }
};

方法2:排序后双指针逼近

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        import copy
        nums1=copy.copy(nums)
        nums1.sort()
        res = []
        l = 0
        r = len(nums1) - 1
        while l < r:
            cur = nums1[l] + nums1[r]
            if cur < target:
                l += 1
            elif cur > target:
                r -= 1
            else:
                break

        # 找到原来的索引
        for i in range(len(nums)):
            if nums[i] in [nums1[l],nums1[r]]:
                res.append(i)
        return res

15. 三数之和

方法1:利用字典去重

  • 在 2Sum 的基础上(如[-10,4,4,5,5,6],对-10 而言,res=[[4,6],[5,5]]),逐个遍历nums ,求满足target为-nums[i] 的结果
  • 对每次的i而言,从i的后面数组中找twoSum(nums[i+1:],target)即可(如[-10,0,1,10,2],与-10相对应的结果为[-10,0,10]。然后遍历0,只要从0的后面(nums[i+1:])找另外两个数即可,不用管0前面的内容,因为前面能对应到的结果已经找全了)
class Solution(object):
    def threeSum(self, nums):
        def twoSum(nums, target):
            res=[]
            dic = dict()
            for i, j in enumerate(nums):
                if target - j in dic:
                    res.append([j, target - j])
                dic[j] = i
            # 去重,如[[1,2],[1,2],[0,3]] -> [[1,2],[0,3]] (假设target为3)
            resAll=[]
            [resAll.append(i) for i in res if i not in resAll]
            return resAll

        all = []
        nums=sorted(nums)
        for i in range(len(nums)):
            if i>0 and nums[i]==nums[i-1]:continue  # 若有相等元素,则跳过
            target=-nums[i]
            cur=twoSum(nums[i+1:],target)
            # 若没有满足条件的twoSum,对结果也没影响.
            for c in cur: # 对cur中的每组列表都添加当前的nums[i]
                c.append(nums[i])
                all.append(c)
        return all

c++对应的去重方式:如[[1,2],[1,2],[0,3]] -> [[1,2],[0,3]] (假设target为3)

vector<vector<int>> v{{1,2},{1,2},{0,3}};
set<vector<int>> s(v.begin(),v.end());
v.assign(s.begin(),s.end());

方法2:排序后双指针逼近

  • sorted(nums)先对列表进行排序, 然后用双指针逼近
  • 利用twoSum函数返回满足条件的所有列表
    • [-10,4,4,5,5,6],对-10 而言,res=[[4,6],[5,5]]
    • 设置left,保证nums[l]可以与其左边的元素比较是否相等(right同理)
    • for i in range(len(nums)) 对每一次遍历,就是求满足target-nums[i] 的结果
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def twoSum(nums, target):
            res = []
            l, r = 0, len(nums) - 1
            while l < r:
                cur = nums[l] + nums[r]
                left = nums[l]
                right = nums[r]
                if cur < target:
                    while l < r and nums[l] == left: l += 1
                elif cur > target:
                    while l < r and nums[r] == right: r -= 1
                else:
                    res.append([nums[l], nums[r]])
                    # 设置left,保证nums[l]可以与其左边的元素比较是否相等(right 同理)
                    while l < r and nums[l] == left: l += 1# 一直向右跳,直到不相等
                    while l < r and nums[r] == right: r -= 1
            print(res)
            return res

        all = []
        nums=sorted(nums)
        for i in range(len(nums)):
            if i>0 and nums[i]==nums[i-1]:continue  # 若有相等元素,则跳过
            target=-nums[i]
            cur=twoSum(nums[i+1:],target)
            # 若没有满足条件的twoSum,则 不执行下面语句.因为cur为[].
            for c in cur:
                c.append(nums[i])
                all.append(c)
        return all

nSum的变体

nums中可能有多对元素之和都等于 target,请返回所有和为 target 的元素对,且不能出现重复

比如输入为 nums = [1,3,1,2,2,3], target = 4,那么算法返回的结果就是:[[1,3],[2,2]]

  • 排序
  • 通过左右指针更新,若有相同元素,则跳过。left = nums[l]right = nums[r] 的作用是记录当前位置,从而便于后续跳过相同的元素
def twoSum(nums, target):
    l = 0
    r = len(nums) - 1
    nums = sorted(nums)
    res = []
    while l < r:
        sum = nums[l] + nums[r]
        left = nums[l]
        right = nums[r]# 便于后续跳过相同的元素
        if sum < target:
            l += 1
        elif sum > target:
            r -= 1
        else:
            res.append([nums[l], nums[r]])
            while l < r and nums[l] == left: l += 1
            while l < r and nums[r] == right: r -= 1
    print(res)
nums = [1, 3, 1, 2, 2, 3]
target = 4
twoSum(nums,target)
# 结果为:[[1, 3], [2, 2]]

   转载规则


《第零章:一个方法团灭 nSum 问题》 M 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
搜索旋转排序数组系列 搜索旋转排序数组系列
153. 寻找旋转排序数组中的最小值 二分查找过程中,比较mid与right(而非left)的原因:以 [1,2,3,4,5,6,7] 为例,分以下情况 若[1,2,3,4,5,6,7] 左<中,中<右。最小值在最左边, 所以
2020-07-17
下一篇 
Effective CPP Effective CPP
1. 让自己习惯C++1:视C++为一个语言联邦 C++以C为基础 Object-Oriented C++。面向对象编程 Template C++。泛型编程 STL。template程序库,各部件紧密配合 2:尽量以const,enum
2020-07-14
  目录