zoukankan      html  css  js  c++  java
  • 剑指offer解题报告(Java版)——和为s的两个数,一串连续数 41

       

    引言

       

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

       

    对于3Sum的问题,本文除了常用的退化为2Sumn平方的解法外,还提供了一种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<sortedArray.length-1&&sortedArray[pointEnd]==sortedArray[pointEnd+1]) {

    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中取出来放入resultresult也是个链表,相当于最终的结果是个链表,每个节点是一个解,每个解是一个链表,这个链表中有三个数

       

    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<Integer> midResult = new ArrayList<Integer>();

    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;

       

    }

       

    2Sum3Sum的时间复杂度分析

       

    我们可以很轻易的就知道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就会有更快的一个算法。

       

    4sumhash算法

       

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

       

    根据什么来做hash呢,也就是说hashmap中的key值是什么,我们将一个pair的和作为key值,而value值就是这两个树组成的pairlist数据结构,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<(s+1)/2

       

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

       

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

       

    while(small<(s+1)/2)

    {

    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++;

    }

    }

       

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

  • 相关阅读:
    ElementUI Select下拉框定位问题!
    Vue2 中keyup.enter触发问题!
    Java编写1到100质数之和
    JSP过滤器、Session监听器、Servlet控制器的关系和执行顺序
    EL表达式读取属性不存在的异常,读取类的属性不存在,无法调用到该属性
    Servlet中使用request转发页面引发的500空指针异常
    JSP动态产生的代码,点击显示确认操作,根据操作跳转并传值。
    Linux常用命令大全
    FastAdmin
    Laravel入门
  • 原文地址:https://www.cnblogs.com/keedor/p/4473334.html
Copyright © 2011-2022 走看看