一、2Sum

思路1:

首先对数组排序。不过由于最后返回两个数字的索引,所以需要事先对数据进行备份。然后采用2个指针l和r,分别从左端和右端向中间运动:当l和r位置的两个数字之和小于目标数字target时,r减1;当l和r位置的两个数字之和大于目标数字target时,l加1。因此只需扫描一遍数组就可以检索出两个数字了。最后再扫描一遍原数组,获取这两个数字的索引。

思路2:

将数组的数组映射到哈希表,key是元素的值,value是该值在数组中的索引。考虑到数组中元素有重复,我们使用STL中的unordered_multimap, 它可以允许重复的key存在。映射以后,对于数组中的某个元素num,我们只要在哈希表中查找num2 = target-num。需要注意的是在哈希表中找到了num2,并不一定代表找到了题目要求的两个数,比如对于数组2 7 11 15,target = 4,当num = 2时,num2 = target-num = 2,此时num2可以在哈希表中找到,但是num和num2指向的是同一个元素。因此当num2 = num时,在哈希表找到num2的同时,还需要保证哈希表中num2的个数>=2。

二、3Sum Closest

思路:

我们可以在 2sum问题的基础上来解决3sum问题,假设3sum问题的目标是target。每次从数组中选出一个数k,从剩下的数中求目标等于target-k的2sum问题。这里需要注意的是有个小的trick:当我们从数组中选出第i数时,我们只需要求数值中从第i+1个到最后一个范围内字数组的2sum问题。

三、3Sum

思路: 

为了避免重复,对于排序后的数组,当我们枚举第一个数时,如果遇到重复的就直接跳过;当我们找到一个符合的二元组(第二个数和第三个数)时,也分别对第二个数和第三个数去重。去重代码如下:

 //为了防止出现重复的二元组,使结果等于target
int k = head+;
while(k < tail && sortedNum[k] == sortedNum[head])k++;
head = k; k = tail-;
while(k > head && sortedNum[k] == sortedNum[tail])k--;
tail = k;

四、4Sum

思路1:

我们可以仿照3sum的解决方法。这里枚举第一个和第二个数,然后对余下数的求2sum,算法复杂度为O(n^3),去重方法和上一题类似

思路2:

O(n^2)的算法,和前面相当,都是先对数组排序。我们先枚举出所有二个数的和存放在哈希map中,其中map的key对应的是二个数的和,因为多对元素求和可能是相同的值,故哈希map的value是一个链表(下面的代码中用数组代替),链表每个节点存的是这两个数在数组的下标;这个预处理的时间复杂度是O(n^2)。接着和算法1类似,枚举第一个和第二个元素,假设分别为v1,v2, 然后在哈希map中查找和为target-v1-v2的所有二元对(在对应的链表中),查找的时间为O(1),为了保证不重复计算,我们只保留两个数下标都大于V2的二元对(其实我们在前面3sum问题中所求得的三个数在排序后的数组中下标都是递增的),即时是这样也有可能重复:比如排好序后数组为-9 -4 -2 0 2 4 4,target = 0,当第一个和第二个元素分别是-4,-2时,我们要得到和为0-(-2)-(-4) = 6的二元对,这样的二元对有两个,都是(2,4),且他们在数组中的下标都大于-4和-2,如果都加入结果,则(-4,-2,2,4)会出现两次,因此在加入二元对时,要判断是否和已经加入的二元对重复(由于过早二元对之前数组已经排过序,所以两个元素都相同的二元对可以保证在链表中是相邻的,链表不会出现(2,4)->(1,5)->(2,4)的情况,因此只要判断新加入的二元对和上一个加入的二元对是否重复即可),因为同一个链表中的二元对两个元素的和都是相同的,因此只要二元对的一个元素不同,则这个二元对就不同。我们可以认为哈希map中key对应的链表长度为常数,那么算法总的复杂度为O(n^2)

五、kSum

问题陈述:

