在两个未排序的数组中查找公共元素

时间:2013-08-27 22:28:23

标签: java

我尝试找到解决此问题的方法: 我有两个整数A和B(A和B可以有不同的尺寸)。我必须在这两个数组中找到共同的元素。我有另一个条件:公共元素之间的最大距离是k。 所以,这是我的解决方案。我认为是正确的:

for (int i = 0; i<A.length; i++){
    for (int j=jlimit; (j<B.length) && (j <= ks); j++){
        if(A[i]==B[j]){
            System.out.println(B[j]);
            jlimit = j;
            ks = j+k;
        }//end if
    }
}

有没有办法做出更好的解决方案?有什么建议?提前谢谢!

8 个答案:

答案 0 :(得分:5)

根据您的解释,我认为最直接的方法是读取数组A,将所有元素放在Set(setA)中,对B(setB)执行相同操作,并使用retainAll方法找到两个集合的交集(属于这两个集合的项目)。

您将看到k distance根本没有被使用,但我认为无法使用导致代码更快或更可维护的条件。我提倡的解决方案在没有强制执行该条件的情况下工作,因此当条件为真时(即称为“削弱先决条件”),它也可以工作

答案 1 :(得分:5)

实施二进制搜索和快速排序!

这将导致大量的代码....但最快的结果。

您可以使用类似的快速排序对较大数组的元素进行排序,这将导致O(nlogn)。

然后遍历每个值的较小数组,并对另一个数组中的特定元素进行二进制搜索。在二进制搜索中为距离添加一些逻辑。

我认为你可以将复杂性降低到O(nlogn)。最坏情况O(n ^ 2)

伪代码。

larger array equals a
other array equals b

sort a

iterate through b
       binary search b at iterated index
     // I would throw (last index - index) logic in binary search
     // to exit out of that even faster by returning "NOT FOUND" as soon as that is hit.
       if found && (last index - index) is less than or equal 
          store last index
          print value

这是解决问题的最快方法。

答案 2 :(得分:2)

虽然这可能是作弊,但由于它使用HashSet s,因此对于此算法的Java实现来说非常好。如果您需要算法的伪代码,请不要再阅读。

JavaDoc中的源代码和作者。干杯。

/**
 * @author Crunchify.com
 */
public class CrunchifyIntersection {

    public static void main(String[] args) {
         Integer[ ] arrayOne = { 1, 4, 5, 2, 7, 3, 9 };
         Integer[ ] arrayTwo = { 5, 2, 4, 9, 5 };

         Integer[ ] common = iCrunchIntersection.findCommon( arrayOne, arrayTwo );

         System.out.print( "Common Elements Between Two Arrays: " );       
         for( Integer entry : common ) {
              System.out.print( entry + " " );
         }
   }

   public static Integer[ ] findCommon( Integer[ ] arrayOne, Integer[ ] arrayTwo ) {

        Integer[ ] arrayToHash;
        Integer[ ] arrayToSearch;

        if( arrayOne.length < arrayTwo.length ) {
            arrayToHash = arrayOne;
            arrayToSearch = arrayTwo;
        } else {
            arrayToHash = arrayTwo;
            arrayToSearch = arrayOne;
        }

        HashSet<Integer> intersection = new HashSet<Integer>( );

        HashSet<Integer> hashedArray = new HashSet<Integer>( );
        for( Integer entry : arrayToHash ) {
            hashedArray.add( entry );
        }

        for( Integer entry : arrayToSearch ) {
            if( hashedArray.contains( entry ) ) {
                intersection.add( entry );
            }
        }

        return intersection.toArray( new Integer[ 0 ] );
    }
 }

答案 3 :(得分:2)

您的实施大致为O(A.length * 2k)。

如果您希望保持“不超过k”逻辑,这似乎就是您要做的最好的事情,因为排除了排序和使用集即可。我会改变一点,使你的代码更容易理解。

  1. 首先,我会确保您迭代两个数组中较小的一个。这将使复杂度为O(min(A.length,B.length)* 2k)。

    要理解这个目的,请考虑A有1个元素而B有100个的情况。在这种情况下,我们只会在外部循环中执行一次迭代,并且k内循环中的迭代。

    现在考虑A何时有100个元素,B有1.在这种情况下,我们将在外循环上执行100次迭代,并在内循环上执行1次迭代。

    如果k小于长数组的长度,则在外循环中迭代较短的数组会更有效。

  2. 然后,为了便于阅读,我会改变你计算k距离的方法。我写的代码证明了这一点。

  3. 这就是我要做的事情:

    //not sure what type of array we're dealing with here, so I'll assume int.
    int[] toIterate;
    int[] toSearch;
    
    if (A.length > B.length)
    {
        toIterate = B;
        toSearch = A;
    }
    else
    {
        toIterate = A;
        toSearch = B;
    }
    
    for (int i = 0; i < toIterate.length; i++)
    {
        // set j to k away in the negative direction
        int j = i - k;
    
        if (j < 0) 
            j = 0;
    
        // only iterate until j is k past i
        for (; (j < toSearch.length) && (j <= i + k); j++)
        {
            if(toIterate[i] == toSearch[j])
            {
                System.out.println(toSearch[j]);
            }
        }
    }
    

    您使用jlimitks可能会有效,但是对于普通程序员来说,处理这样的k距离对于普通程序员来说更容易理解(并且效率稍高)。

