leetcode分类刷题:基于数组的双指针(三、有序数组的元素求和)

1、有点像“leetcode分类刷题:二分查找(Binary Search)(一、基于索引(定义域)的类型)”的前提,数组是排序好的,然后求解的目标是元素之和,不是查找某个元素第一次大于等于数组元素的索引位置
2、稍微复杂化的题型是对三元组、四元组等的求解,就像“leetcode分类刷题:哈希表(Hash Table)(一、简单的两数之和)”中所提到的,这类数组的元素求和题目,难点在于求解的目标,这里需要返回答案的值构成的三元组、四元组等,就需要考虑答案的去重问题;额外可以考虑加上 特殊情况的判断(当然不加这个的代码也能顺利通过),需要注意target值为负数还是非负数进行分类讨论,最后总结这类题目的解题思路:排序+双指针+答案的去重+特殊情况的判断(可以不加)

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

1、本文总结的经典题型,注意细节:下标从1开始
2、该题为后续几道题的基础题

from typing import List
'''
167. 两数之和 II - 输入有序数组
给你一个下标从 1 开始的整数数组numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数target 的两个数。
如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
    输入:numbers = [2,7,11,15], target = 9
    输出:[1,2]
    解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
题眼:两数之和+有序
思路:有序问题使用双指针最好
'''


class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers) - 1
        while left < right:  # 答案的下标不能重复
            if numbers[left] + numbers[right] == target:
                return [left+1, right+1]  # 下标从1开始
            elif numbers[left] + numbers[right] > target:
                right -= 1
            else:
                left += 1


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split(']')[0].split('[')[1].split(',')]
            target = int(in_line[2].strip())
            print(obj.twoSum(nums, target))
        except EOFError:
            break

15. 三数之和

思路:不同于返回下标,这里是返回;"167. 两数之和 II - 输入有序数组"的扩展 + 对三元组的答案在每个位置都要添加去重逻辑
额外可以考虑加上 特殊情况的判断(需要注意target值为负数还是非负数)

from typing import List
'''
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] 。 注意,输出的顺序和三元组的顺序并不重要。
题眼:这种题目的复杂程度取决于求解目标:求值构成的三元组 且不能重复
思路:不同于返回下标,这里是返回值;"167. 两数之和 II - 输入有序数组"的扩展 + 对三元组的答案在每个位置都要添加去重逻辑;
额外可以考虑加上 特殊情况的判断(需要注意target值为负数还是非负数)
'''


class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 这种题目的复杂程度取决于求解目标:求值构成的三元组 且不能重复
        result = []
        nums.sort()
        for i in range(len(nums) - 2):
            # 特殊情况判断:三元组答案的第一个元素大于target,不必再求解后续位置元素
            if nums[i] > 0:
                break
            # 三元组答案的第一个元素去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            # "167. 两数之和 II - 输入有序数组"的扩展,查找剩余二元组,target值为-nums[i];但要注意剩余二元组的去重
            left, right = i + 1, len(nums) - 1
            while left < right:
                # 双指针遍历过程
                if nums[left] + nums[right] > -nums[i]:
                    right -= 1
                elif nums[left] + nums[right] < -nums[i]:
                    left += 1
                else:
                    result.append([nums[i], nums[left], nums[right]])
                    left += 1
                    right -= 1
                    # 三元组答案的第二、三个元素去重
                    while left < right and nums[left] == nums[left - 1]:
                        left += 1
                    while left < right and nums[right] == nums[right + 1]:
                        right -= 1
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            nums = [int(n) for n in input().strip().split('[')[1][:-1].split(',')]
            print(nums)
            print(obj.threeSum(nums))
        except EOFError:
            break

18. 四数之和

