【问题标题】:The intersection of two sorted arrays两个排序数组的交集
【发布时间】:2011-01-24 21:40:09
【问题描述】:

给定两个排序数组:AB。数组A 的大小为La,数组B 的大小为Lb。如何找到AB的交集?

如果LaLb大很多,那么求交点算法会有什么不同吗?

【问题讨论】:

  • 我们不会为你做作业
  • 这是一道面试题。
  • 现在做作业,5年后它会成为你的同事,你会做它的工作,或者更糟糕的是调试它的工作。
  • 问题的文字看起来像家庭作业。还是他们现在进行书面采访?如果他们像这样进行书面采访,那么采访就成了家庭作业。
  • 家庭作业问题和面试问题几乎是一回事。无论哪种方式,都有权威人士希望您已经知道答案,您会根据您给出的回复来判断,并且假设您的回复是您自己的。

标签: c++ algorithm arrays sorting


【解决方案1】:

因为这看起来像一个硬件......我会给你算法:

Let arr1,arr2 be the two sorted arrays of length La and Lb.
Let i be index into the array arr1.
Let j be index into the array arr2.
Initialize i and j to 0.

while(i < La and j < Lb) do

    if(arr1[i] == arr2[j]) { // found a common element.
        print arr[i] // print it.
        increment i // move on.
        increment j
    }
    else if(arr1[i] > arr2[j])
        increment j // don't change i, move j.
    else
        increment i // don't change j, move i.
end while

