我的一位朋友在接受采访时被问到这个问题 -
您如何找到不同的元素?您可以采取哪些不同的方法?
一种简单但冗长的方法是 - 对两个数组进行排序,继续比较每个元素,在进行错误比较时,您将获得结果。
那么有什么不同的方法呢?在面试中指定逻辑。不期望特定语言的特定代码。伪代码就足够了。
(每个答案请提交一种方法)
我提出这个问题的目的是,当数组大小很小时就可以了。但是当数组大小增加时,你必须考虑一种非常有效的方法。在这种情况下,它绝不可能使用比较。
答案 0 :(得分:17)
如果您需要扩展,那么我将使用世界上众多Set实现之一。例如,Java的HashSet。
抛出Set中的所有第一个数组。然后,对于第二个数组的每个成员,如果它包含在Set中,则将其删除;否则将其标记为Unique#2。在此过程之后,集合的最后一个剩余成员是唯一#1。
我可能会这样做,即使是在采访中,甚至是简单的十元素阵列。生活太短暂,无法花时间找到一个聪明的方法来缩放墙壁,当它有一个非常好的门时。
答案 1 :(得分:9)
这是一种数学方法,受到Kevin的回答及其评论的启发。
让我们分别调用数组A
和B
,让它们的唯一元素分别为a
和b
。首先,取两个数组的总和,然后减去另一个;因为其他一切都取消了,sum(A) - sum(B) = a - b = s
。然后,将两个数组的元素相乘,然后将它们相互分开。事情再次取消,mult(A) / mult(B) = a / b = r
。现在,我们从a = rb
获得rb - b = s
或b = s / (r - 1)
,然后a = rs / (r - 1)
。
我称之为数学,因为在实际程序中将事物放在一起可能不合理。关键是要有两个不同的操作,它们既可以单独允许取消行为,又可以分配到另一个上面。从rb - b = s
转到b = s / (r - 1)
时会使用后一种属性,例如,添加和XOR,这是我的第一次尝试。
答案 2 :(得分:7)
这可以通过两个序列的和和平方和快速解决。计算这些总和肯定会比建议的哈希更快,并且不涉及序列项之间的任何比较。
以下是如何操作:如果两组是{ a i }和{ b i 然后调用A和B它们的和,A2和B2是平方和,即A2 = Sum({ a i 2 }),为方便起见, D = AB , D2 = A2-B2 。因此, D = ab 和 D2 = a 2 -b 2 ,其中 a 和 b 是两个不同的元素,我们可以看到
a =(D 2 + D2)/(2 * D)
b = a - D
这是因为,从代数中, a 2 -b 2 =(a + b)(ab)或 D2 =(a + b)D ,所以 a + b = D2 / D ,因为我们也知道 ab ,我们可以找到 a 和 b 。
Python中的一个例子可能更有说服力
a, b = 5, 22 # the initial unmatched terms
x, y = range(15), range(15)
y[a] = b
print "x =", x # x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
print "y =", y # y = [0, 1, 2, 3, 4, 22, 6, 7, 8, 9, 10, 11, 12, 13, 14]
D = sum(x) - sum(y)
D2 = sum([i**2 for i in x]) - sum([i**2 for i in y]) #element-wise squaring
a = (D2+D*D)/(2*D)
b = a - D
print "a=%i, b=%i" % (a, b)
#prints a=5, b=22 which is correct
(当然,这有点类似于jk的答案,除了它不需要所有术语的乘法和可能产生的巨大数字,但是由于jk的数学方法的想法。)
答案 3 :(得分:3)
从技术上讲,您可以在恒定时间内完成,因为数组(及其中的值)是有限的。对于广义问题,我们必须弄清楚一些比较棘手的问题。
这是一个线性时间解决方案。
首先,我们应该基于一个数组构建一个哈希。哈希表中的值查找需要O(1 + k / n)比较[1]
,其中k是哈希表的长度。因此,对第一个数组(包含n个元素)的迭代和每个值的查找需要O(n + k)。
然后我们迭代另一个,查找哈希中的每个元素。找不到元素时 - 这是另一个元素中唯一的元素。 (再次O(n + k))。然后我们迭代哈希以寻找第二个唯一元素(O(k))。
总时间为O(n + k)。因为让k大于n是没有意义的,所以它是线性解。
Perl代码:
sub unique
{
my ($arr, $brr) = @_;
my %hash = map{$_ => 1} @$arr;
%hash{$_}-- for @$brr;
return grep {$_} keys %hash;
}
答案 4 :(得分:2)
在LINQ中:
var unique1 = (from a in arrayA where !arrayB.Contains(a) select a).First();
var unique2 = (from b in arrayB where !arrayA.Contains(b) select b).First();
return new Pair(unique1, unique2);
...
public sealed class Pair<T0, T1>
{
public T0 Item1 {get;set;}
public T1 Item2 {get;set;}
public Pair(T0 item1, T1 item2)
{
Item1 = item1;
Item2 = item2;
}
//plus GetHashCode, equality etc.
}
答案 5 :(得分:1)
这是另一种可能性。与我的previous answer不同,它不会修改传入的数组,并且应该具有较低的大O绑定(O(n)而不是O(n ^ 2) - 假设时间哈希表查找为常数),但是显着增加记忆力。
function findUnique(a:Array, b:Array):Array {
var aHash:Hashtable = buildHash(a);
var bHash:Hashtable = buildHash(b);
var uniqueFromA:int;
var uniqueFromB:int;
for each(value:int in a) {
if(!bHash.contains(value)) {
uniqueFromA = value;
break;
} else {
/* Not necessary, but will speed up the 2nd for-loop by removing
* values we know are duplicates. */
bHash.remove(value);
}
}
for each(value:int in b) {
if(!aHash.contains(value)) {
uniqueFromB = value;
break;
}
}
return [uniqueFromA, uniqueFromB];
}
function buildHash(a:Array):Hashtable {
var h:Hashtable = new Hashtable();
for each(value:int in a) {
h[value] = true;
}
return h;
}
答案 6 :(得分:1)
根据约束条件,您可以在线性时间内快速解决此问题。如果你持有一个int [10],那么你可以假设索引1处的元素对应于数字1;元素本身包含两个数组的计数。以下伪代码将很快解决问题:
let buckets = new int[10] // init all buckets to zero
for num in arr1 do buckets[num]++ // add numbers from the first array
for num in arr2 do buckets[num]++ // add from the second array
for i in 1 to 9 do // find odd man out
if buckets[i] <= 1 then return i
这本质上是一个有界哈希表。如果我们给定的元素列表被限制在1到9之间,则仅可用。
从技术上讲,您甚至不需要保持每个元素的运行计数。原则上,您可以简单地遍历arr1,然后遍历arr2,直到您遇到未从第一个数组散列的元素。
答案 7 :(得分:1)
几乎所有先前答案背后的逻辑始终是相同的:使用数学中的集合运算来解决问题。
数学集合只能包含每个元素一次。因此,以下列表不能是数学意义上的集合,因为它包含一个数字(3)两次:
{ 1, 2, 3, 4, 3, 5 }
由于集合操作(特别是检查元素是否已存在于集合中)是常见操作,因此大多数语言都具有有效实现这些集合操作的数据结构。因此,我们可以在我们的解决方案中简单地回到这一点:
// Construct set from first list:
Set uniques = Set.from(list1);
// Iterate over second list, check each item’s existence in set.
for each (item in list2)
if (not uniques.Contains(item))
return item;
不同的集合实现会产生不同的性能,但这种性能总是优于天真的解决方案(对于大型列表)。特别是,存在两种实现:
在每种情况下,使用情况保持不变,上面的伪代码为您的问题提供了教科书解决方案。 Java实现可能如下所示:
// Construct set from first list:
Set<Integer> uniques = new HashSet<Integer>(list1);
// Iterate over second list, check each item’s existence in set.
for (int item : list2)
if (! uniques.Contains(item))
return item;
注意这看起来几乎与伪代码完全一样。 C#,C ++或其他语言的解决方案差别不大。
编辑糟糕,我刚刚注意到请求的返回值是不匹配元素的对。但是,此要求不会改变推理,几乎不会更改伪代码(使用互换列表执行相同操作)。
答案 8 :(得分:0)
int[] a = {1, 2, 3, 5};
int[] b = {1, 2, 3, 5, 6};
HashSet set = new HashSet();
for (int o : b) {
set.add(o);
}
for (int p : a) {
if (set.contains(p)) {
set.remove(p);
}
}
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Log.d("TAG", " " + iterator.next());
}
答案 9 :(得分:0)
根据您使用的语言,它可能具有内置方式来执行数组差异。 PHP执行http://ca3.php.net/array_diff
答案 10 :(得分:0)
鉴于两个阵列说A1的大小&#39; n&#39;和A2的大小&#39; n-1&#39;,两个阵列都相同 元素,除了我们必须找到的元素。
注意:A1中的元素可以重复。
示例:
A1:{2,5,5,3}
A2:{2,5,3}
输出:5
A1:{1,2,3,3,3-}
A2:{2,3,1,3}
输出:3
public static void main(String args[])
{
int[] a ={1,2,3,3,3};
int[] b ={2,3,1,3};
int flag=1;
int num=0;
List<Integer> lst = new ArrayList<>(b.length);
for(int i : b)
lst.add(Integer.valueOf(i));
for(int i=0;i<a.length;i++)
{
flag=1;
for(int j=0;j<lst.size();j++)
{
if(a[i] == lst.get(j)){
lst.remove(j);
flag=0;
break;
}
}
if(flag == 1)
num=a[i];
}
System.out.println(num);
}
答案 11 :(得分:0)
两次通过数组就足够了。
第一遍:将较短列表中的每个项目添加到一个hashmap(python中的dict)。 第二遍:对于较长列表中的每个项目,检查hashmap中是否存在密钥(O(1)搜索时间)。如果不是,那个键就是唯一的条目。
总时间复杂度:O(2n)= O(n)
答案 12 :(得分:0)
这是一个不需要散列或设置,O(1)空间和O(n)时间的解决方案。它不是最佳解决方案,但它是一个很好的解决方案。您所做的就是对list1中的值求和,并将值list2求和,找出差异。
var findExtra= function (l1, l2) {
var l1_sum = l1.reduce(function(prev, curr) {
return prev + curr;
});
var l2_sum = l2.reduce(function(prev, curr) {
return prev + curr;
});
console.log(l1_sum, l2_sum);
if (l1.length < l2.length) return l2_sum - l1_sum;
return l1_sum - l2_sum;
}
console.log(findExtra([1,2,3,4,-1], [1,2,3,4]));
答案 13 :(得分:0)
这可以使用Xor。
完成首先Xor来自两个数组的所有元素。设x和y是每个数组的额外元素。 剩下的是x ^ y。
现在在xor中,如果设置了一个位,则表示它设置在两个数中的一个而不是另一个中。
我们可以用它来找到丢失的个人数字。所以找一个在^ b中设置的位。获得最高位很容易。它可以通过
完成N'安培; 〜第(n-1)
(1110)&amp; 〜(1101)= 0010
为了获得每个单独的数字,我们将两个数组的数字分成两部分,数字设置了检查位,哪些没有。我们在每个数组上进行XOR,这样我们就得到了值a和b。这将取消所有重复元素,并将x和y分开。
这可能会令人困惑。现在取x = 3,y = 2
X = 110
Y = 010
的x ^ Y = 100
因此,当我们得到该位时,我们得到的数字是bitset = 100.设置第三位。假设数组元素为5,1(两次重复两次)
5 = 101
6 = 001
现在,5有3位,所以我们用x
对它进行xor我们得到x ^ 5 ^ 5 = x
类似地,6没有设置第3位,所以xor和y。
我们得到y ^ 1 ^ 1 = y
代码
for(i=0;i<n;i++)
xor = xor^a[i]^b[i];
set_bit = xor & ~(xor-1) ;
for(i = 0; i < n; i++)
{
if(a[i] & set_bit_no)
x = x ^ a[i]; /*XOR of first set */
else
y = y ^ a[i]; /*XOR of second set*/
if(b[i] & set_bit_no)
x = x ^ b[i]; /*XOR of first set */
else
y = y ^ b[i]; /*XOR of second set*/
}
这与此处发布的方法类似 http://www.geeksforgeeks.org/find-two-non-repeating-elements-in-an-array-of-repeating-elements/
答案 14 :(得分:0)
如何获得每个数组的总和并从另一个数组中减去一个?不同的是额外的元素。
例如:A = 1,2,3,4,5 B = 1,2,3,4
总和(A)= 15,总和(B)= 10; 15 - 10 = 5这是额外的元素。
答案 15 :(得分:0)
分别制作阵列a,b的集合A,B。 A \ B在A中给出了另外的整数.B \ A给出了B中的附加整数。
如果这些操作中的任何一个返回空集,则附加整数在数组中两次。在构造集合时会发现这一点:向集合中添加重复的整数不会增加集合的大小。
答案 16 :(得分:0)
将第一个数组的每个元素放入哈希值。对于第二个数组中的每个项目,如果它已经在哈希中,则删除它,否则添加它。最后,你有一个带有两个键的哈希,这是你的独特元素。
Ruby中的简单版本
def unique(a,b)
h={}
a.each do |ax|
h[ax]=true
end
b.each do |bx|
if h[bx]
h.delete(bx)
else
h[bx]=true
end
end
h.keys
end
拥有更多的Ruby个性,但仍然在宇宙中,我们不能简单地(a | b) - (a & b)
或a.to_set ^ b.to_set
:
def unique(a,b)
h = {}
a.each do |ax|
h[ax] = true
end
b.each do |bx|
h.delete(bx) or h[bx] = true
end
h.keys
end
答案 17 :(得分:0)
这是解决方案的一些简单伪代码。我假设可以修改数组,并且数组有一个remove(value)方法(或者你可以写一个简单的方法)。它需要2个数组并返回一个包含2个值的数组,第一个是第一个数组的唯一值,第二个是第二个数组的唯一值。
function findUnique(a:Array, b:Array):Array {
var uniqueFromA:int;
var uniqueFromB:int;
for each(value:int in a) {
var len:int = b.length;
b.remove(value);
/* b's length didn't change, so nothing was removed, so the value doesn't
* exist in it. */
if(b.length == len) {
uniqueFromA = value;
}
}
/* Only the unique value in b still exists in b */
uniqueFromB = b[0];
return [uniqueFromA, uniqueFromB];
}
答案 18 :(得分:0)
如果不进行比较,就无法解决这个问题。一些现代语言确实设置了差异和其他聚合运算符,但它们通过内部进行比较来工作。如果总数组大小是奇数(这里不是这种情况)那么它确实可以将 xor 元素组合在一起。我认为ALU的 xor 操作是否应被视为比较的问题值得商榷。
如果没有指定语言,则无法引用库,因此唯一可行的解决方案是基于比较的伪代码说明。
所以你走了:
a <- first element
a_count = 1
b_count = 0
loop over remaining elements
if element != a
b <- element
++b_count
else
++a_count
if found_result
break loop
end loop
found_result
if a_count > 1 and b_count > 0
the unique one is b
return true
if b_count > 1 and a_count > 0 # a_acount actually always > 0
the unique one is a
return true
return false