《算法导论》练习2.3-7

今天是五一放假第一天,放完假就要考试了,本来今天打算复习一下高数,结果无奈今天好像状态不太对,就开始啃《算法导论》了。

习题2.3-7:设计一个算法,对于一个给定的包含n个整数的集合S和另一个给定的整数X,该算法可以在Θ(nlgn)时间内确定S中是否存在两个元素,使得它们的和恰为X。

Θ(nlgn)的时间复杂度很容易让人想到利用快速排序,归并排序等Θ(nlgn)的算法首先进行排序,因为无序的序列实在是太难处理了。然后用两个下标
i=0;
j=Array.length-1;
通过每次比较两个位置的元素的和来缩小判定范围。伪代码如下

checkSum(A,x)
{
    //进行升序排序,可以换成任意一个时间为Θ(nlgn)的排序算法
    mergeSort(A,A.length)
    i=0;
    j=A.length-1;
    while(i<j)
        if(A[i]+A[j]>x)
            j--;
        else
            if(A[i]+A[j]<x)
                i++;
            else
                return true;
    return false;
}

下面是利用循环不等式对上述算法进行证明

  • 初始化:设给定的数组A中可能存在两个元素a,b,使得a+b==x为真。排序后若a,b存在则两者显然在A[0…A.length-1]中,这里假设数组时升序排序的。令i=0,j=A.length-1。
  • 保持:第一次迭代前(也就是数组刚刚排序完成后)若a,b存在则两者显然在A[i…j]中

    • 如果A[i] + A[j] > x,说明A[i]和A[j]中至少有一个不是我们要找的a,b。由于数组时升序排列的,所以A[j]肯定是大于等于A[i]的,这时应该寻找比A[j]更小的元素,也就是A[j]的前一个元素,这时若a,b存在则必然有a,b在A[i…j-1]中,故执行j的值减一,此时若a,b存在,则a,b必在A[i…j]中。
    • 如果A[i] + A[j] < x,说明A[i]和A[j]中至少有一个不是我们要找的a,b。由于数组时升序排列的,所以A[j]肯定是大于等于A[i]的,这时应该寻找比A[i]更大的元素,也就是A[i]的后一个元素,这时若a,b存在则必然有a,b在A[i+1…j]中,故执行i的值加一,此时若a,b存在,则a,b必在A[i…j]中。
    • 如果A[i] + A[j] == x为真,则说明A[i] A[j]是我们要找的a,b,直接返回真值。
    • 由上述过程可知第一次迭代前若a,b存在则必然在A[i…j-1]中或者已经被找到,每次迭代结束,若a,b存在则必然也在A[i…j-1]中,或者已经被找到。每次迭代都会缩小查找范围。
  • 终止:每次迭代都会缩小a,b所在的范围,当i = j的时候,范A[i…j]内只有一个元素,很明显不符合求和为x的要求,当i > j的时候,A[i…j]内不存在元素(这里以i为起点,j为终点),显然不符合求和为x的要求,则直接返回false表示未找到符合要求的两个元素。

时间复杂度证明:

  • 归并排序的时间复杂度为稳定的Θ(nlgn)
  • 后续的7~14行的循环部分平均和最坏时间复杂度均为Θ(n)
  • 两种算法并列,则checkSum算法的时间复杂度为稳定的Θ(nlgn)

总结:算法题还是要自己多思考,并利用工具进行证明,例如循环不等式,数学工具或其他方式。

吐槽:快凌晨一点了,鬼知道几点能起床,我还要复习准备期中考试。。。。。。

猜你喜欢

转载自blog.csdn.net/kongming07/article/details/80146592