leetcode题解之三数之和

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
'''
class Solution:
    '''简单的双向指针算法:未找到解时,左右边界每次只移动1位
    执行用时: 760 ms,  在所有 python3 提交中击败了91.80%的用户
    内存消耗: 17.0 MB, 在所有 python3 提交中击败了23.80%的用户
    '''
    def threeSum(self, nums: list) -> list:
        n = len(nums)
        if nums is None or n < 3:
            return []
        nums.sort()
        i = 0
        ans = []
        while i < n and nums[i] <= 0:
            L, R, t = i + 1, n - 1, -nums[i]
            while L < R:
                if nums[L] + nums[R] < t: 
                    L += 1  #和太小,左边界右移1位
                elif nums[L] + nums[R] > t:
                    R -= 1  #和太大,右边界左移1位
                else: #恰好相等,保存解
                    ans.append([nums[i], 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, R = L + 1, R - 1 #更新左右边界
            i += 1
            while i < n and nums[i] == nums[i-1]:#跳过相同的nums[i]
                i += 1
        return ans
    
    '''另一种的双向指针算法(按理说效率更高,但实际用时比第一种多):
    使用对分查找算法寻找合适的右边界(最左侧的解或者离解最近的较大元素值下标)。
    也许是因为需要在右侧对分查找解的缘故,效率不如想象的高。
    执行用时: 1344 ms,  在所有 python3 提交中击败了33.35%的用户
    内存消耗: 17.0 MB, 在所有 python3 提交中击败了23.80%的用户
    '''
    def threeSum2(self, nums: list) -> list:
        def bsearch(arr, low, high, key):
            while low <= high:
                mid = (low + high) // 2
                if arr[mid] < key:
                    low = mid + 1
                else: #若有解,返回最左侧的key的下标
                    high = mid - 1
            return low #若未找到key,则返回第一个比key大的元素下标
        
        n = len(nums)
        if nums is None or n < 3:
            return []
        nums.sort()
        i = 0
        ans = []
        while i < n and nums[i] <= 0:
            L, R, t = i + 1, n - 1, -nums[i]
            while L < R:
                if nums[L] + nums[R] >= t: #可能存在解或者和太大
                    R = bsearch(nums, L+1, R-1, t-nums[L]) #寻找最接近t-nums[L]的元素下标
                    if nums[L] + nums[R] == t:
                        ans.append([nums[i], nums[L], nums[R]])
                    R -= 1 #只有找到解或者和太大时,才移动右边界
                L += 1 #不管哪种情况都要移动左边界
            i += 1
            while i < n and nums[i] == nums[i-1]:#跳过相同的nums[i]
                i += 1
        return ans
    
    '''非常高效的算法:使用字典来消除重复元素,并设置两个列表分别存储负数和非负数。
    只需二重循环扫描2个有序列表,效率大大提高。
    执行用时: 388 ms,  在所有 python3 提交中击败了98.30%的用户
    内存消耗: 17.5 MB, 在所有 python3 提交中击败了20.80%的用户
    '''
    def threeSum3(self, nums: list) -> list:
        lib = {}
        ans = []
        for num in nums: #以元素值为键,出现次数为值
            lib[num] = lib.get(num, 0) + 1
        if lib.get(0, 0) >= 3: #元素0出现了3次或更多
            ans.append([0, 0, 0])

        neg = sorted([x for x in lib if x < 0])
        pos = sorted([x for x in lib if x >= 0])

        for i in neg: #向右扫描负数
            for j in pos[::-1]: #向左扫描正数
                k = 0 - i - j
                if k in lib:
                    if k in (i, j) and lib[k] >= 2:
                        ans.append([i, j, k])
                    elif k > i and k < j: #为避免重复,k必须在[i,j]范围内
                        ans.append([i, j, k])
        return ans
    
    #网友作品
    def threeSum4(self, nums: [int]) -> [[int]]:
        nums.sort()
        res, k = [], 0
        for k in range(len(nums) - 2):
            if nums[k] > 0: break # 1. because of j > i > k.
            if k > 0 and nums[k] == nums[k - 1]: continue # 2. skip the same `nums[k]`.
            i, j = k + 1, len(nums) - 1
            while i < j: # 3. double pointer
                s = nums[k] + nums[i] + nums[j]
                if s < 0:
                    i += 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                elif s > 0:
                    j -= 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                else:
                    res.append([nums[k], nums[i], nums[j]])
                    i += 1
                    j -= 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
        return res

    
x = Solution()
a = [-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0]
print(x.threeSum2(a))
print(x.threeSum3(a))
print(x.threeSum(a))
 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值