描述一个运行时间为O(nlgn)的算法,给定n个整数的集合S和另一个整数x,该算法能确定S中是否存在两个其和刚好为x的元素。
这里给出两种算法,都要求先用归并排序算法(merge sort)把集合S中元素从小到大排好序,这一步运行时间为O(nlgn)
第一种算法比较简单,即对集合S中每一个整数a,利用二分搜索法(Binary Search)在排好序的集合S中搜索(x - a)是否存在,如果存在则返回true. 如果每个集合S的整数a都无法在S中找到对应的(x - a),则算法返回false. 这一步的运行时间也是O(nlgn)。两步结合起来,整个算法运行时间保持为O(nlgn)。
第二种算法同样基于集合S中元素已从小到大排好序。
1: Using Merge Sort to sort the array A in time O(nlgn)
2: i = 1
3: j = n
4: while i < j
5: if A[i] + A[j] == x
6: return true
7: elseif A[i] + A[j] < x
8: i = i + 1
9: else
10: j = j - 1
11: return false
line 2 - 11的运行时间是O(n),因为即j - i的值从n - 1逐渐减少到1,每循环一次就减1. 整个算法运行时间为O(nlgn).
这个算法比较巧妙,关键是如何证明它的正确性呢?
这里用到两个性质:
- 如果A[i] + A[j] < x, 则对所有的k < j,一定有A[i] + A[k] < x
- 如果A[i] + A[j] > x, 则对所有的k > i,一定有A[k] + A[j] > x
反证法:假设算法返回false,但集合S中存在A[i]和A[j] (i < j), A[i] + A[j] = x, 也就是在算法循环过程中,这一对(i, j)没有被检查到。
case 1: 假设存在k,j < k <= n, A[i] + A[k] 组合被算法检查到了。由于A[i] + A[j] = x,则A[i] + A[k] > x, 此时算法在检查完A[i] 和 A[k] 后会将k减1,接着检查A[i] 和 A[k - 1],如果依然有 j < k -1 ,则A[i] + A[k - 1] > x,算法会继续检查A[i] 和 A[k - 2]... 一直检查到A[i] 和 A[j],所以假设不成立。
case 2: 假设存在k, 1 <= k < i,A[k] + A[j] 组合被算法检查到了。由于A[i] + A[j] = x,则A[k] + A[j] < x, 此时算法在检查完A[k] 和 A[j] 后会将k加1,接着检查A[k + 1] 和 A[j],如果依然有 k + 1 < i ,则A[k + 1] + A[j] > x,算法会继续检查A[k + 2] 和 A[j]... 一直检查到A[i] 和 A[j],所以假设不成立。
由于起始条件是i = 0, j = n, A[0] + A[n]一开始就被检查到了,所以上述case 1和case 2涵盖了所有除起始条件之外的情况. 所有假设情况都不成立,因此证明算法正确。