题目:请给出一个运行时间为Θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。
来源:互联网 发布:java 数据字典设计 编辑:程序博客网 时间:2024/06/07 20:47
加上了我自己的一点想法,原文为转载
第四种方法:
1、构造一个跟S大小一样大的向量T O(1)
2、将X –S[i]都放进T去, O(n)
3、构造一个M[2*n]大小的数组,然后将S和T采用归并排序的思想插入 O(nlgn)
剩下的事情就是将比较集合T中是否存在至少有连续的元素相同,如果有则存在这样的两个元素其和为X,否则不存在, O(n)
第三种方法:
思路:排序加索引,但是这个索引的复杂度为O(n), 具体为,先对数组进行排序,采用MergeSort或者QuickSort都行(此处考虑的是时间复杂度,空间的复杂度暂时不计)复杂度为O(nlgn),然后设置两个游标从一个为start一个为end,然后start从前往后,end从后往前,对整个序列进行遍历,(设当前要查找value,集合为S,集合中是否有两个数加一起等于它),用delta = X-S[m],如果delta>S[n] 则要求m++,若小于则要求n--,否则相等则输出,结果
code :
# include <stdio.h># include <malloc.h># define MAX 111111111/*by--Acton2013年9月18日12:40:42*/void MergeSort(int a[], int start, int end );void Findnumbers(int a[],int start, int end , int val );int main(void){int a[] = {1,2,3,4,5,6};int want_find ;want_find = 7;MergeSort(a,0,5);for (int i = 0 ; i< 6; i ++){printf("%d ",a[i]);}printf("\n");Findnumbers(a,0,5,want_find);return 0;}void Merge(int a[], int start , int mid ,int end){int nl = mid -start+1;int nr = end-mid ;int *R = (int*)malloc(sizeof(int)*nr);int *L = (int*)malloc(sizeof(int)*nl);for(int i = 0 ;i <nl;i++){L[i] = a[start+i];}L[nl] = MAX;for (int j = 0; j < nr; j ++){R[j] = a[mid+1+j];}R[nr] = MAX;i = 0 ;j = 0; int k = start;while(k <= end){if (L[i] < R[j]){a[k] = L[i];i ++;}else{a[k] = R[j];j ++;}k ++;}}void MergeSort(int a[], int start, int end ){if (start < end){int mid=(start+end)/2;MergeSort(a,start,mid);MergeSort(a,mid+1,end);Merge(a,start,mid,end);}}void Findnumbers(int a[],int start, int end , int val ){//MergeSort(a,0,5);int m = start;int n = end;while (m < n ){if (val-a[m] < a[n]){n --;}else if (val-a[m] > a[n]){m ++;}else{printf("%d + %d = %d\n",a[m],a[n],val);n--;}}return ;}
解题思路:
直观的方法是直接计算集合中两两元素的和,然后判断是否存在x,但时间复杂度为Θ(n^2),不符合题目的要求,也不是一个好的解决问题的方法,下面两种方法要好一些:
第一种是《算法导论》的教师手册上提供的思路,构建一个辅助集合S',通过查找辅助集合S'和原集合合并后的集合中是否有重复的元素来判断,具体步骤如下:
1)对集合S进行排序
2)构建辅助集合S',S'={z:z=x-y,y∈S},也就是说S'中的元素是x减去集合S中的元素生成
3)对集合S'进行排序
4)移除集合S中重复的元素,只保留一个,也就是说使集合S中的元素唯一。对集合S'中做同样的处理。
5)合并集合S和S'
6)当且仅当合并的集合中存在连续的位置上出现相同的的值时,集合S中存在两个数的和为x.(基本直译)
这个解题思路是有问题,而且如果简单从字面意思理解的话,这个思路是错误的,在某些情况下是不正确的。下面一一列出这个思路存在的问题:
a. 在生成辅助集合S'之后,才开始将集合S中的重复元素去掉只保留一个,这样S'中也会有同样的重复元素,为什么不在生成辅助结合S'之前做呢?如果在第1步之后做的话,S'中的元素也是唯一的了,减少重复的工作
b. 第3步完全没有必要,因为S在第1步中已经排好序了,所以生成的S'集合也是排好序的了,只是排序的方式不同。如果集合S是升序排列,则集合S'是降序排列。所以没有必要再对集合S'排序,只需在合并的时候稍作处理即可。
c. 第6步中的描述原文是"There exist two elements in S whose sum is exactly x if and only if the same value appears in consecutive positions in the merged output",如果 从字面意思理解的话,就是只要合并的集合中有重复的元素就证明结合S中存在两个数的和为x.但是如果这么理解的话,是不对的,比如集合S={2,3,5, 6},x=4, 则S'={2, 1,-1,-2},合并后的集合为{-2, -1, 1, 2, 2, 3, 5, 6},合并后的结合中存在重复的元素{2, 2},位置连续并且和为x,但是集合S中并没有两个数的和 为x.所以第6步的表述是有问题,要么是真的错了,要么是语音差异理解的有问题。那要怎么才能正确地确定呢?就是在合并的集合中必须至少有两个重复的元素,这时 才能肯定集合S中存在两个数的和为x.可以证明一下,假设w∈S, y∈S,则集合S'中也存在w∈S,y∈S,所以合并的集合中会有两个重复的元素w、y.如果有多对解 的话,重复的元素个数会更多。
d. 第4步中将重复的元素唯一化,如果重复的元素的和刚好是x呢?这时岂不是反而弄巧成拙了?比如集合S={2,2,5, 6},x=4,去除重复的元素,反而错失了找到两个数的和为x的机会,而且题目中也没有要求两个和为x的元素不能重复。
还有一点需要注意的是,这个思路只是用来确定集合S中是否有两个元素的和为x,不需要确定是哪两个元素的和为x.
所以个人认为正确的思路应该是这样:
1)对集合S进行排序
2)检查集合S中是否有重复的元素,如果有则判断重复的元素乘以2(就是两个相加)是否为x,如果是的话,就找到了,无需做后面的处理;否则移除重复的元素,使集合S 中的元素唯一。
3)构建辅助集合S',S'={z:z=x-y,y∈S},也就是说S'中的元素是x减去集合S中的元素生成
4)合并集合S和S'
5)当合并的集合中有两个或两个以上的重复元素时,集合S中存在两个元素的和为x.
接下来确定上面的思路的算法复杂度。第1步的使用归并排序来完成,时间复杂度为Θ(nlg(n)),第2、3、4、5步的时间复杂度为Θ(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; }
第二种是使用排序+二分查找,具体步骤如下:
1)对集合S进行排序
2)从集合S中选择一个元素S(i),计算x与S(i)的差值y=x-S(i)。在集合S中查找除S(i)之外的元素中是否存在y,如果存在,则返回。
3)检查是否全部元素已遍历,如果没有跳到第2步。
接下来确定该思路的复杂度。第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; }
- 题目:请给出一个运行时间为Θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。
- 题目1.请给出一个运行时间为O(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个证书x时,判断出S中是否存在有两个其和等于x的元素。
- 算法导论 2.3-7 给出一个运行时间为⊙(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素
- 描述一个运行时间为O(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数 X 时,判断出S中是否存在有两个其和刚好等于 X 的元素。
- 描述一个运行时间为Θ(nlgn)的算法,给定n个整数的集合S和另一个整数x,该算法能确定S中是否存在两个其和刚好为x的元素
- 2.3-7 描述一个运行时间为Θ(nlgn)的算法,给定n个整数的集合S和另一个整数x,该算法能确定S中是否存在两个其和刚好为x的元素
- 一个运行时间为nlgn的算法,能判断在集合S中是否有两数之和为x
- 算法入门---判断集合S中是否存在两个其和等于x的元素
- 算法入门---判断集合S中是否存在两个其和等于x的元素
- 算法学习笔记----判断集合S中是否存在有两个其和等于x的元素
- 1. 给一个有N个整数的数组S..和另一个整数X,判断S里有没有2个数的和为X,请设计成O(n*log2(n))的算法。
- 给一个有N个整数的数组S..和另一个整数X,判断S里有没有2个数的和为X,请设计成O(n*log2(n))的算法
- 集合S中是否存在两个元素使它们的和等于另外一个整数
- 判断集合S中是否存在两个其和等于x的元素
- 162 1. 给一个有N个整数的数组S..和另一个整数X,判断S里有没有2个数的和为X
- 给定N个整数集合是否存在两个其和刚好为指定常数的元素
- 【算法导论学习-005】整数集合S中是否存在两个数等于给定的一个整数
- 给出一个实数集合和一个实数,怎样判断集合中是否存在两个和等于该实数的元素
- 如何快速方便的输出向量vector容器中不重复的内容
- SUN X4200 Server, All Leds of HDD0 is not lit
- weblogic 部署应用时一直停留在部署状态解决办法
- Oracle Licensing
- How to clear the FMA faults and error logs from Solaris
- 题目:请给出一个运行时间为Θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。
- J2ME主要应用于嵌入是系统开发
- Redhat Enterprise Linux 系列笔记1
- How to Clear Fault from ilom CLI
- 在eclipse如何运行jsp文件?
- 宁可编译和链接时出错,也不要运行时出错
- How to collect HW info by Ipmitool
- 考研日记20139.15
- jawr提升EXT加载速度