Java 确定集合S中是否存在两个元素,它们的和恰好是x-正确解?
摘自算法简介 描述一个Θ(n lgn)-时间算法 给定一组n个整数和 另一个整数x,确定 或者说S中是否存在两种元素 其和正好是x 这是迄今为止我用Java实现的最佳解决方案:Java 确定集合S中是否存在两个元素,它们的和恰好是x-正确解?,java,arrays,algorithm,Java,Arrays,Algorithm,摘自算法简介 描述一个Θ(n lgn)-时间算法 给定一组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 -
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;
}
公共静态布尔测试(int[]a,int val){
合并(a);
对于(int i=0;i=a[i])?val-a[i]:a[i]-val;
if(Arrays.binarySearch(a,i,a.length,diff)>=0){
返回true;
}
}
返回false;
}
现在我的第一个问题是:这是正确的解决方案吗?根据我的理解,mergeSort应该在O(n lgn)中执行排序,循环应该取O(n lgn)(n表示迭代乘以O(lgn)表示二进制搜索,结果是O(2n lgn),因此它应该是正确的
我的第二个问题是:有没有更好的解决方案?数组排序是否必要?您的分析是正确的,是的,您必须对数组进行排序,否则二进制搜索无法工作。您的解决方案似乎很好。是的,您需要排序,因为这是二进制搜索的先决条件。您可以对逻辑稍作修改,如下所示:
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;
}
公共静态布尔测试(int[]a,int val)
{
数组。排序(a);
int i=0;//第一个元素的索引。
int j=a.length-1;//最后一个元素的索引。
while(i val)
j--;
//否则,如果总和小于val,则增加总和。
其他的
i++;
}
//找不到任何这样的对..返回false。
返回false;
}
我确实认为我在您的实现中发现了一个小错误,但是测试应该会很快发现这个错误
该方法看起来有效,并将达到所需的性能。您可以通过将迭代二进制搜索替换为对数组的扫描来简化该方法,实际上,将二进制搜索替换为线性搜索,并在上一次线性搜索停止时恢复:
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!
}
}
intj=a.length-1;
for(int i=0;ival){
j--;
}
如果(a[i]+a[j]==val){
//海瑞卡!
}
}
这一步是O(n)。(证明这一点留给您作为练习。)当然,整个算法仍然需要O(n logn)作为合并排序
a[i]
是大于还是小于val
,您仍然需要diff是val-a[i]
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;
}
公共静态布尔测试(int[]a,int val){
Set=newhashset();
//在数组中查找val/2
int c=0;
对于(int n:a){
如果(n*2==val)
++c
}
如果(c>=2)
返回true;//是!-找到多个
//现在查看不包括val/2的配对
set.addAll(Arrays.asList(a));
对于(int n:a){
如果(n*2==val)
继续;
if(set.contains(val-n))
返回true;
}
返回false;
}
还有另一个非常快速的解决方案:假设您必须用Java解决这个问题,大约需要10亿个整数。您知道,在Java中,整数从-2**31+1
到+2**31
创建一个具有2**32
10亿位(500 MB,在今天的硬件上做起来很简单)的阵列
在集合上迭代:如果您有一个整数,则将相应的位设置为1
到目前为止
在集合上再次迭代:对于每个值,检查是否在“current val-x”处设置了位
如果有,则返回true
当然,它需要500 MB的内存
但是,如果你有,比如说,用10亿个整数来解决这个问题,这个问题将围绕任何其他的O(n logn)解运行
O(n).一个简单的解决方案是,排序后,将指针从数组的两端向下移动,寻找和x的对。如果和太高,则减少右指针。如果太低,则增加左指针。如果指针交叉,答案是否。这里是一个替代解决方案,通过在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]);
}
}
}
}
publicstaticvoiddivide(int数组[],int开始,int结束,int和){
if(array.length<2 | |(start>=end)){
返回;
}
int mid=(开始+结束)>>1;//[p+r/2]
//分开
如果(开始<结束){
除法(数组、开始、中间、和);
除法(数组,中间+1,结束,和);
校验和(数组、开始、中间、结束、和);
}
}
私有静态无效校验和(int[]数组、int str、int mid、int end、int sum){
int lsize=mid-str+1;
int rsize=结束-中间;
int[]l=newint[lsize];//初始化
int[]r=newint[rsize];//初始化
//副本L
对于(int i=str;i is heureka是启发式和eureka的混合体?@Bishiboosh:不,是希腊单词的德语音译。我不知道英文音译去掉了H。你从堆栈溢出中学到的东西…:-)你可以使用哈希集吗?如果可以,那么你可以使用O(n),见下文。(val>=a[i])val-a[i]:a[i]-val
这就是Math.abs()
的作用:)是的,Math.abs
就是为了这个,但我想说的更多:这里不需要它。事实上是错误的。diff
必须始终被赋值val-a[I]
无论这种差异是积极的还是消极的。正如下面几个答案中所建议的,有一个更好的O(n)sol'n给出了一些关于输入的假设。是的,排序似乎是必要的,但一旦你有了一个已排序的列表,你就可以改进。不要天真地使用二进制搜索,你可以考虑以下方法:初始化指针l,r到列表的开始和结束。选中a[l]+a[r]=f;如果