http://blog.csdn.net/linyunzju/article/details/7720413
这篇文章总结的比较好,代码也写的比较清晰。
原创部分:
上面的博文,在应用“两个下标夹逼”解决“三个数字”问题、能否“重复使用数字”两个方面,给出了非常好的解释,代码清晰明了,不愧是浙大才子。下面,自己补充一些东西。
1、用排序+查找思路解决“三个数字”问题。
先确定两个数字,是O(N^2),再在剩下的排好序的数组中二分查找需要的数字,最后的时间复杂度是:O(N^2*logN)
2、如果完全相等的一对数字找不到,能否找出和最接近的解?
①应用排序+查找的思路:
先排序,需要O(NlogN),然后取每一个元素a[i],为了找到a[j],使和最接近sum,即变成在排好序的数组中找一个数,使该数字与a[i]最接近。可以想象,使用二分查找,如果sum-a[i]存在于数组中,则可以直接命中;如果不存在,则查找的区间最终缩小到两个数字,一个大于sum-a[i],一个小于sum-a[i],则这两个数字中比较接近的保留下俩,作为a[i]的最佳匹配对象,如此遍历所有的i,选出误差最小的i即可。注意,这里不需要存储所有的误差,只需要存储当前遍历的i,j中误差最小值及相应的i,j即可。时间复杂度依然是:O(NlogN +N*logN) = O(NlogN)。
②两个下标夹逼思路:
同样是先排序,然后取收尾两个下标i=0,j=N-1,求和并保存其误差,若误差为正,则j--,若误差为负,则i++。在满足i<j的条件下,如此循环下去,找出误差绝对值最小的一组ij即可。注意,这里不需要存储所有的误差,只需要存储当前遍历的i,j中误差最小值及相应的i,j即可。
转载部分:
问题:
1. 快速找出一个数组中的两个数,让这两个数之和等于一个给定的值。
2. 快速找出一个数组中的三个数,让这三个数之和等于一个给定的值。
1. 解法:算法复杂度为O(nlogn)。先用快速排序对数组排序,让后用双指针(双索引)法对排序好的数组进行反向遍历,并且遍历的方向不变。(若是计算两个数的和,则初始化为i=0,j=n-1,若是计算两个数的差,则初始化为i=0,j=1)
之所以这样遍历方式能成功,是因为排序后,若ai+aj<sum,则ai+ak<sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有i++才可能有等号的情况;若ai+aj>sum,则ak+aj>sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有j--才可能有等号的情况。
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1001
- int A[MAXN];
- // 总的时间复杂度为O(nlogn)
- int main()
- {
- int n, sum, i, j;
- cin >> n >> sum;
- for (i=0; i<n; i++)
- cin >> A[i];
- // 快速排序O(nlogn)
- sort(A, A+n);
- // 双索引反向遍历
- i=0; j=n-1;
- // 每个数只能用一次(若每个数可以用多次,则用<=)
- while (i<j)
- {
- int plus = A[i]+A[j];
- if (plus == sum)
- {
- printf("(%d,%d) ",A[i],A[j]);
- i++, j--;
- }
- else if (plus < sum)
- i++;
- else
- j--;
- }
- }
2. 解法:时间复杂度为O(n^2)。如果数组已排序,利用解法1的双指针遍历法,可以在O(n)的时间内找到两个数之和等于一个給定的数。我们假设找到的三个数ai,aj,ak有ai<=aj<=ak,要让ai+aj+ak=sum,也就是要ai+aj=sum-ak,设subsum=sum-ak,很容易发现subsum的值只有n个,而确定ai+aj=subsum中的ai,aj只需要O(n)的时间,所以总的时间复杂度为O(nlogn+n*n)=O(n^2)
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1001
- int A[MAXN];
- int main()
- {
- int n, sum, i, j, k;
- cin >> n >> sum;
- for (i=0; i<n; i++)
- cin >> A[i];
- sort(A, A+n);
- for (k=0; k<n; k++)
- {
- i=0; j=k-1;
- if (k==i) i++;
- if (k==j) j--;
- int subsum = sum-A[k];
- while (i<j)
- {
- int plus = A[i]+A[j];
- if (plus == subsum)
- {
- printf("(%d,%d,%d) ",A[i],A[j],A[k]);
- i++, j--;
- }
- else if (plus < subsum)
- i++;
- else
- j--;
- if (k==i) i++;
- if (k==j) j--;
- }
- }
- }
若允许每个数被多次选取,只需稍微改下代码,具体如下:
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1001
- int A[MAXN];
- int main()
- {
- int n, sum, i, j, k;
- cin >> n >> sum;
- for (i=0; i<n; i++)
- cin >> A[i];
- sort(A, A+n);
- for (k=0; k<n; k++)
- {
- i=0; j=k;
- //if (k==i) i++;
- //if (k==j) j--;
- int subsum = sum-A[k];
- while (i<=j)
- {
- int plus = A[i]+A[j];
- if (plus == subsum)
- {
- printf("(%d,%d,%d) ",A[i],A[j],A[k]);
- i++, j--;
- }
- else if (plus < subsum)
- i++;
- else
- j--;
- //if (k==i) i++;
- //if (k==j) j--;
- }
- }
- }
xjs.xjtu@gmail.com
2012-09-02