两数、三数、四数之和相关题目(Leetcode题解-Python语言)

作为 Leetcode 的第一题,两数之和自然是知名度最高的,从两数之和出发也有不少的衍生题目,下面就让我们好好地解决它们。

1. 两数之和

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = dict()
        for i, num in enumerate(nums):
            if target - num in record:
                return [record[target - num], i]
            else:
                record[num] = i

最直接的思路是用哈希表,把出现过的数字(及其索引下标)都用哈希表记录下来,这样每当遍历新的数字,就能快速找出 target - num 是否出现过,若是则返回下标。

170. 两数之和 III - 数据结构设计

class TwoSum:

    def __init__(self):
        self.record = dict()

    def add(self, number: int) -> None:
        if number in self.record:
            self.record[number] += 1
        else:
            self.record[number] = 1

    def find(self, value: int) -> bool:
        for num in self.record.keys():
            if value - num != num:
                if value - num in self.record:
                    return True
            elif self.record[num] > 1:
                return True
        return False

这题是不断地向数组添加数字,同时得能够检查数字是否为数组中某两个数字之和,思路还是哈希表,每当要对数字进行检查时,这个数字就相当于 target,遍历数组中的数字 num,找到 target - num 是否也在数组中即可。要注意的是可能出现 target - num 等于 num 的情况,而这种情况得 num 出现过两次才能返回 True,所以要用字典统计 num 出现的次数。

167. 两数之和 II - 输入有序数组

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        n = len(numbers)
        left = 0
        right = n - 1
        while left < right:
            total = numbers[left] + numbers[right]
            if total > target:
                right -= 1
            elif total < target:
                left += 1
            else:
                return [left+1, right+1]

相较于上一题,这题的条件是数组已经按非递减顺序排列,这其实也给了我们一个新的思路,因为有序数组总是与双指针联系在一起。所以说,这题我们可以使用左右双指针 left 与 right,当两数之和大于 target 时右指针左移,当两数之和小于 target 时左指针右移,正好等于 target 时返回答案(注意下标从 1 开始)。

15. 三数之和

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans = []
        n = len(nums)
        nums.sort()
        for i in range(n):
            # 除了首位置之外,如果有多个重复的 nums[i],那就只考虑最后一个
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            left = i + 1
            right = n - 1
            while left < right:
                total = nums[i] + nums[left] + nums[right]
                # total 太大,右指针左移
                if total > 0:
                    right -= 1
                # total 太小,左指针右移
                elif total < 0:
                    left += 1
                else:
                # total 符合条件
                    ans.append([nums[i], nums[left], nums[right]])
                    # 避免 left + 1 与 left 一样
                    while left < right and nums[left] == nums[left + 1]: 
                        left += 1
                    # 避免 right - 1 与 right 一样
                    while left < right and nums[right] == nums[right - 1]: 
                        right -= 1
                    # 确保了不会出现重复的答案
                    left += 1
                    right -= 1
        return ans

从上一题得到的灵感,就是可以将数组排序然后用双指针解法,这对于三数、四数甚至更高的数之和都是适用的。在本题中,我们首先遍历并固定第一个下标 i,然后对它后面的区域进行双指针查找。由于不能出现重复的三元组,所以去重点有两个:
1、除了第一个位置以外,凡是 nums[i] == nums[i-1] 的,就说明 i 这个位置的数与上一个位置的重复了,直接考虑 i + 1。如果用的是 nums[i] == nums[i+1] 判断,就会把如 (-1, -1, 2) 这样的答案给忽略掉。
2、每当找到一个答案以后,left 和 right 指针都要移动,而它们应该跳过与当前位置数字相同的位置,避免出现重复的三元组。

18. 四数之和

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        ans = []
        nums.sort()
        n = len(nums)
        for i in range(n):
            if i > 0 and nums[i] == nums[i-1]:
                continue
            for j in range(i+1, n):
                if j > i+1 and nums[j] == nums[j-1]:
                    continue
                left = j + 1
                right = n - 1
                while left < right:
                    total = nums[i] + nums[j] + nums[left] + nums[right]
                    if total > target:
                        right -= 1
                    elif total < target:
                        left += 1
                    else:
                        ans.append([nums[i], nums[j], 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 += 1
                        right -= 1
        return ans

与三数之和相比,仅仅是多遍历了一个 j,注意 j 的位置是从 i + 1 开始的,所以它的去重是 if j > i+1 and nums[j] == nums[j-1]:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值