玩转算法面试LeetCode算法练习——查找问题2

目录

 

使用查找表的经典问题

1. 两数之和

15. 三数之和

18. 四数之和

16. 最接近的三数之和(不完全属于查找表)

454. 四数相加 II

49. 字母异位词分组

447. 回旋镖的数量

149. 直线上最多的点数


使用查找表的经典问题

1. 两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution:
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        dic = dict()
        for index,value in enumerate(nums):
            sub = target - value
            if sub in dic:
                return [dic[sub],index]
            else:
                dic[value] = index

15. 三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

优化方式见四数之和

class Solution:
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        先排序,从小到大选取第一个数,再在剩余区间内左右夹逼。
        另外,排除一些不可能的情况可以提速很多。 
        { … , a , … , b→ , … ,←c , …} 
        采用类似二分搜索的思想,用两个指针,这样时间复杂度是O(n^2)
        """
        nums.sort()
        res = []
        for i in range(len(nums)-2):
            #nums[i]与nums[i-1]的值相同时,在第一次nums[i-1]时就已经遍历完全为三和0的情况了
            if i>0 and nums[i] == nums[i-1]:
                continue
            l = i+1
            r = len(nums)-1
            while l<r:
                s = nums[i]+nums[l]+nums[r]
                if s == 0:
                    res.append([nums[i],nums[l],nums[r]])
                    while l<r and nums[l]==nums[l+1]:#排除在当前nums[i]下,nums[l]的相同情况
                        l+=1
                    while l<r and nums[r]==nums[r-1]:#排除在当前nums[i]下,nums[r]的相同情况
                        r-=1
                    l+=1
                    r-=1
                elif s<0:
                    l+=1
                else:
                    r-=1
        return res

法二:

        freq = {}
        for elem in nums:
            freq[elem] = freq.get(elem, 0) + 1
        if 0 in freq and freq[0] > 2:
            res = [[0,0,0]]
        else:
            res = []
        neg = sorted((filter(lambda x: x < 0, freq)))
        nneg = sorted((filter(lambda x: x>= 0, freq)))
        for elem1 in neg:
            for elem2 in nneg:
                target = -(elem1 + elem2)
                if target in freq:
                    if target in (elem1, elem2) and freq[target] > 1: # 此target为elem1, elem2其中一个
                        res.append([elem1, target, elem2])
                    elif target < elem1 or target > elem2: # 此target不为elem1或elem2
                        res.append([elem1, target, elem2])

        return res

 

18. 四数之和

 

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 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]
]

在没优化之前是36%,优化之后83%  时间从1160ms到180ms

class Solution:
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        
        res = []
        n = len(nums)
        if n<4:
            return res
        nums.sort()
        
        for i in range(n-3):
            #优化1:最小的四数之和大于target或最大的四数之和小于target,break
            if sum(nums[i:i+4]) > target or sum(nums[-4:]) < target:  
                break
            #优化2:nums[i]与最大的后三个数之和小于target,continue for循环
            if nums[i] + sum(nums[-3:])<target:
                continue
            #nums[i]与nums[i-1]的值相同时,在第一次nums[i-1]时就已经遍历完四和=target的情况了
            if i>0 and nums[i]==nums[i-1]:
                continue
            target1 = target - nums[i]
            for j in range(i+1,n-2):#3数之和的代码
                #优化3:(当前i)最小的三数之和大于target1或最大的三数之和小于target1,break
                if sum(nums[j:j+3])>target1 or sum(nums[-3:])<target1:
                    break
                #优化4:(当前i)nums[j]与最大的后三个数之和小于target,continue for循环
                if nums[j] + sum(nums[-2:]) < target1:
                    continue
                if j>i+1 and nums[j]==nums[j-1]:
                    continue
                target2 = target1 - nums[j]
                l = j+1
                r = len(nums)-1
                while l<r:
                    s = nums[l]+nums[r]
                    if s == target2:
                        res.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 s<target2:
                        l+=1
                    else:
                        r-=1
        return res

16. 最接近的三数之和(不完全属于查找表)

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

优化后:100%,优化前40%

class Solution:
    def threeSumClosest(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        nums.sort()
        closest_sum = nums[0]+nums[1]+nums[2]#初始化
        min_diff = abs(closest_sum-target)
        for i in range(len(nums)-2):
            #优化1:
            if i>0 and nums[i]==nums[i-1]:
                continue
            l = i+1
            r = len(nums)-1
            #优化2:当前i与最大两个数之和小于target且差的绝对值小于之前的min_diff(减少了循环次数)
            cur_max_sum = nums[i]+nums[r-1]+nums[r]
            if cur_max_sum <target and abs(cur_max_sum - target) < min_diff:
                min_diff = abs(cur_max_sum-target)
                closest_sum = cur_max_sum
                continue
            while l<r:
                cur_sum = nums[i]+nums[l]+nums[r]
                if abs(cur_sum-target)<abs(closest_sum-target):
                    min_diff = abs(cur_sum - target)
                    closest_sum = cur_sum
                if cur_sum == target:
                    return target
                elif cur_sum<target:
                    l+=1
                else:
                    r-=1
        return closest_sum

 

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 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

暴力解法O(n^4) 执行6亿次; 将D放入查找表O(n^3)执行1亿次;

class Solution:
    def fourSumCount(self, A, B, C, D):
        """
        :type A: List[int]
        :type B: List[int]
        :type C: List[int]
        :type D: List[int]
        :rtype: int
        """
        #时间复杂度O(n^2),空间复杂度O(n^2)
        #from collections import defaultdict
        #dic = defaultdict(int)
        dic = {}
        for c_num in C:
            for d_num in D:
                dic[c_num+d_num] = dic.get(c_num+d_num,0)+1
                
        res = 0
        for a_num in A:
            for b_num in B:
                s = 0-a_num-b_num
                if s in dic.keys():
                    res += dic[s]
        return res

49. 字母异位词分组


给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

说明:

  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。
class Solution:
    def groupAnagrams(self, strs):
        """
        :type strs: List[str]
        :rtype: List[List[str]]
        先将字符串数组中的每一个字符串列表化然后排序,变成字符串,通过这种变换字母异位词变成相同的,
        生成的字符串作为dic的key,value为k,k为字符串列表的索引,这样将字母异位词组合在一个列表中
        """
        res = []
        dic = {}
        k=0
        for i in range(len(strs)):
            onestrlist = (list(strs[i])).sort()
            onestrlist="".join(onestrlist)
            #onestrlist = sortstr(onestr)
            if onestrlist in dic.keys():               
                res[dic[onestrlist]].append(strs[i])
            else:
                dic[onestrlist]=k
                res.append([strs[i]])
                k+=1
        return res 

法2:

        dict={}
        for each_str in strs:
            new_str=''.join(sorted(each_str))
            if new_str in dict:
                dict[new_str].append(each_str)
            else:
                dict[new_str]=[each_str]
        return list(dict.values())

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]]

class Solution:
    def numberOfBoomerangs(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        #时间复杂度O(n^2),空间O(n)
        res = 0        
        for x in points:
            dic = {} #key=距离,value=频率
            for y in points:
                i = x[0] - y[0]
                j = x[1] - y[1]
                #由于是n对不同的点,x,y是同一个点时,距离=0的值频率只有xy是同一个点的时候
                dis = i*i+j*j
                dic[dis] = dic.get(dis,0)+1
            for item in dic.values():
                res += item * (item - 1)    #value=1的值不影响res
        return res

 

149. 直线上最多的点数

 

 

给定一个二维平面,平面上有 个点,求最多有多少个点在同一条直线上。

示例 1:

输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
|        o
|     o
|  o  
+------------->
0  1  2  3  4

示例 2:

输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
|  o
|     o        o
|        o
|  o        o
+------------------->
0  1  2  3  4  5  6

测试用例中有  [[0,0],[94911151,94911150],[94911152,94911151]],因此要用longdouble

# Definition for a point.
# class Point:
#     def __init__(self, a=0, b=0):
#         self.x = a
#         self.y = b

class Solution:
    def maxPoints(self, points):
        """
        :type points: List[Point]
        :rtype: int
        """
        import numpy as np
        length = len(points)
        if length < 3: 
            return length
        res = 0
        for i in range(length):
            dic={'in_y':0} #key是斜率,value是点的数量
            same = 1
            for j in range(i+1,length):
                x = points[i].x-points[j].x
                y = points[i].y-points[j].y                
                if y != 0 and x == 0:#都为0是同一个点,y!=0 x==0平行于y轴
                    dic['in_y']  += 1
                elif x!=0:
                    k = np.longdouble(1) * y / x
                    dic[k] = dic.get(k,0) + 1
                else:
                    same +=1     #列表中有与i重复的点,因此i与其他点构成的直线,都要加上same的个数                
            res = max(max(dic.values())+same,res)
        return res

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值