![](/img/trans.png)
[英]How do I use a recursive algorithm to determine whether the array contains two elements that sum to a given integer?
[英]Determine whether or not there exist two elements in Set S whose sum is exactly x - correct solution?
摘自算法简介
描述一个Θ(n lg n)时间算法,给定一组n个整数和另一个整数x,确定S中是否存在两个元素,其和是x。
到目前为止,这是我用Java实现的最佳解决方案:
public static boolean test(int[] a, int val) {
mergeSort(a);
for (int i = 0; i < a.length - 1; ++i) {
int diff = (val >= a[i]) ? val - a[i] : a[i] - val;
if (Arrays.binarySearch(a, i, a.length, diff) >= 0) {
return true;
}
}
return false;
}
现在我的第一个问题是:这是一个正确的解决方案吗? 根据我的理解,mergeSort应该在O(n lg n)中执行排序,循环应该取O(n lg n)(n用于迭代乘以O(lg n)进行二进制搜索,得到O(2 l lg) n),所以它应该是正确的。
我的第二个问题是:有更好的解决方案吗? 排序阵列必不可少?
您的解决方案似乎很好 是的,你需要排序,因为它是二进制搜索的先决条件。 您可以对逻辑稍作修改,如下所示:
public static boolean test(int[] a, int val)
{
Arrays.sort(a);
int i = 0; // index of first element.
int j = a.length - 1; // index of last element.
while(i<j)
{
// check if the sum of elements at index i and j equals val, if yes we are done.
if(a[i]+a[j] == val)
return true;
// else if sum if more than val, decrease the sum.
else if(a[i]+a[j] > val)
j--;
// else if sum is less than val, increase the sum.
else
i++;
}
// failed to find any such pair..return false.
return false;
}
还有另一个非常快速的解决方案:想象一下,你必须在Java中解决这个大约10亿个整数的问题。 你知道在Java中整数从-2**31+1
到+2**31
。
创建一个2**32
亿位的数组(500 MB,在今天的硬件上做的很简单)。
迭代你的集合:如果你有一个整数,将相应的位设置为1。
到目前为止O(n)。
在您的集合上再次迭代:对于每个值,检查您是否在“当前val - x”处设置了位。
如果你有一个,你就会返回true。
当然,它需要500 MB的内存。
但是,如果您有10亿个整数来解决这个问题,那么这应该围绕任何其他O(n log n)解决方案运行。
上)。
这是对的; 你的算法将在O(n lg n)时间内运行。
有一个更好的解决方案:您计算diff的逻辑是不正确的。 无论a[i]
是否大于或小于val
,你仍然需要diff为val - a[i]
。
这是使用哈希集的O(n)解决方案:
public static boolean test(int[] a, int val) {
Set<Integer> set = new HashSet<Integer>();
// Look for val/2 in the array
int c = 0;
for(int n : a) {
if(n*2 == val)
++c
}
if(c >= 2)
return true; // Yes! - Found more than one
// Now look pairs not including val/2
set.addAll(Arrays.asList(a));
for (int n : a) {
if(n*2 == val)
continue;
if(set.contains(val - n))
return true;
}
return false;
}
一个简单的解决方案是,在排序之后,从数组的两端向下移动指针,寻找总和为x的对。 如果总和太高,则递减右指针。 如果太低,则增加左边的一个。 如果指针交叉,答案是否定的。
我确实认为我在你的实现中发现了一个小错误,但测试应该很快发现。
该方法看起来有效,并将达到所需的性能。 您可以通过使用扫描数组替换迭代二进制搜索来简化它,实际上通过线性搜索替换二进制搜索,线性搜索将恢复前一个线性搜索中断的位置:
int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
while (a[i] + a[j] > val) {
j--;
}
if (a[i] + a[j] == val) {
// heureka!
}
}
该步骤是O(n)。 (证明这是留给你的练习。)当然,整个算法仍然需要O(n log n)进行合并排序。
您的分析是正确的,是的,您必须对数组进行排序,否则二进制搜索不起作用。
这是一个替代解决方案,通过向mergesort添加更多条件。
public static void divide(int array[], int start, int end, int sum) {
if (array.length < 2 || (start >= end)) {
return;
}
int mid = (start + end) >> 1; //[p+r/2]
//divide
if (start < end) {
divide(array, start, mid, sum);
divide(array, mid + 1, end, sum);
checkSum(array, start, mid, end, sum);
}
}
private static void checkSum(int[] array, int str, int mid, int end, int sum) {
int lsize = mid - str + 1;
int rsize = end - mid;
int[] l = new int[lsize]; //init
int[] r = new int[rsize]; //init
//copy L
for (int i = str; i <= mid; ++i) {
l[i-str] = array[i];
}
//copy R
for (int j = mid + 1; j <= end; ++j) {
r[j - mid - 1] = array[j];
}
//SORT MERGE
int i = 0, j = 0, k=str;
while ((i < l.length) && (j < r.length) && (k <= end)) {
//sum-x-in-Set modification
if(sum == l[i] + r[j]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + r[j]);
}
if (l[i] < r[j]) {
array[k++] = l[i++];
} else {
array[k++] = r[j++];
}
}
//left over
while (i < l.length && k <= end) {
array[k++] = l[i++];
//sum-x-in-Set modification
for(int x=i+1; x < l.length; ++x){
if(sum == l[i] + l[x]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + l[x]);
}
}
}
while (j < r.length && k <= end) {
array[k++] = r[j++];
//sum-x-in-Set modification
for(int x=j+1; x < r.length; ++x){
if(sum == r[j] + r[x]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + r[j] + " " + r[x]);
}
}
}
}
但这种算法的复杂性仍不等于THETA(nlogn)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.