编程之美2.12快速寻找满足条件的2个数
题目:
对于一个数组,快速找出两个数字,让这两个数字的和等于一个给定的值,默认假设数组中肯定有至少一组符合要求。
数组a[]={5,6,1,4,7,8,9},sum=10.
解法一:
穷举,遍历数组中所有的2个数字,相加之和看是否等于给定的数字,时间复杂度为N(N-1)/2,既O(N^2),这种方法效率不高。
1 #include "iostream" 2 using namespace std; 3 4 void sum(int *a,int N){ 5 for(int i=0;i<N;i++){ 6 for(int j=i+1;j<N;j++) 7 if(a[i]+a[j]==10) 8 cout<<"a="<<a[i]<<" b="<<a[j]<<endl; 9 } 10 } 11 int main(){ 12 int data[] = {5, 6, 1, 4, 7, 9, 8}; 13 int length=sizeof(data)/sizeof(data[0]); 14 sum(data,length); 15 }
解法二:
求2个数字之和,假定和为sum,对于元素a[i]只需判断数组中是否存在sum-a[i],无序数组中查找一个数的时间复杂度为O(n),不对数组进行操作的时间复杂度仍为O(n^2)。如果对数组进行排序,然后才去二分查找的方法,查找一个元素的时间复杂度为O(log2n),总时间复杂度为O(n*log2n)。而排序的过程只需要进行一次,时间复杂度为O(N*log2n),所以总时间复杂度仍为O(n*log2n)。
解法三:
换个角度,对于有序序列,从两头分别取数,之和大于sum,j--,之和大于sumi++。将排序号的数组遍历一遍即可输出全部的结果。
1 #include "iostream" 2 #include "algorithm" 3 using namespace std; 4 5 void sum(int *a,int N){ 6 int i,j; 7 for(i = 0,j = N-1;i<j;){ 8 if(a[i]+a[j]==10){ 9 cout<<a[i]<<","<<a[j]<<endl; 10 i++; 11 } 12 else if(a[i]+a[j]<10) 13 i++; 14 else 15 j--; 16 } 17 } 18 int main(){ 19 int data[] = {5, 6, 1, 4, 7, 9, 8}; 20 sort(data, data+7); //快速排序 21 int length=sizeof(data)/sizeof(data[0]); 22 sum(data,length); 23 }
扩展问题:
1,将两个数字改为3个数字。
假设找到了3个数ai,aj,ak,ai+aj+ak=sum,既ai+aj=sum-ak。i,k从左侧遍历,j从右侧遍历。以sum=17为例,会输出三组满足条件的数。
1 #include "iostream" 2 #include "algorithm" 3 using namespace std; 4 5 void sum(int *a,int N){ 6 int i,j,k; 7 for(k=0;k<N;k++) 8 for(i = k+1,j = N-k-1;i<j;){ 9 if(a[i]+a[j]+a[k]==17){ 10 cout<<a[i]<<","<<a[j]<<","<<a[k]<<endl; 11 i++; 12 } 13 else if(a[i]+a[j]+a[k]<17) 14 i++; 15 else 16 j--; 17 } 18 } 19 int main(){ 20 int data[] = {5, 6, 1, 4, 7, 9, 8}; 21 sort(data, data+7); //快速排序 22 int length=sizeof(data)/sizeof(data[0]); 23 sum(data,length); 24 }