• 有N个数的数组,找出这个数组中的两个数,使得这两个数的和最接近0


    有N个数的数组,没有顺序。现在的问题是让你在数组中找出两个数,使得这两个数的和尽可能的接近0。

    想到的的方法是尝试所有数对<xi,xj>的组合,之后找出其中和的绝对值最小的数对即可。但是这样做的时间复杂度是O(N^2),有没有更快一点的方法呢?

    这里给出一个O(NlogN)时间复杂度的算法。

    有一种比较直观的做法。

    对数组排好序之后。如果数字全部是正数,那么取最小的两个数的和。如果数字全部是负数,则取最大的两个数字的和。如果数字有正有负。那么我们必须枚举每一个xi,之后用二分在数组中找小于等于-xi的最大值和大于等于-xi的最小值。能与xi构成和的绝对值最小的数字,肯定就是这两个数字中的某一个。所有的xi都枚举过之后,我们就可以找出和的绝对值最小值了。时间复杂度是O(NlogN)。

    还有一种比较简便的做法,但是证明有些复杂。

    1:对数组中的数进行从小到大排序。
    2:设置两个游标,一个指向第一个数,另一个指向最后一个数,之后进行如下操作:把游标处的两个数相加取绝对值,与记录当前已经得到的最小绝对值比较,小则记录。之后对游标处的数的绝对值进行比较,移动指向数字绝对值较小的游标(如果是左面的游标指向的数字绝对值小,则向右移动,否则右面的游标向左移动)。循环此步骤,直至找到最小绝对值0或者游标相遇。

    举个例子:
    
    原始序列:
    
    -2 3 4 -7 9 5
    
    排序后:
    
    -7 -2 3 4 5 9
    
    寻找过程:
    
    res = MAX_INF;
    
    -7 -2 3 4 5 9
    |           |         res = 2
    
    -7 -2 3 4 5 9
    |         |           res = 2
    
    -7 -2 3 4 5 9
        |     |           res = 2
    
    -7 -2 3 4 5 9
        |   |             res = 2
    
    -7 -2 3 4 5 9
        | |               res = 1
    
    -7 -2 3 4 5 9
        |                 res = 1

      虽然给出了这样的算法,但是怎么证明该算法是正确的?以下的证明很繁琐。

    证明:

    设排好序的序列为

    x[1],x[2],......,x[i],x[i+1],......,x[j],x[j+1],......,x[n-1],x[n]

    假设最优解为<x[i],x[j]>

    x[1],x[2],......,x[i],x[i+1],......,x[j],x[j+1],......,x[n-1],x[n]
                                  |                                   |

    如果算法是正确的,那么必须得证明算法执行过程中游标不会错过最优解。

    初始时,游标为<1, n>,显然没有错过最优解。之后每步,都有一个游标移动。那么肯定有一个游标会先到达最优位置。例如左面的游标最先到达x[i]的位置,而另一个游标的位置为k>j

    x[1],x[2],....,x[i],x[i+1],....,x[j],x[j+1],....x[k],....,x[n-1],x[n]
                     |                                |

    下面分情况讨论:

    (1): x[i] > 0

    这种情况下,显然 x[j] > 0 , x[k] > 0,并且x[i]+x[k] > x[i]+x[j],那么把右面的游标向左移动会更优,并且不会错过最优解。

    (2): x[i] < 0,x[j] > 0

    这种情况下又分3中小情况。

    2.1: x[i]+x[j] > 0

    这种情况下,x[i]+x[k] > 0 并且x[i]+x[j] < x[i]+x[k] (因为x[j]<x[k]),那么把右面的游标向左移动会更优,并且不会错过最优解。

    2.2: x[i]+x[j] < 0, x[i]+x[k] < 0

    这种情况下,显然|x[i]+x[k]| < |x[i]+x[j]|,推出<x[i],x[j]>不是最优解,这与假设相矛盾,所以在遇到最优解之前,这种情况不会出现。

    2.3: x[i]+x[j] < 0, x[i]+x[k] > 0

    这种情况下有|x[i]+x[j]| < x[i]+x[k]=>-x[i]-x[j]<x[i]+x[k]=>x[j]+x[k] > -2*x[i] => x[k] > -x[i] = |x[i]| (因为x[j] < -x[i])。也就是说,在这种情况下,我们会移动位置为k那个游标,而不会移动位置为i那个游标,所以不会错过最优解。

    (3): x[i] < 0, x[j] < 0

    这种情况下又继续分成两种小情况。

    3.1 x[k] < 0

    这种情况下<x[i],x[j]>不是最优解,这与假设相矛盾,所以在遇到最优解之前这种情况不会出现。

    3.2 x[k] > 0

    这种情况下也分两种小情况。

    3.2.1 x[i]+x[k] < 0

    |x[i]+x[j]| < |x[i]+x[k]| => -x[i]-x[j] < -x[i]-x[k] => x[j] > x[k],这与x[j] < x[k]相矛盾,因为x是排好序的数组。所以这种情况在遇到最优解之前不会出现。

    3.2.2 x[i]+x[k] > 0

    |x[i]+x[j]| < |x[i]+x[k]| => -x[i]-x[j] < x[i]+x[k] => x[k]+x[j] > -2*x[i]=> x[k] > -2*x[i]-x[j] => x[k] > |x[i]|。也就是说,在这种情况下,我们会移动位置为k那个游标,而不会移动位置为i那个游标,所以不会错过最优解。

    由对称性可证明当右面的游标先到达x[j]时这种情况。

    综上所述可知,只要重复算法的过程,那么我们的游标肯定能够到达最优解<i,j>的状态,而我们采用的方法又是不断记录最小值,那么我们肯定能够得到最优解。

    三楼给出了一个非常简便的方法:按照绝对值排序。仔细想想,其实就是我上面的方法的另一种形式。

    比如,

    -7 -2 3 4 5 9

    按照3楼的方法,将会是 -2 3 4 5 7 9取相邻两数值差绝对值的最小值。如果我们按照绝对值由大到小排序,则序列为9 7 5 4 3 -2,那么这时取相邻两个数的绝对值等价于方法二的两个游标。
  • 相关阅读:
    学习Spring,看这几本书就够了
    这份书单会告诉你,Java网络编程其实很重要
    心雨(三)【英语】
    成功安装SQL Server实例后 无法找到SQL Server Configuration Manager工具的解决方案
    Windows Cluster失败后,AlwaysOn在残存Server节点上快速恢复DB的详细步骤
    SQL Server 数据库本地备份文件通过OSS工具上阿里云(恢复还原数据库)
    透过systemctl管理mysqld服务
    MongoDB 读偏好设置中增加最大有效延迟时间的参数
    MongoDB 副本集丢失数据的测试
    MySQL 时间类型 DATE、DATETIME和TIMESTAMP
  • 原文地址:https://www.cnblogs.com/haolujun/p/2721874.html
Copyright © 2020-2023  润新知