答案 4 :(得分:1)

O(N)解决方案(BloomFilters):

这是一个使用bloom过滤器的解决方案(实现来自Guava库)

public static <T> T findCommon_BloomFilterImpl(T[] A, T[] B, Funnel<T> funnel) {
    BloomFilter<T> filter = BloomFilter.create(funnel, A.length + B.length);
    for (T t : A) {
        filter.put(t);
    }
    for (T t : B) {
        if (filter.mightContain(t)) {
            return t;
        }
    }
    return null;
}

像这样使用它:

    Integer j = Masking.findCommon_BloomFilterImpl(new Integer[]{12, 2, 3, 4, 5222, 622, 71, 81, 91, 10}, new Integer[]{11, 100, 15, 18, 79, 10}, Funnels.integerFunnel());
    Assert.assertNotNull(j);
    Assert.assertEquals(10, j.intValue());

在O(N)中运行,因为计算Integer的哈希非常简单。所以仍然是O(N),如果你可以将你的元素的散列计算减少到O(1)或小O(K),其中K是每个元素的大小。

O(N.LogN)解决方案(排序和迭代):

排序和遍历数组将引导您进入O(N * log(N))解决方案:

public static <T extends Comparable<T>> T findCommon(T[] A, T[] B, Class<T> clazz) {
    T[] array = concatArrays(A, B, clazz);
    Arrays.sort(array);
    for (int i = 1; i < array.length; i++) {
        if (array[i - 1].equals(array[i])) {     //put your own equality check here
            return array[i];
        }
    }
    return null;
}
当然,

concatArrays(~)在O(N)中。 Arrays.sort(~)是QuickSort的双轴实现,复杂度为O(N.logN),再次遍历数组为O(N)。

所以我们有O((N + 2).logN)〜&gt; O(N.logN)。

作为一般情况解决方案(没有问题的“k内”条件)比你的好。在您的确切情况下,应该考虑k“接近”N.

答案 5 :(得分:1)

如果数组已经排序,则为简单解决方案

 public static void get_common_courses(Integer[] courses1, Integer[] courses2) {
        // Sort both arrays if input is not sorted 
        //Arrays.sort(courses1);
        //Arrays.sort(courses2);
        int i=0, j=0;
        while(i<courses1.length && j<courses2.length) {
            if(courses1[i] > courses2[j]) {
                j++;
            } else if(courses1[i] < courses2[j]){
                i++;
            } else {
                System.out.println(courses1[i]);
                i++;j++;
            }
        }
}

Apache commons collections API以高效的方式完成了这项工作而没有排序

    public static Collection intersection(final Collection a, final Collection b) {
    ArrayList list = new ArrayList();
    Map mapa = getCardinalityMap(a);
    Map mapb = getCardinalityMap(b);
    Set elts = new HashSet(a);
    elts.addAll(b);
    Iterator it = elts.iterator();
    while(it.hasNext()) {
        Object obj = it.next();
        for(int i=0,m=Math.min(getFreq(obj,mapa),getFreq(obj,mapb));i<m;i++) {
            list.add(obj);
        }
    }
    return list;
}

答案 6 :(得分:1)

使用Java 8的解决方案

static <T> Collection<T> intersection(Collection<T> c1, Collection<T> c2) {
    if (c1.size() < c2.size())
        return intersection(c2, c1);
    Set<T> c2set = new HashSet<>(c2);
    return c1.stream().filter(c2set::contains).distinct().collect(Collectors.toSet());
}

使用Arrays :: asList和基元的盒装值:

Integer[] a =...    
Collection<Integer> res = intersection(Arrays.asList(a),Arrays.asList(b));

答案 7 :(得分:0)

通用解决方案

public static void main(String[] args) {
    String[] a = { "a", "b" };
    String[] b = { "c", "b" };
    String[] intersection = intersection(a, b, a[0].getClass());
    System.out.println(Arrays.toString(intersection));
    Integer[] aa = { 1, 3, 4, 2 };
    Integer[] bb = { 1, 19, 4, 5 };
    Integer[] intersectionaabb = intersection(aa, bb, aa[0].getClass());
    System.out.println(Arrays.toString(intersectionaabb));
}

@SuppressWarnings("unchecked")
private static <T> T[] intersection(T[] a, T[] b, Class<? extends T> c) {
    HashSet<T> s = new HashSet<>(Arrays.asList(a));
    s.retainAll(Arrays.asList(b));
    return s.toArray((T[]) Array.newInstance(c, s.size()));
}

<强>输出

[b]
[1, 4]