今天是五一放假第一天,放完假就要考试了,本来今天打算复习一下高数,结果无奈今天好像状态不太对,就开始啃《算法导论》了。
习题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)
总结:算法题还是要自己多思考,并利用工具进行证明,例如循环不等式,数学工具或其他方式。
吐槽:快凌晨一点了,鬼知道几点能起床,我还要复习准备期中考试。。。。。。