k-sum 问题

zhe两数之和  , 两次遍历, 第一次 生成对应位置值的差值字典, 第二次判断差值字典存在,则找到一组答案.  leetcode 第一题.  On

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        tmp = {}
        for i in range(len(nums)):
            diff = target-nums[i]
            tmp[diff] = i

        for i in range(len(nums)):
            if nums[i] in tmp and i != tmp[nums[i]]:
                return [i, tmp[nums[i]]]

leetcode 167 输入有序数组的两数之和;   区别是输入有序非递减,输出要求有序; 仍可以用两数和的思路, 两次遍历.    双指针也可以用. 

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        tmp = {}
        for i in range(len(numbers)):
            # tmp[target-numbers[i]] = i # 存储的是下标, 非递减时下标一定是不同的,值可能相同会覆盖, 所以key要换成下标啊, value 是对应的差值
            # tmp[i] = target-numbers[i] #key 为下标, value 为差值时 ,下次循环获取对应的key 有点麻烦, 改造一下数据结构, hash 碰撞处理
            if target-numbers[i] in tmp:  # key 仍为差值, value 为下标列表
                tmp[target-numbers[i]].append(i)
            else: 
                tmp[target-numbers[i]] = [i]
        for i in range(len(numbers)):
            if numbers[i] in tmp:
                for j in range(len(tmp.get(numbers[i]))):
                    if tmp[numbers[i]][j] != i:   # 找到一组  
                        # return [tmp[numbers[i]][j]+1,i+1]  # 返回的下标必须有序 满足 index1<index2
                        ret = [tmp[numbers[i]][j]+1,i+1]
                        ret.reverse()    # 本身是升序所以, i < target-i 
                        return ret

653. 两数之和 IV - 输入二叉搜索树https://leetcode.cn/problems/two-sum-iv-input-is-a-bst/

判断in self.s 时,如果in 则必然不是本节点, 所以不需要前面!=自己下标 

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:

    def __init__(self):
        self.s = set()

    def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
        if root is None :
            return False
        if k-root.val in self.s:
            return True
        
        self.s.add(root.val)
        return self.findTarget(root.left,k) or self.findTarget(root.right, k)

查找两棵二叉搜索树之和https://leetcode.cn/problems/two-sum-bsts/

15. 三数之和 - 力扣(LeetCode)15. 三数之和 - 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组。  示例 1:输入:nums = [-1,0,1,2,-1,-4]输出:[[-1,-1,2],[-1,0,1]]解释:nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。注意,输出的顺序和三元组的顺序并不重要。示例 2:输入:nums = [0,1,1]输出:[]解释:唯一可能的三元组和不为 0 。示例 3:输入:nums = [0,0,0]输出:[[0,0,0]]解释:唯一可能的三元组和为 0 。 提示: * 3 <= nums.length <= 3000 * -105 <= nums[i] <= 105https://leetcode.cn/problems/3sum/description/

3sum 用固定+ 双指针; 然后做超时优化. 时间复杂o(nlogn) + o(n2) 

class Solution:
    def __init__(self):
        self.l = list()  # set 自动去重

    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        # 超时
        # 固定一个数, 剩下的双指针
        for i in range(len(nums)-2): # 第一次优化有边界 n-2 ,至少需要三个元素 
            # 第三次优化,跳过重复的i
            if i>0 and nums[i] == nums[i-1]:
                continue
            left = i+1
            right = len(nums)-1
            while(left<right):
                if nums[left] + nums[right] == -nums[i]:
                    # 第二次优化, 跳过重复元素
                    self.l.append([nums[i],nums[left],nums[right]]) 
                    while(left<right and nums[left] == nums[left+1]):
                        left+=1
                    while(left<right and nums[right] == nums[right-1]):
                        right-=1
                    # 第四次优化,这里有问题,找到一个结果后, left ,right 继续变
                    left+=1
                    right-=1
                elif nums[left] + nums[right] > -nums[i]:
                    right-=1
                else :
                    left+=1
        # return self.s   # 返回结果要求是list 用set 不合适
        return self.l

        

