Leetcode编程实践-查找-对撞指针

leetcode 1 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

力扣第一题,就像词汇书的abandon,无脑解法就是套两个循环遍历,这里使用复杂度更低的写法。
【思路】
将数组排序,并且标记原始数组中的索引,可以一遍遍历出结果

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 记录索引
        nums=list(enumerate(nums))
        # 根据元素大小排序
        nums=sorted(nums, key=lambda x: x[1])
        i,j=0,len(nums)-1 # 记录坐标
        while nums[i][1]+nums[j][1]!=target:
            if nums[i][1]+nums[j][1]>target:
                j-=1
            elif nums[i][1]+nums[j][1]<target:
                i+=1
        return sorted([nums[i][0],nums[j][0]])

leetcode 15 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

【思路】
使用指针对撞,有三个数字需要记录三个坐标,最外层遍历第一个坐标 i ,将数列排序后即从最小的一项开始遍历,若该项>0,则不存在三数之和为0,中间位置从 i+1 开始遍历,末尾位置从 len(n) 向前遍历,该两点形成对撞式的遍历。
防止数列中的重复元素被重复计算,若下一个点等于当前点,则跳过下一个点。
代码实现:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort() # 将数列排序
        res=[]
        for i in range(len(nums)-2):
            if nums[i]>0: break  # 第一个元素就大于0 则不存在三数之和为0
            if i>0 and nums[i]==nums[i-1]: continue # 跳过重复元素
            l,r=i+1,len(nums)-1 # 后两个元素遍历初始位置
            while l<r: 
                s=nums[i]+nums[l]+nums[r]
                if s==0:
                    res.append([nums[i],nums[l],nums[r]])
                    l+=1
                    r-=1
                    while l<r and nums[r]==nums[r+1]: # 跳过重复元素 且确保l<r
                        r-=1
                    while l<r and nums[l]==nums[l-1]: 
                        l+=1
                    
                if s>0:r-=1
                if s<0:l+=1
        
        return res

【处理技巧】

  1. 采用for + while的形式来处理三索引;
  2. 当数组不是有序时需要注意,有序的特点在哪里,有序就可以用哪些方法解决?无序的话不便在哪里?
  3. 对撞指针套路:
# 对撞指针套路
l,r = 0, len(nums)-1 
while l < r:
	if nums[l] + nums[r] == target: 
		return nums[l],nums[r]
	elif nums[l] + nums[r] < target: 
		l += 1
	else:
		r -= 1
  1. 处理重复值的套路:先转换为有序数组,再循环判断其与上一次值是否重复:
# 1.
for i in range(len(nums)):
	if i > 0 and nums[i] == nums[i-1]: continue
# 2.
while l < r:
	while l < r and nums[l] == nums[l-1]: l += 1

leetcode 454 四数之和2

给定四个包含整数的数组列表 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

【思路】
纯暴力:O(n^4)
小暴力:字典储存一个数组,key为数组中的元素,value为该元素出现的次数,再遍历剩余数组,O(n^3)
小小暴力:字典储存两个数组的相加和,key为两数之和,value为该结果的次数,在遍历剩余数组,O(n^2)

class Solution:
    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
        from collections import Counter        
        record=Counter(a+b for a in A for b in B) # 储存两数组之和
        return sum([record.get(-d-c,0) for c in C for d in D]) # 遍历剩余两数组

leetcode 149 一条线上的点

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

【思路】
y = k x + b y=kx+b y=kx+b 确定一条直线,获得两点之间的斜率和截距就可以得到一条线,计算每两个点练成的线,用字典储存,key为斜率和截距,值为这条线上的点

class Solution:
    def maxPoints(self, points: List[List[int]]) -> int:
        # 将点按照从小到大排序 方便计算斜率
        points.sort(key=lambda x: x[0])
        points.sort(key=lambda x: x[1])
        
        if len(points)<3 : return len(points)
        '''
		由于当数值特别大时,精度计算会出问题,故使用最简分数形式表达,即分子分母除以最大公约数
		'''
        def gcd(x,y): # 最大公约数函数
            if y==0:
                return x
            else:
                return gcd(y,x%y)
           
        def linear(a,b): # 计算斜率和截距
            if a[0]==b[0]: return a[0]
            y,x=a[1]-b[1],a[0]-b[0]
            k=y/x
            b=a[1]-k*a[0]
            g=gcd(x,y)
            if g!=0:
                k='{},{}'.format(y//g,x//g)
            else:
                k='{},{}'.format(y,x)

            return ','.join([str(k),str(b)])

        from collections import defaultdict
        dic=defaultdict(list)
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):                    
                if points[i] not in dic[linear(points[j],points[i])]:
                    dic[linear(points[j],points[i])].append(points[i])
                if points[j] not in dic[linear(points[j],points[i])]:
                    dic[linear(points[j],points[i])].append(points[j])
        # 由于存在重复点,对点进行计数
        return max([sum([points.count(i) for i in v]) for v in dic.values()])


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值