思路:“15. 三数之和”的扩展 + 对三元组的答案在每个位置都要添加去重逻辑;
额外可以考虑加上 特殊情况的判断(需要注意target值为负数还是非负数

from typing import List
'''
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]]
题眼:这种题目的复杂程度取决于求解目标:求值构成的四元组 且不能重复
思路:“15. 三数之和”的扩展 + 对三元组的答案在每个位置都要添加去重逻辑;
额外可以考虑加上 特殊情况的判断(需要注意target值为负数还是非负数)
'''


class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        result = []
        # 情况1、数组长度小于4,直接返回[]
        if len(nums) < 4:
            return result
        nums.sort()
        for i in range(len(nums) - 3):
            # 特殊情况判断:四元组答案的第一个元素情况(需要注意target值为负数还是非负数)
            if target >= 0 and nums[i] > target or target < 0 and nums[i] >= 0:
                break
            # 四元组答案的第一个元素去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            # 寻找剩余三元组的过程
            for j in range(i + 1, len(nums) - 2):
                # 特殊情况判断:四元组答案的第一个元素情况(需要注意target值为负数还是非负数)
                if target >= 0 and nums[i] + nums[j] > target or target < 0 and nums[i] + nums[j] >= 0:
                    break
                # 四元组答案的第二个元素去重
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                # 寻找剩余二元组的过程
                left, right = j + 1, len(nums) - 1
                while left < right:
                    # 双指针遍历过程
                    if nums[left] + nums[right] < target - nums[i] - nums[j]:
                        left += 1
                    elif nums[left] + nums[right] > target - nums[i] - nums[j]:
                        right -= 1
                    else:
                        result.append([nums[i], nums[j], nums[left], nums[right]])
                        left += 1
                        right -= 1
                        # 四元组答案的第三、四个元素去重
                        while left < right and nums[left] == nums[left - 1]:
                            left += 1
                        while left < right and nums[right] == nums[right + 1]:
                            right -= 1
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split(']')[0].split('[')[1].split(',')]
            target = int(in_line[2].strip())
            print(nums, target)
            print(obj.fourSum(nums, target))
        except EOFError:
            break

16. 最接近的三数之和

思路:"167. 两数之和 II - 输入有序数组"的扩展,好像变得更简单了,因为不需要考虑答案去重了

from typing import List
'''
16. 最接近的三数之和
给你一个长度为 n 的整数数组nums和 一个目标值target。请你从 nums 中选出三个整数,使它们的和与target最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
    输入:nums = [-1,2,1,-4], target = 1
    输出:2
    解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
题眼:三数之和
思路:"167. 两数之和 II - 输入有序数组"的扩展
'''


class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        result = nums[0] + nums[1] + nums[2]  # 将result初始值设定为排序后的前三个元素之和 或者 设置为float('inf)
        for i in range(len(nums) - 2):
            # "167. 两数之和 II - 输入有序数组"的扩展
            left, right = i + 1, len(nums) - 1
            while left < right:
                s = nums[i] + nums[left] + nums[right]
                if abs(s - target) < abs(result - target):
                    result = s
                if s == target:
                    return result
                elif s > target:  # 更接近的加和 需要变小,更新右指针
                    right -= 1
                else:    # 更接近的加和 需要变大,更新左指针
                    left += 1
        return result


if __name__ == "__main__":
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split('[')[1].split(']')[0].split(',')]
            target = int(in_line[2])
            print(nums, target)
        except EOFError:
            break

259. 较小的三数之和

思路:"16. 最接近的三数之和"的扩展,求解目标变成了三元组的个数,也不需要考虑去重的问题

from typing import List
'''
259. 较小的三数之和
给定一个长度为 n 的整数数组和一个目标值 target,寻找能够使条件 nums[i] + nums[j] + nums[k] < target 
成立的三元组  i, j, k 个数(0 <= i < j < k < n)。
示例 1:
    输入: nums = [-2,0,1,3], target = 2
    输出: 2 
    解释: 因为一共有两个三元组满足累加和小于 2:
        [-2,0,1]
        [-2,0,3]
题眼:三数之和
思路:"16. 最接近的三数之和"的扩展,求解目标变成了三元组的个数,不考虑去重的问题
'''


class Solution:
    def threeSumSmaller(self, nums: List[int], target: int) -> int:
        nums.sort()
        result = 0
        for i in range(len(nums) - 2):
            # "167. 两数之和 II - 输入有序数组"的扩展
            left, right = i + 1, len(nums) - 1
            while left < right:
                s = nums[i] + nums[left] + nums[right]
                if s < target:
                    result += right - left  # (left, right]区间的所有数值全部满足
                    left += 1
                else:
                    right -= 1
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(n) for n in in_line[1].split('[')[1].split(']')[0].split(',')]
            target = int(in_line[2])
            print(obj.threeSumSmaller(nums, target))
        except EOFError:
            break

猜你喜欢

转载自blog.csdn.net/qq_39975984/article/details/132577478
今日推荐