18. 四数之和 - 力扣(LeetCode)18. 四数之和 - 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复): * 0 <= a, b, c, d < n * a、b、c 和 d 互不相同 * nums[a] + nums[b] + nums[c] + nums[d] == target你可以按 任意顺序 返回答案 。 示例 1:输入:nums = [1,0,-1,0,-2,2], target = 0输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]示例 2:输入:nums = [2,2,2,2,2], target = 8输出:[[2,2,2,2]] 提示: * 1 <= nums.length <= 200 * -109 <= nums[i] <= 109 * -109 <= target <= 109https://leetcode.cn/problems/4sum/description/

 继续用3sum的思想搞ac了, 增加剪枝优化

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        # 套用三数和
        nums.sort()
        n = len(nums)
        ret = []
        if n<4:
            return ret
        for i in range(n-3):
            if i >0 and nums[i] == nums[i-1]:
                continue
            # 优化下增加剪枝, 最小的四个和比target大 后面的不用看了,使用索引千面判断下
            if nums[i]+nums[i+1] + nums[i+2] +nums[i+3] > target:
                break
            # 再优化剪枝, 当前+ 最大的数 比target小, 跳过当前
            if nums[i] + nums[n-1] + nums[n-2] + nums[n-3] < target:
                continue
            for j in range(i+1,n-2):
                if j>i+1 and nums[j] == nums[j-1]:
                    continue
                # 优化下增加剪枝, 最小的四个和比target大 后面的不用看了,使用索引千面判断下
                if nums[i]+nums[j] + nums[j+1] +nums[j+2] > target:
                    break
                if nums[i]  + nums[j] + nums[n-1] + nums[n-2] < target:
                    continue
                l, r= j+1, n-1
                while l<r: 
                    if target ==  nums[i] + nums[j] + nums[l] + nums[r]:
                        # 找到一个结果
                        ret.append([nums[i],nums[j],nums[l],nums[r]])
                        # 找到后跳过重复
                        while l<r and nums[l] == nums[l+1]:
                            l+=1
                        while l<r and nums[r] == nums[r-1]:
                            r-=1
                        l+=1
                        r-=1
                    elif target > nums[i] + nums[j] + nums[l] + nums[r]:
                        l+=1
                    else:
                        r-=1
        return ret

最接近的三数之和https://leetcode.cn/problems/3sum-closest/

接着用双指针, 跳指针的时候 先跳过重复的再移动,   若先移动再跳出问题难debug.

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        # 排序数组 nlogn
        nums.sort()
        # 初始化结果
        ret = nums[0]+nums[1]+nums[2]
        # 初始化最接近的值
        # min_diff = abs(ret-target)
        n = len(nums)
        # 遍历nums
        for i in range(n-2): # 三数
            first = nums[i]
            if i>0 and nums[i] == nums[i-1]: #去重 
                continue
            left,right =i+1,n-1 
            while left < right: # 用三数和双指针思想
                total = nums[i] + nums[left] + nums[right]
                if (total == target): # 0最接近直接返回
                    return total
                # total 与 target 更接近更新 ret, 更新最小diff 
                if abs(total-target) < abs(ret-target):
                    ret = total
                    # min_diff = abs(total-target)
                        
                # 固定步长导致漏掉一些组合, 按需移动 
                if total > target:
                    # 移动指针时跳过重复值
                    while left<right and nums[right] == nums[right-1]:
                        right-=1
                    # 跳过后再移动
                    right-=1
                else:
                    while left<right and nums[left]==nums[left+1]:
                        left+=1
                    left +=1
        return ret

等差三元组的数目https://leetcode.cn/problems/number-of-arithmetic-triplets/

BF就行了,没有别的必要 

class Solution:
    def arithmeticTriplets(self, nums: List[int], diff: int) -> int:

        nums = set(nums)
        # 严格递增, n >=3 , 且diff 指定
        n = len(nums)
        ret =0
        # 从第一个确定位置开始找diff
        for i in nums: # 三元组
            # 假设使用双指针, left , right ; 三元组的 right - left 一定等于2diff
            #  对每个位置 nums[i] + diff 存在 且 nums[i] + 2diff 存在 则找到一组答案, 剩下的肯定不是
            if (i + diff) in nums and (i+2*diff) in nums:
                ret+=1
        return ret