在一个数组,从中找出k个数(每个数不能重复取。数组中同一个值有多个,可以取多个),使得和为零。找出所有这样的组合,要求没有重复项(只要值不同即可,不要求在原数组中的index不同)

解法:

2 sum 用hash table做,可以时间O(n),空间O(n),
2 sum 如果用sort以后,在前后扫描,可以时间O(nlogn + n) = O(nlogn),空间O(1)
2 sum 用hash table做的好处是快,但是等于是利用了不用排序的特点。排序的办法,在高维度(也就是k sum问题,k>2)的时候,nlogn就不是主要的时间消耗成分,也就更适合2sum的sort后双指针扫描查找的办法。

那么,对于k sum, k>2的,如果用sort的话, 可以 对 n-2的数做嵌套循环,因为已经sort过了,最后剩下的两维用2 sum的第二个办法, 时间是O(nlogn + n^(k-2) * n) = O(n^(n-1)),空间O(1)。 但是这样跟纯嵌套循环没有什么区别,只是最后一层少了一个因子n。有什么办法能优化?
就是说,对于 k sum (k>2) 问题 (一个size为n的array, 查找k个数的一个tuple,满足总和sum为0), 有没有时间复杂度在O(n^(k-2))的办法?

之前常规的一层一层剥离,n的次数是递增的。只有在最后一层,还有两个维度的时候,时间开销上减少一个n的因子,但是这样时间开销还是太多

我们可以通过对问题分解来解决
举个例子
...-5,-4,-3,-2,-1, 0,1, 2, 3, 4, 5.... 要找 4 sum = 0
那么先分解
4 分成 2 sum + 2 sum 来解决,但是这里的子问题2 sum没有sum=0的要求,是保留任何中间值。只有当子问题的2 sum解决以后,回归原问题的时候,我们才又回归原始的2 sum问题,这时候sum=0
子问题,空间和时间消耗,都是O(n^2)
回归大问题,时间消耗,是O(n^2)

假设k sum中  k = 2^m, 那么一共有m层,会有m次分解
分解到最底层,时间空间消耗 从 原始O(n)变为新的O(n^2)
分解到次底层,时间空间消耗 从 O(n^2)变为新的O((n^2)^2)
...
到达最顶层,时间空间消耗就都变成了O(n^(2*m)) = O(n^(2logk))

和之前的方法O(n^(k-1))相比,O(n^(2logk))的时间是少了很多,但是空间消耗却很大。
因为子问题无法确定把哪一个中间结果留下,那么就需要把子问题的结果全部返回,到最后,空间消耗就很大了。整体效果算是空间换时间吧。

通过 问题的分解 + hashtable的运用,能明显减少时间消耗, 但是空间消耗变大是个问题。比如说,如果有10^6的int类型数组,我如果用这个hashtable的办法,就要有10^12的pair,这就有10T以上的空间消耗。

问题的分解是个很好的思路,但是中间值得保留迫使空间消耗增大,这和用不用hashtable倒没有很大关系,只是说,如果不用hashtable,时间消耗会更大。

