Leetcode编程训练4 查找2

目标

这次的作业主要是完成

  • 多个数据的查找(nSum):排序和对撞指针
  • 多个点共线、距离相等:查表
  • 滑动数组

多个数据的查找

问题是在给定数组nums内找出和为target的n个数组合(n=2,3,4…),甚至有结果最接近target的n个数组合。

  • 思路1是2Sum中的查找表法:遍历数组过程中,当遍历到元素v时,可以只看v前面的元素时候含有target-v的元素存在,如果没有就将v放入查找表中,继续查找下一个解,否则查找成功返回解。
  • 思路2是将数组排序后,采用两指针对撞的方法找到l和r使得nums[l]+nums[r]=target-nums[i]-nums[j],注意重复结果的处理。

LeetCode 1 两数之和

问题: 给出一个整型数组nums,返回这个数组中两个数字的索引值i和j,使得nums[i] + nums[j]等于一个给定的target值,两个索引不能相等。如:nums= [2,7,11,15],target=9 返回[0,1]
思路: 一种是采用查找表避免嵌套循环,另一种是双指针碰撞。
代码

class Solution:
    '''法1:采用查表找相同'''
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = dict()
        for i in range(len(nums)):
            complement = target - nums[i]
            # 已经在之前的字典中找到这个值
            if record.get(complement) is not None:
                res = [i,record[complement]]
                return res
            record[nums[i]] = i
    '''法2:采用双指针碰撞'''
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        _nums = list(enumerate(nums))
        _nums.sort(key=lambda x:x[1])
        lo,hi = 0, len(nums)-1
        while lo < hi:
            if _nums[lo][1] + _nums[hi][1] > target:
                hi -= 1
            elif _nums[lo][1] + _nums[hi][1] < target:
                lo += 1
            else:
                return _nums[lo][0],_nums[hi][0]

LeetCode 15 三数之和

问题: 给出一个整型数组,寻找其中的所有不同的三元组(a,b,c),使得a+b+c=0注意:答案中不可以包含重复的三元组。如:nums = [-1, 0, 1, 2, -1, -4],结果为:[[-1, 0, 1],[-1, -1, 2]]
思路: 固定a,然后找到b和c,然后循环遍历a。注意去重。
代码:

class Solution:
    def threeSum(self, nums: [int]) -> [[int]]:
        nums.sort()
        res = []
        for i in range(len(nums)-2):
            # 因为是排序好的数组,如果最小的都大于0可以直接排除
            if nums[i] > 0: break
            # 排除i的重复值
            if i > 0 and nums[i] == nums[i-1]: continue
            l,r = i+1, len(nums)-1
            while l < r:
                sum = nums[i] + nums[l] + nums[r]
                if sum == 0:
                    res.append([nums[i],nums[l],nums[r]])
                    l += 1
                    r -= 1
                    while l < r and nums[l] == nums[l-1]: l += 1
                    while l < r and nums[r] == nums[r+1]: r -= 1
                elif sum < 0:
                    l += 1
                else:
                    r -= 1
        return res

LeetCode 16 最接近的三数之和t

问题: 给出一个整形数组,寻找其中的三个元素a,b,c,使得a+b+c的值最接近另外一个给定的数字target。
如:给定数组 nums = [-1,2,1,-4], 和 target = 1.与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
思路: 不是找相等的target,而是找最近的。因此开始随机设定三个数的和为结果值,然后比较判断三数只和是否与target相等,如果相等直接返回,如果不等则判断三数之和与target之间的差是否小于这个结果,如果小于则替换并保存和的结果值。

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        diff = abs(nums[0]+nums[1]+nums[2]-target)
        res = nums[0] + nums[1] + nums[2]
        for i in range(len(nums)):
            l,r = i+1,len(nums)-1
            t = target - nums[i]
            while l < r:
                if nums[l] + nums[r] == t:
                    return nums[i] + t
                else:
                    if abs(nums[l]+nums[r]-t) < diff:
                        diff = abs(nums[l]+nums[r]-t)
                        res = nums[i]+nums[l]+nums[r]
                    if nums[l]+nums[r] < t:
                        l += 1
                    else:
                        r -= 1
        return res

LeetCode 18 四数之和

问题: 给出一个整形数组,寻找其中的所有不同的四元组(a,b,c,d),使得a+b+c+d等于一个给定的数字target。
如:nums = [1, 0, -1, 0, -2, 2],target = 0结果为:[[-1, 0, 0, 1],[-2, -1, 1, 2],[-2, 0, 0, 2]]
思路: 类似3Sum的思路,注意重复值的处理。
代码

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        res = []
        if len(nums) < 4: return res
        if len(nums) == 4 and sum(nums) == target:
            res.append(nums)
            return res
        for i in range(len(nums)-3):
            if i > 0 and nums[i] == nums[i-1]: continue
            for j in range(i+1,len(nums)-2):
                if j > i+1 and nums[j] == nums[j-1]: continue
                    l,r = j+1, len(nums)-1
                    while l < r:
                        sum_value = nums[i] + nums[j] + nums[l] + nums[r]
                        if sum_value == target:
                            res.append([nums[i],nums[j],nums[l],nums[r]])
                            l += 1
                            r -= 1
                            while l < r and nums[l] == nums[l-1]: l += 1
                            while l < r and nums[r] == nums[r+1]: r -= 1
                        elif sum_value < target:
                            l += 1
                        else:
                            r -= 1
        return res

