能否快速找出一个数组中的两个数字,让这个两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定至少存在一组满足要求的解。
作者给出了3种方案:
[方案一]穷举法:从数组任意取出两个数字,计算两者之和是否为给定的数字。时间复杂度O(N^2),效率较低。
[方案二]将问题变通为一个查找问题:求两个数之和,假定为sum。对于数组中的每个数array[i],判断sum-array[i]是否在数组中。在一个无序数组中查找一个数的复杂度是O(N),对于每个数字都要进行相应的查找,则时间复杂度仍为O(N^2)。为了提高效率,可以相对数组进行排序,然后利用二分查找算法,时间复杂度为O(NlogN)。
[方案三]利用hash表提高查找效率,因为给定一个数字,根据hash映射查找另一个数字是否在数组中,只需用O(1)的时间,查找时间为O(1),总的时间复杂度就为O(N)。但是这会使得空间复杂度也为O(N)。
[方案四]先对数组进行排序,然后对数组中的两个数之和进行一个有序的遍历。具体算法:先令i=0,j=n-1,判断array[i]+array[j]是否等于sum,如相等则结束;如小于则i向右移动一个位置,否则j向左移动一个位置。这样时间复杂度为排序O(NlogN)+查找O(N) = O(N(logN+1))
[扩展问题1]:如果将“两个数字”改为“三个数字”或“任意个数字”时,如何求解?
[方案]对于“三个数字”其实就是对于数组中的每个数字array[i]判断数组中的其他数字是否存在两个数字的和为sum-array[i],以此递归下去。
int index = 0; bool find(int array[], int n, int start, int m, int sum, int result[]){ assert(m >= 2); if(n-start < m) return false; int i,j; if(m == 2){ for(i=start,j=n;i<j;){ if(array[i] + array[j] == sum){ result[index++] = array[i]; result[index++] = array[j]; return true; } else if(array[i] + array[j] < sum) i++; else j--; } return false; }else{ for(i=start;i<n;i++){ result[index++] = array[i]; if(find(array,n,i+1,m-1,sum - array[i],result)){ return true; } index--; } return false; } return false; }
[扩展问题2]:如果完全相等的一对数字找不到,能否找出和最接近的解? 算法实现(C语言):
void findNearest(int array[], int n, int sum, int result[]){ int i,j; int minDiff = INT_MAX; for(i=0,j=n-1;i<j;){ if(array[i]+array[j] == sum){ result[0] = array[i]; result[1] = array[j]; return; }else{ int diff = array[i]+array[j] -sum; int tmp_i = array[i]; int tmp_j = array[j]; if(diff > 0) j--; else{ i++; diff = -diff; } if(diff < minDiff){ minDiff = diff; result[0] = tmp_i; result[1] = tmp_j; } } } }
转自:http://blog.csdn.net/andyliuxs/article/details/7537128
给定一个数N和一组数字集合S,在S中找到和最接近N的子集。