如何找到3个数组中共有的最小数字?

时间:2019-06-12 12:42:35

标签: java arraylist time-complexity complexity-theory

这是问题:

Given 3 random arrays of integers, write a method to find the smallest number that is common among the 3 arrays. HINT: Sort first and just traverse first few elements until you reach the common number
  [-1,-2, 4,5,6,1,2,3,3,3,1,1,1]
  [54,6,7,8,1,3,5,1]
  [1,6,9,1,0,2,1]
  result = 1

我写了一个可行的解决方案代码,但我想知道是否有更简单,更有效的方法来实现这一目标。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Smcomm {

    public static void main(String[] args) {
        int[] A = {-1,-2,4,5,6,1,2,3,3,3,1,1,1};
        int[] B = {54,6,7,8,1,3,5,1};
        int[] C = {1,6,9,1,0,2,1};

        ArrayList<Integer> ar1 = new ArrayList<Integer>();
        ArrayList<Integer> ar2 = new ArrayList<Integer>();
        ArrayList<Integer> ar3 = new ArrayList<Integer>();

        for(int el: A){
            ar1.add(el);
        }

        for(int el: B){
            ar2.add(el);
        }

        for(int el: C){
            ar3.add(el);
        }

        Collections.sort(ar1);
        Collections.sort(ar2);
        Collections.sort(ar3);

        printer(ar1);
        printer(ar2);
        printer(ar3);

        finder(ar1,ar2,ar3);





    }


    static void printer(ArrayList ar){

        Iterator<Integer> it = ar.iterator();

        while(it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("--------------------");
    }

    static void finder(ArrayList ar1, ArrayList ar2, ArrayList ar3){
        ar1.retainAll(ar2);
        ar1.retainAll(ar3);
        if(ar1.size()>0){
            System.out.println(ar1.get(1));
        }else {
            System.out.println("no comm el");
        }
    }

}

不能完全建议我的方法是 retainAll ,因为我认为它的复杂度为O(n ^ 2)。

我不需要查找所有元素,但我只想查找最小的元素。您是否知道在数组中找到第一个公共元素时是否有可能停止该方法的执行?应该没困难,因为数组已经排序。

感谢您的帮助。

6 个答案:

答案 0 :(得分:3)

一种略有不同的方法:

  • 将最短数组转换为排序的集合
  • 将其他两个数组转换为普通数组
  • 迭代排序后的集合,并针对每个条目检查其他两个集合是否包含

其他两个都包含的第一个数字必须是最小的公共条目。这样可以防止您查看重复的条目(在这里真的没关系),并且还避免了对所有三个数组进行排序。

最后,对于如此小的示例,任何正确的解决方案都足够了。仅当您在谈论包含成千上万甚至数百万个条目的列表时,不同解决方案的潜在权衡才有意义。

答案 1 :(得分:2)

首先,不需要将数组转换为List(因为您不需要使用retainAll,它可以找到3个数组的所有公共元素,更多)超出您的需求)。

似乎需要对数组(或列表)进行排序,以确保O(NlogN)的最坏情况复杂度。我不认为您可以做得更好。

一旦有了3个排序数组,就可以使用3个索引(每个数组一个)在一个while循环中循环访问3个数组,直到找到在所有3个数组中都出现的第一个元素。这将花费线性时间。

最后一件事-您当前的解决方案有一个小错误-应该输出ar1.get(0),而不是ar1.get(1)

答案 2 :(得分:0)

您只能使用Arrays和foreach循环以简单的方式实现此目的:

  • 排序数组
  • 遍历数组A,然后移动B和C直到数字> A。
  • 检查它们是否相等,否则转到下一个数字,但要避免再次遍历相同的索引,请从其停止处继续。

这里是如何实现此目的的示例:

public static void main(String[] args) {
    int[] A = {-1, -2, 4, 5, 6, 1, 2, 3, 3, 3, 1, 1, 1};
    int[] B = {54, 6, 7, 8, 1, 3, 5, 1};
    int[] C = {1, 6, 9, 1, 0, 2, 1};

    Arrays.sort(A);
    Arrays.sort(B);
    Arrays.sort(C);

    Optional<Integer> inCommon = findInCommon(A, B, C);

    if (inCommon.isPresent()) {
        System.out.println(inCommon.get());
    } else {
        System.out.println("no comm el");
    }
}

private static Optional<Integer> findInCommon(int[] A, int[] B, int[] C) {
    int lastB = 0;
    int lastC = 0;

    for (int valA : A) {
        while (B[lastB] < valA) lastB++;
        while (C[lastC] < valA) lastC++;

        if (valA == B[lastB] && valA == C[lastC]) {
            return Optional.of(valA);
        }
    }
    return Optional.empty();
}

答案 3 :(得分:0)

使用Java 8流的另一种方法,先计算交集,然后求出最小值:

 List<Integer> list1 = Arrays.asList(A);
 List<Integer> list2 = Arrays.asList(B);
 List<Integer> list3 = Arrays.asList(C);

 Set<Integer> commonElements = list1.stream().filter(list2::contains).filter(list3::contains).collect(Collectors.toSet());
 int minimumCommonElement = Collections.min(commonElements);

答案 4 :(得分:0)

我认为实现此目的的最简单方法是将元素存储在TreeSet(自然排序并删除重复项)中,而不是将其存储在ArrayList中,然后只需遍历set 1的每个元素,直到找到一个全部三个集合中都存在。

static void finder(Set<Integer> s1, Set<Integer> s2, Set<Integer> s3) {
    boolean found=false;
    for (int i: s1) {
        if (s2.contains(i) && s3.contains(i)) {
            System.out.println(i);
            found=true;
            break;
        }
    }
    if (!found) {
        System.out.println("no comm el");
    }
}

答案 5 :(得分:0)

以下内容效果很好。在我看来,您只需要对列表之一进行排序,并将其用作插入的来源。

      List<Integer> list1 = new ArrayList<>(
      List.of(-1, -2, 4, 5, 6, 3, 1, 2, 3, 3, 3, 1, 1, 1));
      List<Integer> list2 = new ArrayList<>(List.of(54, 6, 7, 8, -4, 1, 3, 5, 1));
      List<Integer> list3 = new ArrayList<>(List.of(1, 6, 9, 1, 0, -4, 2, 1));

      Collections.sort(list1);
      for (int e : list1) {
         if (list2.contains(e) && list3.contains(e)) {
            System.out.println(e);
            break;
         }
      }

这里的想法是,如果elist1(排序列表)中最小,那么如果它在list2list3中,则它必须是这三个都是最小的。

通过使用集合删除重复项可能会有所改善。但是,除非最初将数据包含在要启动的集合中,否则也可能存在性能问题。

刚看过其他答案后,有人建议对最小的列表(或集合)进行排序。这是我从未考虑过的好主意。