LeetCode 454 四数相加 II

问题: 给出四个整形数组A,B,C,D,寻找有多少i,j,k,l的组合,使得A[i]+B[j]+C[k]+D[l]=0。其中,A,B,C,D中均含有相同的元素个数N,且0<=N<=500;
输入: A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2] 输出:2
没时间看了。。

LeetCode 49 字母异位词分组

问题: 给出一个字符串数组,将其中所有可以通过颠倒字符顺序产生相同结果的单词进行分组。
示例:输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
输出:[[“ate”,“eat”,“tea”],[“nat”,“tan”],[“bat”]]
说明:所有输入均为小写字母。不考虑答案输出的顺序。

思路: 本来我是以set记录每个单词的字库,结果遇到了’bob’和’bo’,所以单词长度或者词频也很重要,因此转换成dict保存单词信息。结果一看答案给每个单词排序,然后以计数-单词为key-value建立dict,然后返回dict的结果,所以排序666.
代码:

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        from collections import defaultdict
        _dict = {}
        for str in strs:
            _str = ''.join(sorted(list(str)))
            if _str not in _dict:
                _dict[_str]  = [str]
            else:
                _dict[_str].append(str)
        return [v for k,v in _dict.items()]

LeetCode 447. 回旋镖的数量

问题: 给出一个平面上的n个点,寻找存在多少个由这些点构成的三元组(i,j,k),使得i,j两点的距离等于i,k两点的距离。其中n最多为500,且所有的点坐标的范围在[-10000,10000]之间。
输入:[[0,0],[1,0],[2,0]] 输出:2
解释:两个结果为: [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]

思路: 固定i点,遍历数组后找到所有与i相等距离的点,这些点排列即可得到包含i点的结果。然后针对不同的i点将所有的情况计数即为所求。
代码:

class Solution:
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        def func(x1,y1):
            from collections import Counter
            _dict = Counter([(x2-x1)**2 + (y2-y1)**2 for x2,y2 in points])
            return sum(t*(t-1) for k,t in _dict.items())
        return sum(func(x1,y1) for x1,y1 in points)

class Solution:
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        res = 0
        from collections import Counter
        for i in points:
            record = Counter()
            for j in points:
                if i != j:
                    record[self.dis(i,j)] += 1
            for k,v in record.items():
                res += v*(v-1)
        return res
    def dis(self,point1,point2):
        return (point1[0]-point2[0]) ** 2 + (point1[1]-point2[1]) ** 2

LeetCode 149. 直线上最多的点数

问题: 给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:输入: [[1,1],[2,2],[3,3]] 输出: 3
示例 2:输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] 输出: 4

思路: 与上一题距离相同相比,这道题要求共线,即固定i点后,余下的点与i连线的斜率相等或者点重合。这道题我最不喜欢,因为截至写文时候,还没有解答出来,总是不能解决[[0,0],[0,0]]问题或者[[1,1],[2,2],[1,1],[2,2]]。
斜率的计算(y2-y1)/(x2-x1)

滑动数组

滑动数组部分解决的问题是对数组中待查找的2个数值索引有限制,采用滑动数组方法可以减少重复遍历,减少计算量。思路是在set()集合结构为窗,当集合未填满则不处理,填满情况下继续添加则去除集合最前面的值,在最后一位补充新增值。

LeetCode 219. 存在重复元素 II

问题 给出一个整形数组nums和一个整数k,是否存在索引i和j,使得nums[i]==nums[j],且i和J之间的差不超过k。
示例1:输入: nums = [1,2,3,1], k = 3输出: true
示例 2:输入: nums = [1,2,3,1,2,3], k = 2输出: false
思路: 由于滑动数组的元素都不同且不考虑位置,因此选择set作为数据结构。
代码:

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        rec = set()
        for i,n in enumerate(nums):
            if n in rec: return True
            rec.add(n)
            if len(rec) == k+1:
                rec.remove(nums[i-k])
        return False

LeetCode 220 Contains Dupliccate Ⅲ

问题: 给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得nums [i] 和nums [j]的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:输入: nums = [1,2,3,1], k = 3, t = 0 输出: true
示例 2:输入: nums = [1,0,1,1], k = 1, t = 2 输出: true
示例 3:输入: nums = [1,5,9,1,5,9], k = 2, t = 3 输出: false

思路: 与上一题相比,判断条件由查找表中有nums[i]变为了查找比v-t大的最小元素。
代码:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        record = set()
        for i in range(len(nums)):
            if len(record) != 0:
                rec = list(record)
                find_index = self.lower_bound(rec,nums[i]-t)
                if find_index != -1 and rec[find_index] <= nums[i] + t:
                    return True
            record.add(nums[i])
            if len(record) == k + 1:
                record.remove(nums[i - k])
        return False
    def lower_bound(self, nums, target):
        low, high = 0, len(nums)-1
        while low<high:
            mid = int((low+high)/2)
            if nums[mid] < target:
                low = mid+1
            else:
                high = mid
        return low if nums[low] >= target else -1

参考:

https://github.com/datawhalechina/team-learning-program/blob/master/LeetCodeClassification/3.%E6%9F%A5%E6%89%BE.md#leetcode-1-two-sum

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值