【数组】kSum问题的更多相关文章

  1. bzoj1901--树状数组套主席树

    树状数组套主席树模板题... 题目大意: 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[ ...

  2. 求一个数组的最大子数组(C/C++实现)

    最大子数组:要求相连,加起来的和最大的子数组就是一个数组的最大子数组.编译环境:VS2012,顺便说句其实我是C#程序员,我只是喜欢学C++. 其实这是个半成品,还有些BUG在里面,不过总体的思路是这 ...

  3. HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5008 Description In this problem, you are given ...

  4. 2016 一中培训 day 5 ksum

    又是一天的爆零!!!!! 原本第一题 很容易做 竟然优化过度 丢了答案 1693: ksum Time Limit 1000 ms Memory Limit 524288 KBytes Judge S ...

  5. BZOJ1901 - Dynamic Rankings(树状数组套主席树)

    题目大意 给定一个有N个数字的序列,然后又m个指令,指令种类只有两种,形式如下: Q l r k 要求你查询区间[l,r]第k小的数是哪个 C i t  要求你把第i个数修改为t 题解 动态的区间第k ...

  6. 2Sum,3Sum,4Sum,kSum,3Sum Closest系列

    1).2sum 1.题意:找出数组中和为target的所有数对 2.思路:排序数组,然后用两个指针i.j,一前一后,计算两个指针所指内容的和与target的关系,如果小于target,i右移,如果大于 ...

  7. hdu_5788_Level Up(树状数组+主席树)

    题目链接:hdu_5788_Level Up 题意: 有一棵树,n个节点,每个节点有个能力值A[i],mid[i],mid的值为第i节点的子树的中位数(包括本身),现在让你将其中的一个节点的A值改为1 ...

  8. 洛谷P2617 Dynamic Ranking(主席树,树套树,树状数组)

    洛谷题目传送门 YCB巨佬对此题有详细的讲解.%YCB%请点这里 思路分析 不能套用静态主席树的方法了.因为的\(N\)个线段树相互纠缠,一旦改了一个点,整个主席树统统都要改一遍...... 话说我真 ...

  9. 【BZOJ3196】二逼平衡树(树状数组,线段树)

    [BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...

随机推荐

  1. Linux监控本机当前状态命令

    vmstat 1.简介 vmstat命令是最常见的Linux监控工具,可以查看系统的状态值,其中包括:CPU.内存.虚拟内存.I/O情况. 2.参数说明 命令格式:  vmstat [-a] [-n ...

  2. [转] libcurl异步方式使用总结(附流程图)

    文为转载,原文地址:libcurl异步方式使用总结 实习期间用到了libcurl来做HTTPS双向认证,用的是异步方式,简单总结一下. libcurl这个库的同步方式很简单,不做介绍,而异步方式很难理 ...

  3. setTimeout问题

    function fn(argu1){ alert(argu1); } setTimeout(fn, 1000, 12); setTimeout从第三个参数开始,之后的参数都是fn的.fn不用加(), ...

  4. [IOS] 详解图片局部拉伸 + 实现图片局部收缩

    (图为微信首页右上角『+』效果) 当初还在开发WP7的时候,从IOS同事那边了解到类似微信以上功能的实现. Item条数不同,总高度也不同,这就需要将背景图片进行局部拉伸到响应的高度,并且保持上方的三 ...

  5. leetcode-爬楼梯(动态规划)

    假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 示例 1: 输入: 2 输出: 2 解释: 有两 ...

  6. Postgresql 創建觸發器,刪除觸發器和 禁用觸發器

    CREATE OR REPLACE FUNCTION XF_VIP_AFUPD_WX() RETURNS trigger AS $$ DECLARE i_count integer; s_wx_ope ...

  7. Dalsa线扫相机配置-一台工控机同时连接多个GigE相机

    如图,我强悍的工控机,有六个网口. 实际用的时候连了多台相机,为了偷懒我就把六个网口的地址分别设为192.168.0.1~192.168.0.6,以为相机的IP只要设在192.168.0这个网段然后随 ...

  8. [翻译]第一天 - 在 Windows 下安装和运行 .NET Core

    原文: http://michaelcrump.net/getting-started-with-aspnetcore/ 免责声明:我不是 .NET Core 开发团队的一员,并且使用的是公开.可用的 ...

  9. 显式等待-----Selenium快速入门(十)

    上一篇说了元素定位过程中的隐式等待,今天我们来探讨一下显示等待.显式等待,其实就是在使用WebDriverWait这个对象,进行等待.显式等待对比隐式等待,多了一些人性化的设置,可以说是更细化的隐式等 ...

  10. NetCore偶尔有用篇:NetCore项目WebApi返回Json属性大小写

    一.概述 1.前面文章介绍Controller的大小写问题时,目的只是介绍它的差异性,有同学回复了,这里把它作为一个点写一下吧. 二.默认定义的转换结果 1.写一个返回对象的方法. 2.运行查看结果. ...