区分多个元素和两个数组?

时间:2009-10-19 18:49:23

标签: algorithm arrays data-structures logic

我的一位朋友在接受采访时被问到这个问题 -

  • 您已经提供了两个大小为10的整数数组。
  • 两者都包含9个相等的元素(比如说1到9)
  • 只有一个元素不同。

您如何找到不同的元素?您可以采取哪些不同的方法?

  

一种简单但冗长的方法是 - 对两个数组进行排序,继续比较每个元素,在进行错误比较时,您将获得结果。

那么有什么不同的方法呢?在面试中指定逻辑。不期望特定语言的特定代码。伪代码就足够了。

(每个答案请提交一种方法)

  

我提出这个问题的目的是,当数组大小很小时就可以了。但是当数组大小增加时,你必须考虑一种非常有效的方法。在这种情况下,它绝不可能使用比较。

19 个答案:

答案 0 :(得分:17)

如果您需要扩展,那么我将使用世界上众多Set实现之一。例如,Java的HashSet。

抛出Set中的所有第一个数组。然后,对于第二个数组的每个成员,如果它包含在Set中,则将其删除;否则将其标记为Unique#2。在此过程之后,集合的最后一个剩余成员是唯一#1。

我可能会这样做,即使是在采访中,甚至是简单的十元素阵列。生活太短暂,无法花时间找到一个聪明的方法来缩放墙壁,当它有一个非常好的门时。

答案 1 :(得分:9)

这是一种数学方法,受到Kevin的回答及其评论的启发。

让我们分别调用数组AB,让它们的唯一元素分别为ab。首先,取两个数组的总和,然后减去另一个;因为其他一切都取消了,sum(A) - sum(B) = a - b = s。然后,将两个数组的元素相乘,然后将它们相互分开。事情再次取消,mult(A) / mult(B) = a / b = r。现在,我们从a = rb获得rb - b = sb = 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)

  • 您已经提供了两个大小为10的整数数组。
  • 两者都包含9个相等的元素(比如说1到9)
  • 只有一个元素不同。

根据约束条件,您可以在线性时间内快速解决此问题。如果你持有一个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;

不同的集合实现会产生不同的性能,但这种性能总是优于天真的解决方案(对于大型列表)。特别是,存在两种实现:

  • 作为(自我平衡)search tree。树的元素已经排序,因此通过使用二元搜索(或其变体)来查找特定元素是有效的。因此查找具有O(log n )的性能。从输入创建树集具有性能O( n ·log n )。这也是整体表现。
  • 可以实现
  • Hash tables以具有O(1)的平均情况查找性能(并且通过一些技巧,这也可以成为最坏情况的性能)。创建哈希表可以在O( n )中完成。因此,哈希表可以实现O( n )的整体运行时。
  • Bloom filters提供了一个很好的概率解决方案 - 即解决方案可能实际上是错误的,但我们可以控制这种情况的可能性。它特别有趣,因为它非常节省空间。
  • ......存在许多其他实现。

在每种情况下,使用情况保持不变,上面的伪代码为您的问题提供了教科书解决方案。 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求和,找出差异。

的Javascript

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