元素和最小的山形三元组 Ihttps://leetcode.cn/problems/minimum-sum-of-mountain-triplets-i/

可以BF, for i xx : for j xx : for k xx : if i<j and j>k : do : 

前缀和思想两次遍历挺合适的.  

class Solution:
    def minimumSum(self, nums: List[int]) -> int:
        # n>=3 且 j 左右一定有小于i ,k 否则-1, 题目任意nums[i]<50
        ret = 200
        # 两次遍历,前缀数组思想
        n = len(nums)
        left, right = [0 for i in range(n)], [0] * n
        mni = 1000 # 随便定义个, 
        mxi = 1000
        left[0] = 1000
        right[n-1] = 1000

        for i in range(1,n): # 从1开始, 0 左边没有元素
            left[i] = mni = min(nums[i-1], mni)
        for i in range(n-2,0,-1):
            right[i] = mxi = min(nums[i+1], mxi)

        for i in range(n):
            if nums[i]>left[i] and nums[i] > right[i]:
                ret = min(ret, nums[i]+left[i]+right[i])
    
        return -1 if ret == 200 else ret
        

元素和最小的山形三元组 IIhttps://leetcode.cn/problems/minimum-sum-of-mountain-triplets-ii/

跟山形三元组1 一样解哦, 只是数量大了不好用BF 

class Solution:
    def minimumSum(self, nums: List[int]) -> int:
        # 与山型三元组1 相同用前缀和思想搞下
        n = len(nums)
        left, right = [0]*n,[0]*n
        left[0] = float('inf')
        right[n-1] = float('inf')
        ret = float('inf')

        for i in range(1,n):
            left[i] = min(nums[i-1], left[i-1])
        for i in range(n-2,-1,-1): #到0吧            
            right[i] = min(nums[i+1], right[i+1])
        
        for i in range(n):
            if(nums[i] > left[i] and nums[i]>right[i]):
                ret = min(ret, nums[i]+left[i]+right[i])
        return -1 if ret == float('inf') else ret

四数相加 IIhttps://leetcode.cn/problems/4sum-ii/

用python的counter 结构方便实现, 其他语言自适配 

from collections import Counter
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        ret = 0
        ab = [a + b for a in nums1 for b in nums2]
        ab = Counter(ab)
        # print(ab)
        cd = [-c-d for c in nums3 for d in nums4]
        cd = Counter(cd)
        # print(cd)
        for f in cd :
            if f in ab:
                ret += ab[f] * cd[f]
        return ret

统计特殊四元组https://leetcode.cn/problems/count-special-quadruplets/

数据量不大用的BF ac , 量大肯定不行, 需要优化 感觉还是 四数相加 a+b =d-c 的思路延续;  

class Solution:
    def countQuadruplets(self, nums: List[int]) -> int:
        # 数据量不大BF
        ret =0
        n = len(nums)
        # a + b = d - c ? 四数相加思路?, 四数和的双指针不太行
        # a < b < c < d 
        for i in range(n-3): # 4个数
            for j in range(i+1, n-2): #3个数
                # 固定了i+j ,剩下的 c d 
                for c in range(j+1, n-1):
                    for d in range(c+1,n):
                        if nums[i] + nums[j] + nums[c] == nums[d]:
                            ret+=1
        return ret

 ksum 用回溯模板弄吧

from typing import List

def backtrack(nums: List[int], target: int, k: int, start: int, current_sum: int, path: List[int], result: List[List[int]]):
    if len(path) == k:
        if current_sum == target:
            result.append(path.copy())
        return
    for i in range(start, len(nums)):
        if i > start and nums[i] == nums[i - 1]:
            continue  # 跳过重复的元素
        if current_sum + nums[i] > target:
            break  # 剪枝:如果当前和已经超过目标,提前终止
        path.append(nums[i])
        backtrack(nums, target, k, i + 1, current_sum + nums[i], path, result)
        path.pop()

def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
    nums.sort()  # 首先对数组进行排序
    result = []
    path = []
    backtrack(nums, target, k, 0, 0, path, result)
    return result

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值