我应该创建一个O(n log(n))
算法来检查int [] ==给定数字中是否有2个数字的总和。
例如。给定[1,4,7,2,3,4]将得到总和8(1 + 7)但不是20
给出的答案建议使用二进制排序或合并排序,但他们只给出了合并排序算法,而没有逻辑处理这个特殊要求。然后另一个答案是:
假设x是我们要检查的总和,z是中的元素集 这个数组:以下算法解决了这个问题:
- 对S中的元素进行排序。
- 形成集合S'= {z:z = x - y表示某些y∈S}。
- 对S'中的元素进行排序。
- 如果S中的任何值出现多次,则删除除一个实例外的所有值。为S'做同样的事。
- 合并两个已排序的集合S和S'。
- S中存在两个元素,当且仅当相同的值出现在合并输出的连续位置时,其总和才是x。
醇>为证明第4步中的声明,首先要注意是否有任何值 在合并输出中出现两次,它必须连续出现 位置。因此,我们可以在步骤5中重述条件,因为存在 S中的两个元素,当且仅当相同的值时,其总和正好为x 在合并输出中出现两次。假设出现一些值w 两次。然后在S中出现一次,在S'出现一次。因为w出现了 S'存在一些y∈S,使得w = x-y,或x = w + y。自从w ∈S,元素w和y在S中,并且与x相加。
相反,假设存在值w,y∈S使得w + y = X。然后,由于x-y = w,值w出现在S'中。因此,w在 S和S',因此在合并输出中会出现两次。
步骤1和3需要O(n log n)步骤。步骤2,4,5和6需要 O(n)步骤。因此总运行时间为O(n log n)。
但我真的不是他们的意思。在步骤2中,x和y是什么?
但是我在下面自己创建了,我想知道它是O(n log(n))
吗?
class FindSum {
public static void main(String[] args) {
int[] arr = {6,1,2,3,7,12,10,10};
int targetSum = 20;
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
int end = arr.length - 1;
if (FindSum.binarySearchSum(arr, targetSum, 0, end, 0, end)) {
System.out.println("Found!");
} else {
System.out.println("Not Found :(");
}
}
public static boolean binarySearchSum(int[] arr, int targetSum,
int from1, int end1,
int from2, int end2) {
// idea is to use 2 "pointers" (simulating 2 arrays) to (binary) search
// for target sum
int curr1 = from1 + (end1-from1)/2;
int curr2 = from2 + (end2-from2)/2;
System.out.print(String.format("Looking between %d to %d, %d to %d: %d, %d", from1, end1, from2, end2, curr1, curr2));
int currSum = arr[curr1] + arr[curr2];
System.out.println(". Sum = " + currSum);
if (currSum == targetSum) {
// base case
return true;
} else if (currSum > targetSum) {
// currSum more than targetSum
if (from2 != end2) {
// search in lower half of 2nd "array"
return FindSum.binarySearchSum(arr, targetSum, from1, end1, from2, curr2 - 1);
} else if (from1 != end2) {
// search in lower half of 1st "array" (resetting the start2, end2 args)
return FindSum.binarySearchSum(arr, targetSum, from1, curr1 - 1, 0, arr.length - 1);
} else {
// can't find
return false;
}
} else {
// currSum < targetSum
if (from2 != end2) {
// search in upper half of 2nd "array"
return FindSum.binarySearchSum(arr, targetSum, from1, end1, curr2 + 1, end2);
} else if (from1 != end2) {
// search in upper half of 1st "array" (resetting the start2, end2 args)
return FindSum.binarySearchSum(arr, targetSum, curr1 + 1, end1, 0, arr.length - 1);
} else {
// can't find
return false;
}
}
}
}
答案 0 :(得分:5)
与@ user384706类似,但您可以在O(n)
。
他们说的是以下内容: S = [1,4,7,2,3,4]
将这些添加到HashSet,理想情况下是TIntHashSet(但时间复杂度相同)
int total = 9;
Integer[] S = {1, 4, 7, 2, 3, 4, 6};
Set<Integer> set = new HashSet<Integer>(Arrays.asList(S));
for (int i : set)
if (set.contains(total - i))
System.out.println(i + " + " + (total - i) + " = " + total);
打印
2 + 7 = 9
3 + 6 = 9
6 + 3 = 9
7 + 2 = 9
答案 1 :(得分:4)
他们说的是以下内容:
S=[1,4,7,2,3,4]
使用mergesort对S进行排序,得到Ss=[1,2,3,4,7]
。费用为O(nlogn)
- 只需查看维基。
然后你有x=8
因此,您可以使用S'=[7,6,5,4,1]
中的元素减去x
来形成S
使用S'
中的mergesort对O(nlogn)
进行排序
删除重复项需要O(n)
。
然后合并Ss
和S'
您在O(n)
的连续位置检查重复项
总计是:
O(nlogn)+O(nlogn)+O(n)+O(n)+O(n) = O(nlogn)
答案 2 :(得分:3)
O(n)解决方案怎么样?
从您的问题中不清楚您是否应该使用您所描述的“另一个答案”[原文如此]或者您是否可以提出自己的解决方案。
你应该问的第一件事是“有什么要求?”因为有局限性。您将获得的最大整数数是多少?两百万?一千万?这些整数的范围是多少?在你的问题中,他们似乎总是大于零。这些整数的最大值是多少?你可以使用多少内存?
因为总是权衡。
例如,这是一个非优化的(见下文) O(n)解决问题的方法(我在输入中添加了'8'):
@Test
public void testIt() {
final int max = 10000000;
final int[] S = new int[max+1];
final int[] in = { 1, 4, 3, 2, 4, 7, 8 };
for ( final int i : in ) {
S[i]++;
}
assertFalse( containsSum(S, 1) );
assertFalse( containsSum(S, 2) );
assertTrue( containsSum(S, 3) );
assertTrue( containsSum(S, 4) );
assertTrue( containsSum(S, 5) );
assertTrue( containsSum(S, 6) );
assertTrue( containsSum(S, 7) );
assertTrue( containsSum(S, 8) );
assertTrue( containsSum(S, 9) );
assertTrue( containsSum(S, 10) );
assertTrue( containsSum(S, 11) );
assertFalse( containsSum(S, 13) );
assertFalse( containsSum(S, 14) );
assertTrue( containsSum(S, 12) );
assertTrue( containsSum(S, 15) );
assertFalse( containsSum(S, 16) );
}
private static boolean containsSum( final int[] ar, final int sum ) {
boolean found = false;
for (int i = 1; i < sum && !found; i++) {
final int b = sum - i;
found = i == b ? ar[i] > 1 : ar[i] > 0 && ar[b] > 0;
}
return found;
}
它没有经过优化,因为使用“仅”1 GB的内存(你代表你的S)可以很容易地为0到2 ** 31的整数编写 O(n)而你的S'使用比特而不是像我在这里做的那样。)
当然,有人可能认为“但1GB是很多内存”:但这一切都取决于要求。我上面的解决方案(或它的优化版本)是 O(n)并且可以立即解决由1亿个整数组成的输入,其中任何其他解决方案都会失败(因为你'由于Java对象的开销,导致 OutOfMemory 错误。
首先要问的是“有什么要求?”。您需要有关输入的更多信息,因为它始终是一种权衡。
答案 3 :(得分:0)
您的算法是O(n log n)。每次将第一个数组大小除以2或在第二个数组上进行二进制搜索。这是O((log n)^ 2)最坏的情况(即S = {1,2 ...,n}和x = 0),因此它被排序吸收。
无论如何,你可以通过以下方式在O(n log n)中轻松完成:
编辑: 回答你的第一个问题。 x是您要查找的总和,y是输入集的元素。因此,如果S = {y_1,y_2,y_3,...,y_n},那么S'= {x - y_1,x - y_2,x - y_3,... x - y_n}
答案 4 :(得分:0)
它的工作原理如下:
public class TwoSumFaster {
private static int countTwoSum(int[] numbers) {
int count = 0;
int front = 0, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] == 0) {
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
Arrays.sort(numbers);
System.out.println(countTwoSum(numbers));
}
}