【问题标题】:Find duplicates between arrays查找数组之间的重复项
【发布时间】:2010-02-11 21:14:25
【问题描述】:

假设给定两个恒定长度为 3 的整数数组,并且您始终确定给定两个数组的两个元素将具有相同的值。

因此假设数组 A 具有三个值:a、b、c。 并且数组 B 具有三个值:d、e、f。

我们确信其中两个值将相同。我们被要求将这四个不同的值放入一个大小为 4 的数组中,这样输出数组 C 在索引 1 和 2 中应该具有来自数组 A 和 B 的相同值。在索引 0 和 3 中它应该具有不同的值数组 A 和 B。我已经实现了它,但对这个解决方案真的不满意......有人有更好的解决方案吗?除了将我的计数器放入数组中的那个... :)

int[] a = { 1, 201, 354 };
int[] b = { 404, 201, 354 };

int[] c = new int[4];

for (int i = 0; i < c.Length; i++)
{
    Console.WriteLine(c[i]);
}

【问题讨论】:

  • 一点也不:),我正在寻找不规则网络的三角剖分,如果每个点周围有两个以上的三角形,我想测量角度...
  • 我懒得写一个带连接的 LINQ,计算重复。
  • 感谢伊恩的诚实 :))))
  • @kl:您对三角测量的评论增加了您的问题的可信度 - 为什么不编辑它以提供上下文。

标签: c# algorithm


【解决方案1】:

对不起,我重新阅读更仔细,我认为这就是你想要的。请指教。 :)

int[] same = a.Intersect(b).ToArray(); ;
int[] diff = a.Union(b).Except(same).ToArray();
int[] c = new int[] { diff[0], same[0], same[1], diff[1] };

【讨论】:

  • 是的,就是这样!
  • 我可能误解了你的问题。您是否希望数组具有共享值? {201、354、201、354}?您的示例的预期输出是什么?
  • 加油,Sapph,将 50 行代码减少到 2 行……你可以做到!
  • 我重写了我的解决方案以更紧密地匹配问题...抱歉误会,如果您需要,请告诉我。
  • +1 为马拉松用户图片(......和迄今为止最干净的解决方案)!
【解决方案2】:

您正在寻找的只是两个数组的集合(集合最多包含每个元素一次)。 c++中的解决方案:

#include <set>

int main () {
    int a[] = { 1,2,3 };
    int b[] = { 4,2,3 };

    std::set<int> s(a, a+3);
    s.insert(b, b+3);
}

