Three sum解题心得

Three sum解题心得

来源:https://leetcode.com/problems/3sum/description/

题目重述

在一串整数序列里找到这样三个和为零的数,找出该序列中所有这样的组合,以小到大的顺序排列,答案中要求不存在相同的组
例子:

For example, given array S = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

解题过程

开始由于写过2Sum,就想过类比题目到3Sum中,只要遍历数组每个元素,设当前元素为 S[i] ,设置其相反数为target,相当于2Sum问题中的两个数和的结果,由此可以将问题化解为多个2Sum子问题,同时时间复杂度可以达到 O(n2) ,由于2Sum的最优复杂度为 O(n)

但是。。。

当我匆匆打完过了测试样例,交上去后,讨厌的Wrong Answer出来了
最后发现这里需要将结果进行排序和消重,,,,于是就重新改进了自己的算法:

版本一思路

算法思想
  1. 对S使用sort排序(时间复杂度 O(nlogn)
  2. 遍历S[i],在i+1到n(n为S长度)之间做2Sum问题,target=-S[i]
  3. 2Sum部分(为了顺序要求,与原先的2Sum有所不同,两次遍历效率降低)
    • 先用hash存取i+1到n的元素,按照{S[i],i}的形式存入hash
    • 再次由j从i+1到n遍历,通过之前的hash表快速查找S[j]互补的另一个数com,使S[j]+com=target
    • 每找到一组数则将结果插入输出数组
  4. 过程中可能遇到的bug (主要都是在消重排序上遇到的问题)

    • i,j遍历时需要跳过连续的元素,以防重复查询,所以这里的循环都用的是while,其中i是外层循环,j是内层循环做2Sum查找
    • 跳过重复元素的代码
        //外层循环
        while(i+1<n&&sortedNums[i]==sortedNums[i+1]) 
            i++; 
        i++;
        //内层循环
        j++;
        while(j<n&&j+1<n
        &&sortedNums[j-1]==sortedNums[j]
        &&sortedNums[j+1]==sortedNums[j])
            j++;
    • 注意:在内层循环里的时候可以当前S[j]获得与S[j]互补的元素com,如果在S[j]之前则说明这个组合已经在结果中了,说明已经过了这个小序列的关于target的中心(因为序列有序,则x+y=target的一对组合是从序列两端向中间靠近的,有一对最近的x,y就当作target的临界值),后面遇到的组合都是不需要再去搜索了,可以跳出当前j的循环,结束2Sum
    • !!!push_back后还要把对应找到的S[j]互补的数com从hash表里删除,不然还是可能在关于targetd的中心处出现重复组合问题
    • hint:外层循环里加入判断,判断当前位置nums[i]如果大于0,则后面的搜索是找不到新的目标序列的(但是不知道为什么加入这个后反而变慢了)
时间复杂度

总而言之,其中hash的索引复杂度为O(1),索引两次循环遍历,就是 O(n2) 的时间复杂度

丑陋的代码

这里贴个代码
这里写图片描述

我的代码链接:https://github.com/zhanzongyuan/leetcode/blob/master/015_3Sum.cpp

最后,算法accpted!但是运行时间特别慢,只击败1%的cpp代码


版本二思路

经过我的一波研究,发现其实这里有个性质我忽略了
有序序列两端互补:就是排序后的在i+1到n的序列里的特性,即x+y=target的互补特性没有用到

算法思想
  1. 同样是类似与一中的思想,将问题转换为2Sum,但是要用到有序序列两端互补的特性
  2. 当j在i+1到n的第二层循环里使用两个下标,分别从两端向中间找互补和为target的一对元素
  3. 找到后就插入到结果里,再同时向中间移动下标,直到跳过多个重复元素,碰到新的元素
  4. 重复2-3直到下标相遇,当i结束则得到所有的满足条件的组,同时符合大学顺序
  5. 注意:这里需要对i进行一中类似的重复元素跳过的操作
版本二代码

这里写图片描述

我的代码链接:https://github.com/zongyuanZhan/leetcode/blob/master/015_3Sum.cpp

再一次accept,速度提高很多!超过75%的代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值