快速寻找满足条件的两个数
2014-04-09 20:23
267 查看
快速寻找满足条件的两个数
提出问题:能否快速找出一个数组中的两个数字,让这两个数字的和等于一个给定的值,为了简化起见我们假设这个数组中至少存在一组满足要求的解。
例如有如下两个数组,
5,6,1,4,7,9,8 给定sum=10
1,5,6,7,8,9给定sum=10
解法一:
这个题目也不是很难,也很容易理解。但是要得出高效的解法,还是需要一番思考的。
直接的解法是穷举:从数组中任意取出两个数字,计算两者之和是否为给定的数字。
显然其时间复杂度为O(n*n)。这个算法简单,写起来也很容易,但是效率不高。
#include<cstdio> #include<algorithm> #define MAX 100005 using namespace std; int main(int argc,char *argv[]) { int n,m; int v[MAX]; int i,j; int flag=0; scanf("%d%d",&n,&m); for(i=0;i<n;i++) scanf("%d",&v[i]); sort(v,v+n); for(i=0;i<n;i++) { for(j=i+1;j<n;j++) { if(v[i]+v[j]==m) { flag=1; break; } } if(flag) break; } if(flag) printf("%d %d\n",v[i],v[j]); else printf("No Solution\n"); return 0; }
解法二:
求两个数字的和,我们假设给定的和为sum。一个变通的思路,就是对数组中的每个元素array[i]都判断一下sum-array[i]是否在数组中。这样就变成一个查找的算法了。
在一个无序的数组中查找一个数的时间复杂度为O(n),对于每个数字array[i],都需要查找对应的sum-array[i]在不在数组中,很容易得到时间复杂度还是O(n*n)。这和原始的方法相比并没有改进,但是如果能有高效的查找算法,就能提高整个算法的效率,怎样提高查找效率?
大家很容易就会想到二分查找,这样可以将原来O(n)的查找时间缩短到O(logN)。这样对于每个array[i],都要花费O(logN)去查找对应的sum-array[i]在不在数组中,总的时间复杂度降为O(NlogN),另外将长度为N的数组进行排序本身也需要O(NlogN)的时间,这样总的时间复杂度仍然为O(NlogN)。这样,就改进了原始的算法。
到这里,我们可能会想,先排序在进行二分查找固然可以将时间复杂度从O(n*n)缩短到O(NlogN),但是还有更快的查找方法:Hash表。应为给定一个数字,根据Hash映射查找另外一个数字是否数组中只需要O(1),这样的话,总体的算法复杂度可以降为O(N),但这种方法需要额外的空间进行Hash表的存储。
#include<cstdio> #include<algorithm> #define MAX 1005 using namespace std; int v[MAX]; int main(int argc,char *argv[]) { int n,m; int i; scanf("%d%d",&n,&m); for(i=0;i<n;i++) { int temp; scanf("%d",&temp); v[temp]++; } for(i=1;i<=m;i++) { if(v[i]>0) { v[i]--; if(v[m-i]>0) { printf("%d %d\n",i,m-i); break; } } } if(i>m) printf("No Solution\n"); return 0; }这段代码的前提是知道元素的取值范围是多少,然后用一个数组进行枚举。
解法三:
还可以换个角度思考问题,假设已经给定了这个数组的任意两个元素的和的有序数组,那么利用二分查找,只需要O(NlogN)就可以解决这个问题。当然我们不大可能去计算这个有序数组,应为它需要O(n*n)的时间。但这个方法仍然启发我们可以对两个数字的和进行一个有序遍历。
首先对数组进行排序,时间复杂度为O(NlogN),然后令i=1,j=n-1看array[i]+array[j]是否等于sum,如果是,则结束。如果小于sum,则i=i+1;如果大于sum,则j=j-1。这样只需要在排好的数组上遍历一次,就可以得到最后结果,时间复杂度为O(N)。两部加起来时间复杂度为O(NlogN)。
#include<cstdio> #include<algorithm> #define MAX 100005 using namespace std; int main(int argc,char *argv[]) { int i,j; int n,m; int v[MAX]; scanf("%d%d",&n,&m); for(i=0;i<n;i++) scanf("%d",&v[i]); sort(v,v+n); int low=0; int high=n-1; while(low<=high-1) { int temp=v[low]+v[high]; if(temp==m) { printf("%d %d\n",v[low],v[high]); break; } else if(temp>m) high--; else if(temp<m) low++; } if(low>high-1) printf("No Solution\n"); return 0; }
相关文章推荐
- 《编程之美》
- 2012年终碎语,编程之美
- 程序员编程艺术3:寻找最小的k个数
- 编程之美2013全国挑战赛资格赛第1题
- 编程之美2013全国挑战赛资格赛第3题
- 编程之美3.8:求二叉树节点的最大距离
- 《编程之美》——中国象棋将帅问题
- 《编程之美》——求二叉树中节点的最大距离(非递归)
- 《编程之美》——求二叉树中节点的最大距离(非递归)
- 《编程之美》——中国象棋将帅问题
- [编程之美2.1]求二进制数中1的个数
- [编程之美2.12]快速寻找满足条件的两个数及leetcode的3 sum closest 和 4 sum解析
- [编程之美2.14]求子数组之和的最大值
- [编程之美2.2]不要被阶乘吓到
- [编程之美2.4]1的数目
- [编程之美3.1]字符串移位包含的问题
- [编程之美2.17]数组循环移位
- [编程之美3.8]求二叉树节点的最大距离
- [编程之美3.9]重建二叉树
- [编程之美3.10]分层遍历二叉树