首页 > 代码库 > 快速寻找满足条件的两个数
快速寻找满足条件的两个数
时间:2014.07.17
地点:基地
-------------------------------------------------------------------------------------
一、问题描述
给定一个数组,要求快速查找出其中的两个值,他们的和为一个给定的值。
比如给定数组:1 4 5 6 8 9,和给定值9,我们能找出4+5=9为所要的数值
-------------------------------------------------------------------------------------
二、各种解法
2.1穷举法
对给定集合从中取出任意值求和,看是否等于给定和值,显然由组合公式知:其复杂度为N(N-1)/2,即O(N^2)。我们要求尽可能降低算法时间复杂度和算法空间复杂度,这个算法比较原始。
2.2先排序后查找
假定给定数字和为sum,对数组中的每个数字arr[i],理想上是要有另外一个数字sum-arr[i]存在数组中即为查找成功,于是问题转换成了一个查找问题,而我们知道查找算法中最快的莫过于二分查找,时间复杂度为O(logN),但二分查找的前提是有序序列,于是我们要先对数组进行排序,我们也知道快排、堆排、归排等都是时间复杂度为O(NlogN)的排序算法,于是针对数组中的每个数arr[i],都需要花费O(logN)的时间去查找对应的sum-arr[i]是否存在,于是总的时间复杂度为O(NlogN),在这里我们排序也需耗时O(NlogN),但好在排序只需一次,于是时间复杂度依旧还是O(NlogN)。
使用二分查找时要注意:
1.将数组划分为两个子部分时,中间分界为:
middle=first+size/2
2.左边子部分可能略多,从first开始,大小为size/2
3.右边子部分可能略少,从middle+1开始,大小为(size-1)/2
2.3使用哈希查找
在2.2节,我们已经将问题转换成了查找问题,事实上,对于查找问题,若不惜代价追求速度的话,哈希查找最快,每次查找时间复杂度为O(1),于是在这里我们只需O(N)即可完成任务。此种方法需要增加额外的存储空间。
2.4双端扫描
首先若数组无序,排序还是要排,时间复杂度为O(NlogN),但若数组有序,则可直接O(N)完成任务,在这里我们假设数组是有序的,现在才用双指针双端扫描法对整个数组扫描的策略:
首先,设定两个指针begin 和 end,分别指向数组首和尾,
然后,逐次判断arr[*begin]+arr[*end]和给定的sum进行大小比较
i.若大于,说明两数和过大,执行--end,缩减两数和
ii.若小于,说明两数和过小,执行++begin,放大两数和
iii.若等于,正是我们所要,返回这个数值对
双端扫描的代码如下:
pair FindSumPair(int* arr, int n, int sum) { //Sort(arr,arr+n); //若数组无序,则先对数组进行排序 int* begin = arr; int* end = arr + n - 1; int tem_sum = 0; while (begin < end) { tem_sum = (*begin) + (*end); if (tem_sum>sum) --end; else if (tem_sum < sum) ++begin; else return pair(*begin, *end); } return pair(-1, -1); }
-------------------------------------------------------------------------------------
三、扩展
2010年中兴面试题-编程求解:输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m, 要求将其中所有的可能组合列出来。
引用算法之道JULY博客中的代码如下:
#include<list> #include<iostream> using namespace std; list<int>list1; void find_factor(int sum, int n) { // 递归出口 if(n <= 0 || sum <= 0) return; // 输出找到的结果 if(sum == n) { // 反转list list1.reverse(); for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++) cout << *iter << " + "; cout << n << endl; list1.reverse(); } list1.push_front(n); //典型的01背包问题 find_factor(sum-n, n-1); //放n,n-1个数填满sum-n list1.pop_front(); find_factor(sum, n-1); //不放n,n-1个数填满sum } int main() { int sum, n; cout << "请输入你要等于多少的数值sum:" << endl; cin >> sum; cout << "请输入你要从1.....n数列中取值的n:" << endl; cin >> n; cout << "所有可能的序列,如下:" << endl; find_factor(sum,n); return 0; }