问题
请给出一个运行时间为nlogn的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。
这是算法导论中的一道题目,主要是运行时间为nlogn,如果没有运行时间限制,可以暴力运行n^2就可以得出结论。在这里给出的解决方案运用到了分治法和二分查找法。
代码
1.解题思路是先给数组排序,用分治法,运行时间为nlogn。分冶法的思想如下:算法从最高层开始分解,把数组对半分解成更小的数组,直到分解成最小为止,mergeSort代码片段就是这一过程的解释。然后merge方法把左右两边的数据,从最低层开始,逐层开始排序合并,直至最高层,把结果合并,排序结束。
void merge(int* t, int p, int q, int r, int* tmp)
{
/// 在原始数组上排序,从p开始,到r结束
int i = p;
int j = q + 1;
int k = p;
while (i <= q && j <= r)
{
/// 如果左边小,则用左边的值赋值
if (t[i] <= t[j])
{
tmp[k++] = t[i++];
}
/// 否则,用右边的值赋值
else
{
tmp[k++] = t[j++];
}
}
/// 如果左边还有数据,则把左边数据赋给tmp
while (i <= q)
{
tmp[k++] = t[i++];
}
/// 如果右边还有数据,则把右边数据赋给tmp
while (j <= r)
{
tmp[k++] = t[j++];
}
/// 把tmp赋给t
for (int z = p; z < k; ++z)
{
t[z] = tmp[z];
}
}
void mergeSort(int* t, int p, int r, int* tmp)
{
/// 如果左边的开始位置小于右边的结束位置,则递归排序
if (p < r)
{
/// 求出中间长度
int q = (p + r) / 2;
/// 左边排序 从p到q
mergeSort(t, p, q, tmp);
/// 右边排序 从q+1到r
mergeSort(t, q + 1, r, tmp);
/// 合并左边和右边
merge(t, p, q, r, tmp);
}
}
2.然后,一个循环x依次减去数组中的数得到数key,用二分查找法binarySearch去查找在数组中是否存在数key,如果存在,则说明数组中存在两数之和等于x,否则不存在。
int binarySearch(int* t, int l, int r, int key) {
int p = (l + r) / 2;
if (p < l || p > r) {
return -1;
}
if (t[p] == key) {
return p;
}
else if (t[p] > key)
{
return binarySearch(t, l, p - 1, key);
}
else {
return binarySearch(t, p + 1, r, key);
}
}
下面是测试代码用来判断以上代码是否正确,测试数组{ 10, 1, 9, 3, 5, 8, 2, 6, 4, 7 }中是否有2个数之和等于11,返回第一组找到的两个数。
void print(int* t, int length)
{
for (int i = 0; i < length; ++i)
{
printf("%d ", t[i]);
}
printf("\n");
}
int main() {
int a[10] = { 10, 1, 9, 3, 5, 8, 2, 6, 4, 7 };
int length = sizeof(a) / sizeof(a[0]);
int tmp[10];
print(a, length);
mergeSort(a, 0, length - 1, tmp);
print(a, length);
int x = 11;
for (int i = 0; i < length; ++ i)
{
int key = x - a[i];
int result = binarySearch(a, 0, length, key);
if (-1 != result && result != i) {
printf("has tow number [%d] plus [%d] equal to %d.\n",
a[i], a[result], x);
return 0;
}
}
printf("has no tow number plus equal to %d.\n", x);
return -1;
}
![nlogn-result](https://img-blog.csdnimg.cn/img_convert/da17440cc0a2cf8ce4d6a74592f598d7.png)
查找是否存在两数之和等于11的数
总结
由以上代码可以看出,只要把思路理清,就可以实现此功能代码。来看看运行时间,因为分治法的运行时间是nlogn,而二分查找法的运行时间是logn,但是因为二分查找法在for循环内,所以要乘以n,最终算法复杂度还是nlogn,没有超过nlogn运行时间。
[download id=”1028″ template=”button”]