【讨论】:

    【解决方案3】:

    如果您可以使用 LINQ,则以下代码就足够了:

    int[] c = a.Union(b).ToArray();
    

    联合检查重复项,因此无需进一步检查:

    返回:一个 System.Collections.Generic.IEnumerable 包含两者的元素 输入序列,不包括重复。

    【讨论】:

      【解决方案4】:

      替换

      // IRQ. 20100211. Deleted unncessary code
      

      var c = a.Concat(b).Distinct().ToArray();
      

      更新:

      新的:

      var same = a.Intersect(b);
      var c = a.Except(same).Concat(same).Concat(b.Except(same)).ToArray();
      

      或者这些

      var c = a.Except(b).Concat(a.Intersect(b)).Concat(b.Except(a));
      var c = a.Except(b).Concat(a).Concat(b).Distinct();
      

      【讨论】:

        【解决方案5】:

        这是一个很酷的 C(++) 解决方案

        int a[3], b[3]; /* the two arrays */
        int c[4]; /* target */
        int s=0, t=0, k;
        int i;
        for (i=0;i<3;i++) { k = a[i]-b[i]; s += k; t += k*(a[i]+b[i]); }
        
        /* At this point s is the difference of the two distinct elements
           and t is the difference of their squares, i.e. s = x - y and t = x^2 - y^2
           because (x-y)(x+y) = x^2-yx+yx-y^2 = x^2-y^2
           Because the two elements are distinct, s != 0 and we can easily divide t
           by s to get (x + y), from which then we have
           s == x - y
           t == x + y
           i.e. x = (s+t)/2 and y=(t-s)/2 */
        
        t /= s;
        int x = (s + t) / 2;
        int y = (t - s) / 2;
        
        /* Now x, y are the distinct elements, x from array a and y from array b */
        /* Fill in the results */
        c[0] = x;
        c[3] = y;
        /* If a[0] is non-shared, then a[1] must be the first shared element; otherwise a[0] */
        c[1] = (a[0] == x ? a[1] : a[0]);
        /* If a[2] is non-shared, then a[1] must be the last shared element; otherwise a[2] */
        c[2] = (a[2] == x ? a[1] : a[2]);
        

        示例:a = {1, 3, 5}, b = {3, 5, 2}

        s = (1-3)+(3-5)+(5-2) = -2-2+3 = -1
        t = (1-3)*(1+3)+(3-5)*(3+5)+(5-2)*(5+2) = -8-16+21 = -3
        t / s = 3
        x = (-1 + 3) / 2 = 1
        y = (3 - (-1)) / 2 = 2
        c[0] = 1
        c[3] = 2
        c[1] = 3
        c[2] = 5
        

        所以 c 根据需要获取值 {1,3,5,2}!

        为了好玩,这里有一个更紧凑的版本:

        /* Declarations */
        int a[3], b[3], c[4];
        int s = 0, t = 0, k, i;
        
        /* Actual algorithm */
        for (i = 0; i < 3; i++) { s += (k = a[i]-b[i]); t += k * (a[i]+b[i]); }
        t /= s;
        c[0] = (s + t) >> 1;
        c[3] = (t - s) >> 1;
        c[1] = (a[0] == x ? a[1] : a[0]);
        c[2] = (a[2] == x ? a[1] : a[2]);
        

        请注意,如果问题是泛化的,以便共享 n-1 个元素并且两个数组中都有一个唯一元素,那么这是一个 O(n) 算法,而通常基于集合交集和/或联合的算法是 O(n log n) :)

        【讨论】:

        • 我是这样找到它的:我首先想到的是对所有 6 个元素进行异或运算,所以你最终会得到 x^y,但是你不能直接恢复 x^y,所以然后我想到了减法,a[0]+a[1]+a[2]-b[0]-b[1]-b[2],它给你 x-y ......但这有同样的问题.所以你需要另一个线性独立的项来解决 x 和 y。然后当我意识到 x^2-y^2 和 x-y 一起很容易地产生 x 和 y 时,这些部分就被拼凑在一起,如上所示。
        【解决方案6】:

        代替counter1、counter2、counter3:

        counter[3];
        

        很多事情从那里变得更容易。您可以在循环中引用所有内容。

        【讨论】:

          【解决方案7】:

          我很确定我不明白这个问题。

          你说:

          我们确信其中两个值将相同。我们被要求输入这四个不同的值

          您指的是哪四个不同的值?这两个是一样的吗?因为这就是“这些”这个词所指的。

          您的意思是:取 4 个唯一值并将它们放入一个数组中?

          这样:

          1、2、3
          2、3、4

          变成:

          1、2、3、4?

          这很简单:

          int[] c = a.Concat(b).Distinct().ToArray();
          

          这使用 .NET 3.5 的 Linq 扩展方法。如果您不在 .NET 3.5 上,则可以这样做:

          Dictionary<int, int> c1 = new Dictionary<int, int>();
          foreach (var x in a)
              c1[x] = 1;
          foreach (var x in b)
              c1[x] = 1;
          int[] c = new List<int>(c1.Keys).ToArray();
          

          现在,如果您需要这样的顺序:

          1. 第一个只出现一次的值
          2. 出现两次的第一个值
          3. 出现两次的第二个值
          4. 仅出现一次的第二个值

          那恐怕我没有适合你的单线,得再考虑一下。

          请问上下文是什么?为什么有这个要求?

          【讨论】:

          • 第二个版本依赖于未指定的行为(字典中键的顺序)
          • Linq和non-Linq这两个版本都按问题输出顺序错误。
          • OrderedDictionary 应该可以工作。请求的订单是广告订单。
          【解决方案8】:

          这是我作为初稿提出的,但我认为它可能需要一些改进。它也不满足在位置 1 和 2 处具有重复项以及在数组中的 0 和 3 处具有唯一编号的要求。我想我还是会发布它,这样您就可以了解它的外观:

            int[] a = { 1, 201, 354 };
            int[] b = { 404, 201, 354 };
          
            int[] c = new int[ 4 ];
          
            // Start by just copying over one of the arrays completely.
            a.CopyTo( c, 0 );
          
            // Loop through b and compare each number against each
            // each number in a.
            foreach( int i in b )
            {
              // Assume that you're not dealing with a duplicate
              bool found = true;
              foreach( int j in a )
              {
                // If you find a duplicate, set found to false
                if( i == j )
                {
                  found = false;
                }           
              }
              // If you haven't found a duplicate this is the
              // number you want - add it to the array.
              if (found == true)
              {
                c[3] = i;
              }
            }
          

          【讨论】:

            【解决方案9】:
            bool isUsed[6]={true, true, true, true, true, true};
            
            int values[6];
            
            int valuesCount = 0;
            
            int i,j;
            
            for( i = 0 ; i < 3 ; i++)
            {
               bool goodValue = true;
               for ( j = 0; j < valuesCount; j++)
               {
                   if(values[j] == a[i])
                   {
                       isUsed[j] = false;
                       goodValue = false;
                       break;
                   }
               }
               if(goodValue)
               {
                   values[valuesCount++]=a[i];
               }
            }
            
            //same for b[i]
            
            for( i = 0 ; i < valuesCount; i++)
            { 
               if( isUsed[i] ) printf("%i ", values[i]);
            }
            

            【讨论】:

            • 感谢修复!我尝试了很多次格式化它,但未能成功 :D 也无法删除响应......
            【解决方案10】:

            这部分

                if (a[0] == b[0]) { counter0++; }
                if (a[0] == b[1]) { counter0++; }
                if (a[0] == b[2]) { counter0++; }
            
                if (a[1] == b[0]) { counter1++; }
                if (a[1] == b[1]) { counter1++; }
                if (a[1] == b[2]) { counter1++; }
            
                if (a[2] == b[0]) { counter2++; }
                if (a[2] == b[1]) { counter2++; }
                if (a[2] == b[2]) { counter2++; }
            

            可以改写成

            for (i=0; i<3; i++)
            {
                for (j=0; j<3; j++)
                {
                     switch(i)
                     {
                          case 0:
                          if (a[i] == b[j]) { counter0++; }
                          break;
                          case 1:
                          if (a[i] == b[j]) { counter1++; }
                          break;
                          case 2:
                          if (a[i] == b[j]) { counter2++; }
                          break;
                      }
                 }
            }
            

            与其他计数器的其他部分应该类似地编写。然后你可以将它重构为一个单独的方法,只需将数组和计数器传递给它。

            另一种选择可能是 LINQ,但我不确定如何编写这样的东西。

            (没试过编译这个,思路清楚吗?)

            更新: 如果您可以将计数器放在一个数组中,这可能会起作用:

            for (i=0; i<3; i++)
            {
                for (j=0; j<3; j++)
                {
                    if (a[i] == b[j]) { counters[i]++; }
            
                 }
            }
            

            【讨论】:

            • 只是一个小提示:我建议使用 foreach 循环而不是 for 循环。我发现它们更易于阅读和处理。
            • @Anne: 但算法需要 i.... foreach(var x in a) { i++; 的值} 可能更糟。
            • @Anne Schuessler:可能,而且我敢肯定还有许多其他的小改进可以做。例如,计数器可以在计数器数组中,因此可以放弃开关/案例并替换为counters[i]++;
            • @Jimmy:是的,我认为你是对的。我正在玩弄我的“解决方案”,并意识到我可能需要将 foreach 改回 for 才能访问数组中每个项目的位置。
            【解决方案11】:

            我试图给出一个简短的答案。但是它假定输入是正确的。

            int c1, c2, i;
            c1 = a[0] == b[0] ? 0
                              : (a[0] == b[1] ? 1 : 2); // index of a[0] in array 'b'
            c2 = a[1] == b[0] ? 0
                              : (a[1] == b[1] ? 1 : 2); // index of a[1] in array 'b'
            
            for(i=0; i<2; i++)
                Console.WriteLine(a[i]);
            Console.WriteLine(b[3-c1-c2]); // looks quite hacky but it is actually element of 'b' not in array 'a'
            

            【讨论】:

              【解决方案12】:

              这是一些简单的代码,但它假定 a 和 b 中的值始终为正数。

              int[] a = { 1, 201, 354 };
              int[] b = { 404, 201, 354 };
              
              int[] c = { -1, -1, -1, -1};
              
              for(int i = 0; i < 3; i++){
                  int notfound = 1;
                  for(int j = 0; j < 3; j++){
                      if(b[j] == -1) continue;
              
                      if(a[i] == b[j]){
                          b[j] = -1;
                          if(c[1] == -1)
                              c[1] = a[i];
                          else
                              c[2] = a[i];
                          notfound = 0;
                          break;
                      }
                  }
                  if(notfound)
                      c[0] = a[i];
              }
              int k = 0;
              while(b[k++] == -1);
              c[3] = b[k];
              

              我还没有测试过,但希望你能明白。这使用了非常少的额外空间(仅用于未找到的空间,可以设为布尔值和索引变量)并且应该非常快。

              【讨论】:

                【解决方案13】:
                int[] a = { 204, 534, 1 };
                int[] b = { 204, 534, 401 };
                int[] c = new int[4];
                
                int x = 3, y = 3, k = 1;
                for(int i=0; i<3; i++)
                    for(int j=0; j<3; j++)
                        if (a[i] == b[j]) {
                            c[k++] = a[i];
                            x -= i;
                            y -= j;
                            break;
                        }
                c[0] = a[x];
                c[3] = b[y];
                

                【讨论】:

                  【解决方案14】:

                  Sapph 提供了一个尽可能简洁的答案,但如果性能非常重要,这里是一个答案。 .NET 数组边界检查可能会增加一些开销,但在 C 中,这会编译为 64 条指令而没有分支。

                  int[] a = { 204, 534, 1 };
                  int[] b = { 204, 534, 401 };
                  int[] c = new int[4];
                  
                  // pick the value from a that is not in b for c[0]
                  // a[0] not in b is implied by a[1] in b and a[2] in b
                  int a1_not_in_b = Convert.ToInt32(a[1] != b[0] & a[1] != b[1] & a[1] != b[2]);
                  int a2_not_in_b = Convert.ToInt32(a[2] != b[0] & a[2] != b[1] & a[2] != b[2]);
                  
                  // bitfield of 2 bit values equivalent to the array {0,1,2,0,1}
                  int idxs = 0 | 1 << 2 | 2 << 4 | 0 << 6 | 1 << 8;
                  // if a[1] not in b start at 1, if a[2] not in b start at 2, else start at 0
                  idxs >>= 2*a1_not_in_b | 4*a2_not_in_b;
                  c[0] = a[(idxs >> 0) & 3];
                  c[1] = a[(idxs >> 2) & 3];
                  c[2] = a[(idxs >> 4) & 3];
                  
                  // pick the value from b that is not in a
                  // b[0] not in a is implied by b[1] in a and b[2] in a
                  int b1_not_in_a = Convert.ToInt32(a[0] != b[1] & a[1] != b[1] & a[2] != b[1]);
                  int b2_not_in_a = Convert.ToInt32(a[0] != b[2] & a[1] != b[2] & a[2] != b[2]);
                  c[3] = b[b1_not_in_a | 2*b2_not_in_a];
                  

                  【讨论】:

                    【解决方案15】:

                    更快?

                    using System;
                    using System.Linq;
                    using sw = System.Diagnostics.Stopwatch;
                    class Program
                    {
                        static void Main()
                        {
                            int[] a = new int[] { 1, 2, 3 },  // try: a={1,2,2} b={2,2,3}
                                  b = new int[] { 4, 2, 3 }, c = new int[4];
                            sw sw = sw.StartNew();
                            for (int i = 5000000; i > 0; i--) { dssd1(a, b, c); dssd1(b, a, c); }
                            Console.Write(sw.ElapsedMilliseconds);
                            Console.Read();
                        }
                    
                        static void dssd0(int[] a, int[] b, int[] c)               // 6710 ms.
                        {
                            int[] s = a.Intersect(b).ToArray();        // same
                            int[] d = a.Union(b).Except(s).ToArray();  // diff
                            c[0] = d[0]; c[1] = s[0]; c[2] = s[1]; c[3] = d[1];
                        }
                    
                        static void dssd1(int[] a, int[] b, int[] c)               //   61 ms.
                        {
                            if (a[0] != b[0] && a[0] != b[1] && a[0] != b[2])
                            { c[0] = a[0]; c[1] = a[1]; c[2] = a[2]; goto L0; }
                            if (a[1] != b[0] && a[1] != b[1] && a[1] != b[2])
                            { c[0] = a[1]; c[1] = a[0]; c[2] = a[2]; goto L0; }
                            c[0] = a[2]; c[1] = a[0]; c[2] = a[1];
                        L0: if (b[0] != c[1] && b[0] != c[2]) { c[3] = b[0]; return; }
                            if (b[1] != c[1] && b[1] != c[2]) { c[3] = b[1]; return; }
                            c[3] = b[2];
                        }
                    }
                    

                    最快?

                        L0: c[3] = b[0] != c[1] && b[0] != c[2] ? b[0] :           //   49 ms.
                            b[1] != c[1] && b[1] != c[2] ? b[1] : b[2];
                    

                    【讨论】:

                      【解决方案16】:

                      这个怎么样?

                      private static int[] FindDuplicates(int[] arrA,int[] arrB)
                          {
                              var aList=new List<int>();
                              Array.Sort(arrA);
                              Array.Sort(arrB);
                      
                      
                              for(int i=0;i<arrA.Length;i++)
                              {
                                 if(arrB.Contains(arrA[i]))
                                 {           
                                 aList.Add(arrA[i]);
                                 }
                      
                              }
                              return aList.ToArray();
                      
                          }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2012-01-12
                        • 2015-12-13
                        • 1970-01-01
                        • 2013-07-31
                        • 1970-01-01
                        • 2016-06-23
                        • 1970-01-01
                        • 2011-04-26
                        相关资源
                        最近更新 更多