java连续0的个数,剑指offer解题报告(Java版)——和为s的两个数,一串连续数 41

引言

第一问题很简单,leetcode上也有相应的题,2Sum问题,leetcode上还有2Sum的进阶版3Sum,只是在这个问题上增加了一层循环而已,另外还有3Sum_Close问题,就是和接近s的三个数,本文将依次介绍2Sum问题,3Sum问题,以及3Sum_close,以及一串连续数问题

对于3Sum的问题,本文除了常用的退化为2Sum的n平方的解法外,还提供了一种hash的方法,使得利用hash的方法使得4Sum问题不再是n三次方的时间复杂度,可以降到n平方的时间复杂度

2Sum问题

这里需要注意的就是要用两个指针来减少对数组的遍历,不然用常规的方法就要遍历两遍数组,两层循环,n平方的时间复杂度

用一个pointHead从前往后走,用一个pointEnd从后往前走,两个如果没遇到就一直这么走下去,判断这两个数加起来是否满足条件,如果满足,输出这两个数,如果不满足,看和是大于还是小于,大于,说明和需要减小才行,于是pointEnd往前走,如果小于,说明和需要被增大才行,于是pointHead往后走

public void findNumbersWithSum(int[] sortedArray, int number)

{

if(sortedArray==null)

return ;

int pointHead=0;

int pointEnd=sortedArray.length-1;

while(pointEnd>pointHead)

{

long curSum=sortedArray[pointEnd]+sortedArray[pointHead];

if(curSum==number)

{

System.out.println(sortedArray[pointHead]);

System.out.println(sortedArray[pointEnd]);

break;

}

else

{

if(curSum>number)

pointEnd–;

else

pointHead++;

}

}

}

以上代码有个问题就是找到一个结果就break了,如果希望找到所有的呢,那么将break变为pointHead++,pointEnd–继续走下去即可

就算改了还是有一个问题,就是结果会不会有重复的情况呢,答案是有的,比如说

int[] array={1,2,4,7,7,8,8,11,15};

那么如果解决重复的问题,一个简单的不增加循环的方法就是在每次循环体的开始检查一下pointHead的那个值是否和pointHead-1的那个值相等,如果相等,则pointHead++并且continue,同样的适合pointEnd

if (pointEnd

pointEnd–;

continue;

}

if (pointHead>0&&sortedArray[pointHead]==sortedArray[pointHead-1]) {

pointHead++;

continue;

}

3Sum问题

外加一层循环,遍历数组所有数,这个数记为first,那么问题转换为在之后的数中找两个树second以及third,使得first+second+third=target结束循环,在first固定的每层循环中如果小就second+,如果大就third-

同样需要考虑的一个问题是找到的结果是否会出现重复的情况,除了上面说到的那种方法之外,还有另外一种方法就是用一个hashmap中去重

将满足条件的结果(三个数字)放入midresult中,midresult是个链表,将midresult放入hashmap中去重

再将hashmap中取出来放入result,result也是个链表,相当于最终的结果是个链表,每个节点是一个解,每个解是一个链表,这个链表中有三个数

public static ArrayList threeSum(int[] num) {

Arrays.sort(num);

ArrayList result = new ArrayList();

Map hm = new HashMap();

for (int firstPos = 0; firstPos < num.length; firstPos++) {

int secPos = firstPos + 1;

int thirdPos = num.length – 1;

while (secPos < thirdPos) {

if (num[firstPos] + num[secPos] + num[thirdPos] == 0) {

ArrayList midResult = new ArrayList();

midResult.add(num[firstPos]);

midResult.add(num[secPos]);

midResult.add(num[thirdPos]);

hm.put(midResult, false);

secPos += 1;

thirdPos -= 1;

} else if (num[firstPos] + num[secPos] + num[thirdPos] < 0) {

secPos += 1;

} else {

thirdPos -= 1;

}

}

}

Iterator it = hm.entrySet().iterator();

while (it.hasNext()) {

//                        Entry entry =(Entry) it.next();

//                        result.add(entry.getKey());

result.add(it.next());

}

return result;

}

2Sum和3Sum的时间复杂度分析

我们可以很轻易的就知道2sum的算法复杂度是O(NlogN),因为排序用了NlogN,头尾指针的搜索是线性的,所以总体是O(NlogN)

考虑3sum, 3sum的算法复杂度就是O(N^2),注意这里复杂度是N平方,而不是O(N^2 log N),很容易在这里犯错误

仔细想想可以知道因为你排序只需要排一次,后面的工作都是取出一个数字,然后找剩下的两个数字,找两个数字是2sum用头尾指针线性扫。

推广下去4sum也就可以退化成3sum问题,那么以此类推,K-sum一步一步退化,最后也就是解决一个2sum的问题,K sum的复杂度是O(n^(K-1))

3Sum_close问题

close问题需要维护一个距离dis,也就是得到的和与真实想要的和之间的误差,如果新的比旧的小,则更新结果,另外还要维护一个真实的和ret

int dis = Integer.MAX_VALUE;

int ret = 0;

int sum = num[i] + num[j] + num[k];

int minus = sum – target;

int d = Math.abs(minus);

if (d < dis) {

dis = d;

ret = sum;

}

if (minus == 0)

return target;

if (minus < 0) {

j++;

} else {

k–;

}

算法提升

这里的算法提升主要是用到hash,用hash的话check某个值存在不存在就是常数时间,那么2sum的解法可以是线性的

比如用hashmap,给定一个sum,只要线性扫描,对每一个number判断sum – num存在不存在就可以了。

注意这个算法对有重复元素的序列也是适用的。比如2 3 3 4那么hashmap可以使hash(2) = 1; hash(3) = 1, hash(4) =1其他都是0,那么check的时候,扫到两次3都是check sum-3在不在hashmap中,注意最后返回所有符合的pair的时候也还是要去重。

这样推广的话3sum其实也有O(N^2)的类似hash算法,这点和之前是没有提高的,但是4sum就会有更快的一个算法。

4sum的hash算法

首先用O(N^2)的时间把所有pair存入hash表,一个pair也就是两两数组成的一对pair,一共有n(n-1)/2个,所以需要n平方的时间复杂度

根据什么来做hash呢,也就是说hashmap中的key值是什么,我们将一个pair的和作为key值,而value值就是这两个树组成的pair的list数据结构,map[hashvalue] = list,每个list中的元素就是一个pair, hashvalue=这个pair的和

那么接下来求4sum就变成了在所有的pair value中求2sum,这个就成了线性算法了,注意这里的线性又是针对pair数量(N^2)的线性,所以整体上这个算法是O(N^2),而且因为我们挂了list,所以只要符合4sum的我们都可以找到对应的是哪四个数字。

一连串数问题

因为是一串连续的数,那么结果就可以用一个small和一个big来界定连续数的第一个和最后一个数

int small=1;

int big=2;

另外samll的大小不必循环遍历到n,因为s/2+s/2+1>s,所以small

curSum可以用求和公式求出来(small+big)/2

如果相等,输出结果,如果大于small++,如果小于big++

while(small

{

int curSum=0;

for(int i=small;i<=big;i++)

curSum+=i;

if(curSum==s)

{

System.out.println(“find one”);

for(int i=small;i<=big;i++)

System.out.println(i);

small++;

}

else

{

if(curSum>s)

small++;

else

big++;

}

}

另外要注意判断一下target的s是否小于3,如果小于3,那么直接返回,因为输入的小于3的无意义,因为1+2就等于3了,而且至少输入两个数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值