[英]O(n log(n)) algorithm that checks if sum of 2 numbers in a int[] = given number
我应该创建一个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;
}
}
}
}
与@ 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
他们说的是以下内容:
S=[1,4,7,2,3,4]
使用mergesort对S进行排序,得到Ss=[1,2,3,4,7]
。 成本是O(nlogn)
- 只需检查维基。
然后你有x=8
所以你通过用S
的元素减去x
形成S'=[7,6,5,4,1]
。
使用O(nlogn)
mergesort对S'
排序
删除重复项需要O(n)
。
然后你合并Ss
和S'
。
检查O(n)
中连续位置的重复项。
总计是:
O(nlogn)+O(nlogn)+O(n)+O(n)+O(n) = O(nlogn)
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的内存(使用位而不是像ints代表你的S和你的S',可以很容易地编写一个O(n) ,用于从0到2 ** 31的整数。我在这做了)。
当然,人们可能会认为“但1GB是一个很大的记忆” :但这一切都取决于要求。 我上面的解决方案(或它的优化版本)是O(n)并且可以立即解决由1亿个整数组成的输入,其中任何其他解决方案都会失败(因为你有OutOfMemory错误,因为Java对象的开销)。
首先要问的是“有什么要求?” 。 您需要有关输入的更多信息,因为它始终是一种权衡。
它的工作原理如下:
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));
}
}
您的算法是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}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.