题目:请给出一个运行时间为Θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。
解题思路:其代码实现如下所示:
#include <stdio.h> #include <errno.h> #ifndef INT_MAX #define INT_MAX ((int)(~0U>>1)) #endif #define ARRAY_SIZE(__s) (sizeof(__s) / sizeof(__s[0])) static void merge(int *a, int start, int mid, int end) { int nl = mid - start + 1; int nr = end - mid; int sentinel = INT_MAX; int left[nl + 1], right[nr + 1]; int i, j, k = start; for (i = 0; i < nl; ++i) { left[i] = a[k++]; } /* Set sentinel */ left[i] = sentinel; for (j = 0; j < nr; ++j) { right[j] = a[k++]; } /* Set sentinel */ right[j] = sentinel; i = j = 0; for (k = start; k <= end; ++k) { if (left[i] <= right[j]) { a[k] = left[i++]; } else { a[k] = right[j++]; } } } static void merge_sort(int *a, int start, int end) { int mid; if ((start >= 0) && (start < end)) { mid = (start + end) /2 ; merge_sort(a, start, mid); merge_sort(a, mid + 1, end); merge(a, start, mid, end); } } static int check_exist_x(int *a, int len, int x) { int i, j, k; int last; /* Just for test, should avoid */ int tmp[len]; int collection[2 * len]; int repeats; if (len < 1) { fprintf(stderr, "Too few elements.\n"); return -EINVAL; } merge_sort(a, 0, len - 1); last = 0; /* Remove repeat elements */ for (i = 1; i < len; ++i) { if (a[last] == a[i]) { if ((a[last] << 1) == x) { /* Found */ return 0; } continue; } a[++last] = a[i]; } ++last; /* Form tmp set */ for (i = 0; i < last; ++i) { tmp[i] = x - a[i]; } i = 0; j = last - 1; k = 0; /* Merge */ while ((i < last) && (j >= 0)) { if (a[i] < tmp[j]) { collection[k++] = a[i++]; } else { collection[k++] = tmp[j--]; } } while (i < last) { collection[k++] = a[i++]; } while (j >= 0) { collection[k++] = tmp[j--]; } repeats = 0; /* Check the number of repeat elements */ for (i = 1, j = 0; i < k; ++i, ++j) { if (collection[i] == collection[j]) { ++repeats; } if (repeats >= 2) { return 0; } } return -1; } int main(void) { int source[] = { 7, 5, 2, 4, 6, 1, 5, 3}; int ret; int x = 13; ret = check_exist_x(source, ARRAY_SIZE(source), x); printf("If there are two elements whose sum equals to x? %s.\n", ret < 0 ? "No" : "Yes"); return 0; }第二种是使用排序+二分查找,具体步骤如下:
接下来确定该思路的复杂度。第1步使用归并排序来排序,时间复杂度为Θ(nlg(n));二分查找的时间复杂度为Θ(lg(n)),第2、3步需要遍历的次数为n,因此第2、3步的时间复杂度为Θ(nlg(n)),因此总的时间复杂度为Θ(nlg(n)),符合题目的要求。
代码实现如下所示:
#include <stdio.h> #include <errno.h> #ifndef INT_MAX #define INT_MAX ((int)(~0U>>1)) #endif #define ARRAY_SIZE(__s) (sizeof(__s) / sizeof(__s[0])) static void merge(int *a, int start, int mid, int end) { int nl = mid - start + 1; int nr = end - mid; int sentinel = INT_MAX; int left[nl + 1], right[nr + 1]; int i, j, k = start; for (i = 0; i < nl; ++i) { left[i] = a[k++]; } /* Set sentinel */ left[i] = sentinel; for (j = 0; j < nr; ++j) { right[j] = a[k++]; } /* Set sentinel */ right[j] = sentinel; i = j = 0; for (k = start; k <= end; ++k) { if (left[i] <= right[j]) { a[k] = left[i++]; } else { a[k] = right[j++]; } } } static void merge_sort(int *a, int start, int end) { int mid; if ((start >= 0) && (start < end)) { mid = (start + end) /2 ; merge_sort(a, start, mid); merge_sort(a, mid + 1, end); merge(a, start, mid, end); } } static int binary_search(int *a, int len, int expect, int target) { int left = 0, right = len - 1, mid; do { if (left == expect) { ++left; } if (right == expect) { --right; } mid = (left + right) / 2; if ((mid != expect) && (a[mid] == target)) { return 0; } else if (a[mid] > target) { right = --mid; } else { left = ++mid; } } while (left <= right); return -1; } static int check_exist_x(int *a, int len, int x) { int i; if (!a || len < 2) { fprintf(stderr, "Too few elements.\n"); return -1; } merge_sort(a, 0, len - 1); for (i = 0; i < len; ++i) { if (!binary_search(a, len, i, x - a[i])) { return 0; } } return -1; } int main(void) { int source[] = { 7, 5, 2, 4, 6, 1, 5, 3}; int x = 13; int ret; ret = check_exist_x(source, ARRAY_SIZE(source), x); printf("If there are two elements whose sum equals to x? %s.\n", ret < 0 ? "No" : "Yes"); return 0; }