LeetCode15三数之和

题目描述:

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

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

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

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

解题思路:

这题肯定要写循环遍历数组的。

  1. 想到的第一种办法就是:遍历数组,每次都与其后面的数组进行相加,如果得到的结果的倒数,在剩余数组中能够找到,则成功。

    存在的问题:

    • 如何快速的定位两数之和的倒数仍在剩余的数组中

    • 如果有重复的结果,如何有效的去重

    第一个问题,我采用的办法是使用python list中的in方法,因为list是使用线性结构存储的,其查询复杂度是 O ( n ) O(n) O(n),整个程序的时间复杂度就达到了 O ( n 3 ) O(n^3) O(n3)。列表与字典的存储结构不同,字典使用Hash表结构来存储,其存取效率要高很多,时间复杂度是 O ( 1 ) O(1) O(1)

    第二个问题,去重也是使用的in方法判断是否在列表其中,在其中,则不添加。

            if len(nums) < 3:
                return []
    
            result = []
            for i in range(len(nums)-1):
                for j in range(i+1, len(nums)):
                    sum = nums[i] + nums[j]
                    if -sum in nums:
                        k = nums.index(-sum)
                        if k != i and k != j:
                            temp = sorted([nums[i], nums[j], nums[k]])
                            if temp not in result:
                                result.append(temp)
    
            return result
    
  2. 所以想的是,为了提高遍历效率,要考虑使用双指针。

    Tips : 一般遍历问题,都考虑一下是否可以使用双指针,然后想清楚双指针的移动条件。

    先对数组排序,使用双指针从首尾两端对数组进行遍历。每次都得到首尾两端数字的和,然后判断两数字和的倒数是否在剩余的列表切片中即可。如果在的话,则加到结果集中。如果不在的话… 额(⊙o⊙)…是个问题。

    如果两数字之和大于0,则需要右边(大于0)的指针左移

    如果两数之和小于0…

    好像这样讲不出道理。。。GG

  3. 虽然上一种做法有问题,不过双指针的想法还是正确的。所以,应该换个思路。

    先对列表进行排序,然后从头到尾遍历。这样每次都固定住一个值,而不是用双指针来解决三值问题,好像是可行的。

    在固定一个值之后,剩下的有序列表中,用双指针法寻找两个值的和是否是固定值的负数。这样指针移动的标准也就出来了。如下图:

    • 当两值之和大于固定值的负数时,右边的指针左移
    • 当两值之和小于固定值的负数时,左边指针右移
    • 当两值之和恰好为固定值负数,保存结果。此时有个很有必要的操作:判断 start+1的值,end-1的值,是否与上一个一样,相同的话,则跳过处理,我一开始就没处理,想偷懒最后保存结果的时候去重即可,但是去重要使用in方法, O ( n ) O(n) O(n)的复杂度,增加了时间复杂性。

解答:

实现是使用的Python3写的。代码地址在GitHub,可调试:https://github.com/zhangdianlei/LeetCode_python/blob/master/src/c15.py

实现:

        if len(nums) < 3:
            return []
        result = []

        nums = sorted(nums)

        for index, item in enumerate(nums):

            target = -item
            start = index + 1
            end = len(nums) - 1

            if item > 0 or nums[end] < 0:
                break

            if index > 0 and item == nums[index-1]:
                continue

            while start < end:

                if nums[end] < 0:
                    break

                if nums[start] + nums[end] == target:
                    temp_list = [nums[index], nums[start], nums[end]]
                    result.append(temp_list)

                    while start < end and nums[start] == nums[start+1]:
                        start = start + 1
                    while start < end and nums[end] == nums[end-1]:
                        end = end - 1

                    start = start + 1
                    end = end - 1

                elif nums[start] + nums[end] < target:
                    start = start + 1

                else:
                    end = end - 1

        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值