【讨论】:

    【解决方案2】:

    我一直在为同样的问题苦苦挣扎,到目前为止,我遇到了:

    1. 线性匹配,在最坏的情况下会产生 O(m+n)。您基本上保留两个指针(A 和 B),每个指针都指向每个数组的开头。然后前进指向较小值的指针,直到到达数组之一的末尾,这表明没有交集。如果在任何时候你有 *A == *B - 你的交叉点来了。

    2. 二进制匹配。在最坏的情况下产生〜O(n * log(m))。您基本上选择较小的数组并在较小数组的所有元素的较大数组中执行二进制搜索。如果你想更花哨,你甚至可以使用二分查找失败的最后一个位置,并将其用作下一次二分查找的起点。这样你可以稍微改善最坏的情况,但对于某些场景,它可能会创造奇迹:)

    3. 双重二进制匹配。它是常规二进制匹配的一种变体。基本上,您从较小数组的中间获取元素并在较大数组中进行二进制搜索。如果你什么也没找到,那么你将较小的数组切成两半(是的,你可以扔掉你已经使用过的元素)并将更大的数组切成两半(使用二分搜索失败点)。然后对每一对重复。结果比 O(n*log(m)) 好,但我懒得计算它们是什么。

    这是两个最基本的。两者都有优点。线性更容易实现。二进制 1 可以说更快(尽管在很多情况下线性匹配会优于二进制)。

    如果有人知道比这更好的事情,我很乐意听到。匹配数组是我这些天做的事情。

    附:不要引用“线性匹配”和“二进制匹配”这两个术语,因为它们是我自己编造的,而且可能已经有了花哨的名称。

    【讨论】:

    • 疾驰搜索是去这里的正确方式,而不是你提到的任何事情。如果您有不匹配,请将指向较小对象的指针提前 1,然后 2,然后 4,依此类推,直到检测到不匹配。然后对你找到的包含解决方案的范围进行二分搜索。
    • 我实施了您的第三个建议(请参阅下面的答案),我认为它应该在概念上胜过其他任何事情。哦,这有实际的应用(使用单词索引的全文搜索到位置)。我的目标是对大量文本进行不到 1 毫秒的全文搜索。
    【解决方案3】:

    set_intersection 用作here。通常的实现类似于合并排序算法的合并部分。

    【讨论】:

    • 我觉得有趣的是没有人问过比较数组元素的成本。对于立即数数据类型(例如 int 或 float),比较很便宜并且 set_intersection 算法很好。但如果它是一个复杂的数据类型,比较两个元素的成本很高,我会改用散列技术。
    • @fearless_fool 你是对的。一个相关问题:stackoverflow.com/questions/896155/…
    【解决方案4】:
    void Intersect()
    {
        int la, lb;
        la = 5;
        lb = 100;
        int A[5];
        int i, j, k;
        i = j = k = 0;
        for (; i < 5; ++i)
            A[i] = i + 1;
        int B[100];
        for (; j < 100; ++j)
            B[j] = j + 2;
        int newSize = la < lb ? la : lb;
        int* C = new int[newSize];
        i = j = 0;
        for (; k < lb && i < la && j < lb; ++k)
        {
            if (A[i] < B[j])
                i++;
            else if (A[i] > B[j])
                j++;
            else
            {
                C[k] = A[i];
                i++;
                j++;
            }
        }
        for (k = 0; k < newSize; ++k)
            cout << C[k] << NEWLINE;
    }
    

    【讨论】:

      【解决方案5】:

      这是在 Java 中,但它可以满足您的需求。它实现了 Nazar 的回答中提到的变体 3(“双重”二进制搜索),应该是最快的解决方案。我很确定这胜过任何一种“疾驰”的方法。飞驰只是浪费时间从小步骤开始,而我们直接进行自上而下的二分搜索。

      在这里应用哪种复杂性等级并不是很明显。我们在较长的数组中进行二分搜索,但不会两次查看相同的元素,所以我们肯定在 O(m+n) 之内。

      此代码已使用随机数据进行了彻底测试。

      import java.util.Arrays;
      
      // main function. may return null when result is empty
      static int[] intersectSortedIntArrays(int[] a, int[] b) {
        return intersectSortedIntArrays(a, b, null);
      }
      
      // no (intermediate) waste version: reuse buffer
      static int[] intersectSortedIntArrays(int[] a, int[] b, IntBuffer buf) {
        int i = 0, j = 0, la = lIntArray(a), lb = lIntArray(b);
        
        // swap if a is longer than b
        if (la > lb) {
          int[] temp = a; a = b; b = temp;
          int temp2 = la; la = lb; lb = temp2;
        }
        
        // special case zero elements
        if (la == 0) return null;
        
        // special case one element
        if (la == 1)
          return Arrays.binarySearch(b, a[0]) >= 0 ? a : null;
          
        if (buf == null) buf = new IntBuffer(); else buf.reset();
        intersectSortedIntArrays_recurse(a, b, buf, 0, la, 0, lb);
        return buf.toArray();
      }
      
      static void intersectSortedIntArrays_recurse(int[] a, int[] b, IntBuffer buf, int aFrom, int aTo, int bFrom, int bTo) {
        if (aFrom >= aTo || bFrom >= bTo) return; // nothing to do
        
        // start in the middle of a, search this element in b
        int i = (aFrom+aTo)/2;
        int x = a[i];
        int j = Arrays.binarySearch(b, bFrom, bTo, x);
      
        if (j >= 0) {
          // element found
          intersectSortedIntArrays_recurse(a, b, buf, aFrom, i, bFrom, j);
          buf.add(x);
          intersectSortedIntArrays_recurse(a, b, buf, i+1, aTo, j+1, bTo);
        } else {
          j = -j-1;
          intersectSortedIntArrays_recurse(a, b, buf, aFrom, i, bFrom, j);
          intersectSortedIntArrays_recurse(a, b, buf, i+1, aTo, j, bTo);
        }
      }
      
      
      static int lIntArray(int[] a) {
        return a == null ? 0 : a.length;
      }
      
      
      static class IntBuffer {
        int[] data;
        int size;
        
        IntBuffer() {}
        IntBuffer(int size) { if (size != 0) data = new int[size]; }
        
        void add(int i) {
          if (size >= lIntArray(data))
            data = resizeIntArray(data, Math.max(1, lIntArray(data)*2));
          data[size++] = i;
        }
        
        int[] toArray() {
          return size == 0 ? null : resizeIntArray(data, size);
        }
        
        void reset() { size = 0; }
      }
      
      static int[] resizeIntArray(int[] a, int n) {
        if (n == lIntArray(a)) return a;
        int[] b = new int[n];
        arraycopy(a, 0, b, 0, Math.min(lIntArray(a), n));
        return b;
      }
      
      static void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) {
        if (n != 0)
          System.arraycopy(src, srcPos, dest, destPos, n);
      }
      

      【讨论】:

        【解决方案6】:

        让我们考虑两个排序数组:-

        int[] array1 = {1,2,3,4,5,6,7,8};
        int[] array2 = {2,4,8};
        
        int i=0, j=0;    //taken two pointers
        

        While 循环将一直运行,直到两个指针都达到各自的长度。

        while(i<array1.length || j<array2.length){
            if(array1[i] > array2[j])     //if first array element is bigger then increment 2nd pointer
               j++;
            else if(array1[i] < array2[j]) // same checking for second array element
              i++;
            else {                         //if both are equal then print them and increment both pointers
                System.out.print(a1[i]+ " ");
        
                if(i==a1.length-1 ||j==a2.length-1)   //one additional check for ArrayOutOfBoundsException
                    break;
                else{
                    i++;
                    j++;
                }
            }
        }        
        

        输出将是:-

        2 4 8
        

        【讨论】:

          【解决方案7】:

          使用 PYTHON 非常简单

          示例: A=[1,2,3,5,7,9,90] B=[2,4,10,90]

          三行代码

          for i in A:
               if(i in B):
                  print(i)
          

          输出:2、90

          【讨论】:

          • 这是一个非常草率的答案。请不要推荐对循环内的列表使用in 检查。此外,该问题被标记为 c++。你也没有解释你的代码。
          【解决方案8】:

          这是我测试过的一个答案,可以匹配两个都已排序但可能具有重复键和值作为条目的数组。 IE。两个列表都按“时间戳”键排序。然后 .equals 检测匹配。这个找到了 a 和 b 的交集,其中重复项的交集消耗了它们。 IE。 a 中与 b 中的元素匹配的每个元素都使用了该条目。对于特定项目的具体细节,我们深表歉意,但也许它很有用。

          在使用 HashSet(不处理重复项)、Guava MultiSet(非常好,但如果你仔细研究它有很多开销检查)之后,我终于做了下面的解决方案。

             /**
               * Finds the intersection of events in a that are in b. Assumes packets are
               * non-monotonic in timestamp ordering. 
               *
               *
               * @param a ArrayList<BasicEvent> of a
               * @param b likewise
               * @return ArrayList of intersection
               */
              private ArrayList<BasicEvent> countIntersect(ArrayList<BasicEvent> a, ArrayList<BasicEvent> b) {
                  ArrayList<BasicEvent> intersect = new ArrayList(a.size() > b.size() ? a.size() : b.size());
                  int count = 0;
                  if (a.isEmpty() || b.isEmpty()) {
                      return new ArrayList();
                  }
          
                  // TODO test case
          //        a = new ArrayList();
          //        b = new ArrayList();
          //        a.add(new BasicEvent(4, (short) 0, (short) 0)); // first arg is the timestamp
          //        a.add(new BasicEvent(4, (short) 0, (short) 0));
          //        a.add(new BasicEvent(4, (short) 1, (short) 0));
          //        a.add(new BasicEvent(4, (short) 2, (short) 0));
          ////        a.add(new BasicEvent(2, (short) 0, (short) 0));
          ////        a.add(new BasicEvent(10, (short) 0, (short) 0));
          //
          //        b.add(new BasicEvent(2, (short) 0, (short) 0));
          //        b.add(new BasicEvent(2, (short) 0, (short) 0));
          //        b.add(new BasicEvent(4, (short) 0, (short) 0));
          //        b.add(new BasicEvent(4, (short) 0, (short) 0));
          //        b.add(new BasicEvent(4, (short) 1, (short) 0));
          //        b.add(new BasicEvent(10, (short) 0, (short) 0));
                  int i = 0, j = 0;
                  int na = a.size(), nb = b.size();
                  while (i < na && j < nb) {
                      if (a.get(i).timestamp < b.get(j).timestamp) {
                          i++;
                      } else if (b.get(j).timestamp < a.get(i).timestamp) {
                          j++;
                      } else {
                          // If timestamps equal, it might be identical events or maybe not
                          // and there might be several events with identical timestamps.
                          // We MUST match all a with all b.
                          // We don't want to increment both pointers or we can miss matches.
                          // We do an inner double loop for exhaustive matching as long as the timestamps
                          // are identical. 
                          int i1 = i, j1 = j;
                          while (i1 < na && j1 < nb && a.get(i1).timestamp == b.get(j1).timestamp) {
                              boolean match = false;
                              while (j1 < nb && i1 < na && a.get(i1).timestamp == b.get(j1).timestamp) {
                                  if (a.get(i1).equals(b.get(j1))) {
                                      count++;
                                      intersect.add(b.get(j1)); // TODO debug
                                      // we have a match, so use up the a element
                                      i1++;
                                      match = true;
                                  }
                                  j1++;
                              }
                              if (!match) {
                                  i1++; // 
                              }
                              j1 = j; // reset j to start of matching timestamp region
                          }
                          i = i1; // when done, timestamps are different or we reached end of either or both arrays
                          j = j1;
                      }
                  }
          //        System.out.println("%%%%%%%%%%%%%%");
          //        printarr(a, "a");
          //        printarr(b, "b");
          //        printarr(intersect, "intsct");
                  return intersect;
              }
          
              // TODO test case
              void printarr(ArrayList<BasicEvent> a, String n) {
                  final int MAX = 30;
                  if (a.size() > MAX) {
                      System.out.printf("--------\n%s[%d]>%d\n", n, a.size(), MAX);
                      return;
                  }
                  System.out.printf("%s[%d] --------\n", n, a.size());
                  for (int i = 0; i < a.size(); i++) {
                      BasicEvent e = a.get(i);
                      System.out.printf("%s[%d]=[%d %d %d %d]\n", n, i, e.timestamp, e.x, e.y, (e instanceof PolarityEvent) ? ((PolarityEvent) e).getPolaritySignum() : 0);
                  }
              }
          

          【讨论】:

            【解决方案9】:
             //intersection of two arrays
            #include<iostream>
            using namespace std;
            int main() {
            
            int i=0,j=0,m,n;
            int arr1[i],arr2[j];
            cout<<"Enter the number of elements in array 1: ";
            cin>> m;
            cout<<"Enter the number of elements in array 2: ";
            cin>>n;
            for (i=0;i<m;i++){
                cin>> arr1[i];
            }
            for(j=0;j<n;j++){
                cin>> arr2[j];
            }
            for(j=0;j<n;j++){
                for(i=0;i<m;i++) {
                    if (arr1[i] == arr2[j]){
                    cout<< arr1[i];
                    cout << ' ';
                    break;
                    }
                } 
             }    
            
             return 0;
             }
            

            【讨论】:

              猜你喜欢
              • 2014-10-19
              • 2015-12-30
              • 2021-11-29
              • 1970-01-01
              • 2011-08-16
              • 1970-01-01
              • 1970-01-01
              • 2013-02-10
              • 1970-01-01
              相关资源
              最近更新 更多