问题:给定一个未排序的正整数数组,是否有可能从该数组中找到一对总和达到给定总和的整数?
约束:这应该在O(n)和就地(没有任何外部存储,如数组,哈希映射)完成(你可以使用额外的变量/指针)
如果无法做到这一点,是否可以提供相同的证明?
答案 0 :(得分:53)
如果你有一个排序数组,你可以通过向中间移动两个指针在O(n)中找到这样的一对
i = 0
j = n-1
while(i < j){
if (a[i] + a[j] == target) return (i, j);
else if (a[i] + a[j] < target) i += 1;
else if (a[i] + a[j] > target) j -= 1;
}
return NOT_FOUND;
如果您对数字的大小有界限(或者如果数组已经排在第一位),则可以对O(N)进行排序。即使这样,log n因子也很小,我不想费心去除它。
<强>证明:强>
如果有解决方案(i*, j*)
,请假设i
在i*
到达j
之前达到j*
而不失一般性。对于j'
和j*
之间的所有j
,我们知道a[j'] > a[j*]
我们可以推断a[i] + a[j'] > a[i*] + a[j*] = target
,因此,算法的所有后续步骤都将导致j减少直到达到j*
(或相等的值)而不给i
前进并“错过”解决方案的机会。
另一方面的解释是相似的。
答案 1 :(得分:12)
适用于已排序数组的O(N)
时间和O(1)
空间解决方案:
让M
成为您追求的价值。使用两个指针X
和Y
。从头开始X=0
,在结尾开始Y=N-1
。计算总和sum = array[X] + array[Y]
。如果sum > M
,则递减Y
,否则递增X
。如果指针交叉,则不存在解决方案。
您可以对一般数组进行排序,但我不确定通常会有O(N)
时间和O(1)
空间解决方案。
答案 2 :(得分:3)
如果数组包含数字,这可能是可能的,数字的上限是事先知道的。然后使用计数排序或基数排序(o(n))并使用@PengOne建议的算法。
否则 我想不出O(n)解决方案。但O(nlgn)解决方案的工作原理如下: -
首先使用合并排序或快速排序(对于inplace)对数组进行排序。查找sum - array_element是否在此排序数组中。 可以使用二进制搜索。
So total time complexity: O(nlgn) + O(lgn) -> O(nlgn).
答案 3 :(得分:3)
AS @PengOne提到它在一般方案中是不可能的。但是如果你对i / p数据做了一些限制。
步骤1:将小于SUM的所有元素移动到数组的开头,比如N通道我们将数组分成[0,K]&amp; [K,N-1]使得[0,K]包含元素&lt; = SUM。
步骤2:由于我们知道边界(0到SUM),我们可以使用基数排序。
步骤3:在A [K]上使用二进制搜索,一个好处是,如果我们需要找到互补元素,我们只需要查看数组A [K]的一半。所以在A [k]中我们迭代A [0到K / 2 + 1]我们需要在A [i到K]中进行二分搜索。
总的appx。时间是,N + K + K / 2 lg(K)其中K是元素数btw 0到Sum in i / p A [N]
注意:如果你使用@ PengOne的方法,你可以用K做第3步。所以总时间是N + 2K,绝对是O(N)
我们不使用任何额外的内存,但会破坏i / p阵列,这也不错,因为它没有任何排序开始。
答案 4 :(得分:2)
这是一个O(N)算法。它依赖于an in-place O(N) duplicate removal algorithm,并且在数组中存在一个良好的哈希函数。
首先,从阵列中删除所有重复项。
其次,遍历数组,并用min(x,S-x)替换每个数字x,其中S是您想要达到的总和。
第三,找出数组中是否有重复项:如果'x'重复,那么原始数组中必须出现'x'和'S-x',并且找到了你的对。
答案 5 :(得分:2)
取两个指针,一个从数组的第0个索引开始,另一个从数组末尾开始说(n-1)。
运行循环直到低<=高
Sum = arr[low] + arr[high]
if(sum == target)
print low, high
if(sum < target)
low++
if(sum > target)
high--
步骤2到10需要O(n)时间,并且计数排序需要O(n)。总时间复杂度为O(n)。
答案 6 :(得分:2)
以下网站提供了一个简单的解决方案,使用hashset查看数字,然后在hashset中搜索给定的sum-current数 http://www.dsalgo.com/UnsortedTwoSumToK.php
答案 7 :(得分:2)
首先,使用radix sort对数组进行排序。那会让你回到O(kN)。然后继续@PengOne建议。
答案 8 :(得分:1)
这是一个考虑重复条目的解决方案。它是用javascript编写的,使用排序和未排序的数组运行。该解决方案在O(n)时间内运行。
var count_pairs_unsorted = function(_arr,x) {
// setup variables
var asc_arr = [];
var len = _arr.length;
if(!x) x = 0;
var pairs = 0;
var i = -1;
var k = len-1;
if(len<2) return pairs;
// tally all the like numbers into buckets
while(i<k) {
asc_arr[_arr[i]]=-(~(asc_arr[_arr[i]]));
asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
i++;
k--;
}
// odd amount of elements
if(i==k) {
asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
k--;
}
// count all the pairs reducing tallies as you go
while(i<len||k>-1){
var y;
if(i<len){
y = x-_arr[i];
if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[i]])>1) {
if(_arr[i]==y) {
var comb = 1;
while(--asc_arr[_arr[i]] > 0) {pairs+=(comb++);}
} else pairs+=asc_arr[_arr[i]]*asc_arr[y];
asc_arr[y] = 0;
asc_arr[_arr[i]] = 0;
}
}
if(k>-1) {
y = x-_arr[k];
if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[k]])>1) {
if(_arr[k]==y) {
var comb = 1;
while(--asc_arr[_arr[k]] > 0) {pairs+=(comb++);}
} else pairs+=asc_arr[_arr[k]]*asc_arr[y];
asc_arr[y] = 0;
asc_arr[_arr[k]] = 0;
}
}
i++;
k--;
}
return pairs;
}
从阵列的两侧开始,慢慢向内工作,记住每个号码被发现的次数。一旦到达中点,所有数字都会被计算出来,您现在可以在进行计数时对两个指针进行计数。
它只计算对,但可以重新编写
享受!
答案 9 :(得分:1)
Ruby实现
ar1 = [ 32, 44, 68, 54, 65, 43, 68, 46, 68, 56]
for i in 0..ar1.length-1
t = 100 - ar1[i]
if ar1.include?(t)
s= ar1.count(t)
if s < 2
print s , " - " , t, " , " , ar1[i] , " pair ", i, "\n"
end
end
end
答案 10 :(得分:1)
在javascript中:当n大于n时,此代码会增加时间和迭代次数。程序执行的测试数将等于((n *(n / 2)+ n / 2),其中n是元素数。如果(arr [i] + arr [j ] === 0),其中0可以是任意数字。
var arr = [-4, -3, 3, 4];
var lengtharr = arr.length;
var i = 0;
var j = 1;
var k = 1;
do {
do {
if (arr[i] + arr[j] === 0) { document.write(' Elements arr [' + i + '] [' + j + '] sum 0'); } else { document.write('____'); }
j++;
} while (j < lengtharr);
k++;
j = k;
i++;
} while (i < (lengtharr-1));
答案 11 :(得分:0)
这是python中的解决方案:
a = [9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 2, 8, 9, 2, 2, 8,
9, 2, 15, 11, 21, 8, 9, 12, 2, 8, 9, 2, 15, 11, 21, 7, 9, 2, 23, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 12, 9, 2, 15, 11, 21, 8, 9, 2, 2,
8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 7.12, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9,
2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 0.87, 78]
i = 0
j = len(a) - 1
my_sum = 8
finded_numbers = ()
iterations = 0
while(OK):
iterations += 1
if (i < j):
i += 1
if (i == j):
if (i == 0):
OK = False
break
i = 0
j -= 1
if (a[i] + a[j] == my_sum):
finded_numbers = (a[i], a[j])
OK = False
print finded_numbers
print iterations
答案 12 :(得分:0)
不保证是可能的;如何选择给定的金额?
示例:未排序的整数数组
2, 6, 4, 8, 12, 10
给定总和:
7
...
答案 13 :(得分:0)
在接受采访时我被问到同样的问题,这是我想到的计划。还有一个改进,允许负数,但只需要修改索引。空间方面并不好,但我相信这里的运行时间是O(N)+ O(N)+ O(N的子集) - &gt;上)。我可能错了。
void find_sum(int *array_numbers, int x){
int i, freq, n_numbers;
int array_freq[x+1]= {0}; // x + 1 as there could be 0’s as well
if(array_numbers)
{
n_numbers = (int) sizeof(array_numbers);
for(i=0; i<n_numbers;i++){ array_freq[array_numbers[i]]++; } //O(N)
for(i=0; i<n_numbers;i++)
{ //O(N)
if ((array_freq[x-array_numbers[i]] > 0)&&(array_freq[array_numbers[i]] > 0)&&(array_numbers[i]!=(x/2)))
{
freq = array_freq[x-array_numbers[i]] * array_freq[array_numbers[i]];
printf(“-{%d,%d} %d times\n”,array_numbers[i],x-array_numbers[i],freq );
// “-{3, 7} 6 times” if there’s 3 ‘7’s and 2 ‘3’s
array_freq[array_numbers[i]]=0;
array_freq[x-array_numbers[i]]=0; // doing this we don’t get them repeated
}
} // end loop
if ((x%2)=0)
{
freq = array_freq[x/2];
n_numbers=0;
for(i=1; i<freq;i++)
{ //O([size-k subset])
n_numbers+= (freq-i);
}
printf(“-{%d,%d} %d times\n”,x/2,x/2,n_numbers);
}
return;
}else{
return; // Incoming NULL array
printf(“nothing to do here, bad pointer\n”);
}
}
欢迎评论家。
答案 14 :(得分:0)
在java中,这取决于数组中的最大数量。 它返回一个具有两个元素索引的int []。 它是O(N)。
public static int[] twoSum(final int[] nums, int target) {
int[] r = new int[2];
r[0] = -1;
r[1] = -1;
int[] vIndex = new int[0Xffff];
for (int i = 0; i < nums.length; i++) {
int delta = 0Xfff;
int gapIndex = target - nums[i] + delta;
if (vIndex[gapIndex] != 0) {
r[0] = vIndex[gapIndex];
r[1] = i + 1;
return r;
} else {
vIndex[nums[i] + delta] = i + 1;
}
}
return r;
}
答案 15 :(得分:0)
首先你应该找到reverse array =&gt;总和减去实际数组 然后检查实际数组中是否存在来自这些新数组的任何元素。
const arr = [0, 1, 2, 6];
const sum = 8;
let isPairExist = arr
.map(item => sum - item) // [8, 7, 6, 2];
.find((item, index) => {
arr.splice(0, 1); // an element should pair with another element
return arr.find(x => x === item);
})
? true : false;
console.log(isPairExist);
答案 16 :(得分:0)
使用O(n×n)性能的Naïve双循环打印输出可以使用O(n)内存用于哈希表,如下所示,可以提高线性O(n)性能:
void TwoIntegersSum(int[] given, int sum)
{
Hashtable ht = new Hashtable();
for (int i = 0; i < given.Length; i++)
if (ht.Contains(sum - given[i]))
Console.WriteLine("{0} + {1}", given[i], sum - given[i]);
else
ht.Add(given[i], sum - given[i]);
Console.Read();
}
答案 17 :(得分:0)
def pair_sum(arr,k):
counter = 0
lookup = set()
for num in arr:
if k-num in lookup:
counter+=1
else:
lookup.add(num)
return counter
pass
pair_sum([1,3,2,2],4)
python中的解决方案