【问题标题】:Complexity of the following code以下代码的复杂性
【发布时间】:2016-07-24 08:15:37
【问题描述】:

我被分配了以下任务:

给定 - 2 个列表。第一个列表的大小为 N1,第二个列表的大小为 N2。每个列表没有相同的元素。 编写一段代码,用第一个和第二个列表中的元素创建一个新列表。这个列表也不应该有相同的元素。 还要估计代码的复杂性。

我写了以下代码:

public class Lists {    
    static ArrayList<Integer> getNewList(ArrayList<Integer> list1, 
                                         ArrayList<Integer> list2) {
        ArrayList<Integer> tmp = new ArrayList<>();
        for (Integer i : list1) {
            tmp.add(i);
        }
        for (Integer i : list2) {
            if (!list1.contains(i)) 
                tmp.add(i);
        }
        return tmp;
    }

    public static void main(String[] args) { 
        Integer[] arr1 = {1, 2, 3, 14, 15, 16};        
        Integer[] arr2 = {3, 6, 7, 8, 14};
        ArrayList<Integer> list1 = new ArrayList<>(Arrays.asList(arr1));
        ArrayList<Integer> list2 = new ArrayList<>(Arrays.asList(arr2));
        for (Integer i : getNewList(list1, list2)) {
            System.out.print(i + " ");
        }
    }
}

并说 getNewList 方法的执行时间与 N1*N2 成正比。作为回复,我收到以下内容,没有任何解释 - “你错了,这段代码的复杂性不是 N1*N2”。

那么有人能说出正确答案吗?并解释复杂性是如何确定的?

【问题讨论】:

    标签: java algorithm performance


    【解决方案1】:

    您的代码的复杂度是O(N1*N2),但您可以使用HashSet 来确定两个列表中出现的数字会更好:

    static ArrayList<Integer> getNewList(ArrayList<Integer> list1, 
                                         ArrayList<Integer> list2) {
        ArrayList<Integer> tmp = new ArrayList<>();
        HashSet<Integer> dups = new HashSet<>();
        tmp.addAll(list1);
        dups.addAll(list1);
        for (Integer i : list2) {
            if (!dups.contains(i)) 
                tmp.add(i);
        }
        return tmp;
    }
    

    这将为您提供O(N1+N2) 运行时间,因为插入和查找在HashSet 中花费预期的O(1) 时间。

    【讨论】:

      【解决方案2】:

      嗯,对你的问题的简短回答是:复杂度是 N1 + (N2*N1)/2 + N3(新列表的大小),应该是 O(N1*N2)

      细分:

      for (Integer i : list1) {
        tmp.add(i);
      } 
      -> clearly, this is N1
      

      for (Integer i : list2) {
        if (!list1.contains(i)) 
          tmp.add(i);
      } 
      -> list2 iteration => N2
      -> for each of this iteration, you call .contain method 
         which uses sequential search, resulting in N1/2 iterations (on average)
      -> So, you get N2*N1/2
      

      在 main 中有一个循环,从 0 迭代到 N3(这是新列表的长度)

      所以,总的来说,你得到 N1 + (N2*N1)/2 + N3,应该在 O(N1*N2) 中

      【讨论】:

        【解决方案3】:

        我肯定也看到了O(N1 * N2) 的复杂性。我猜你的教授忽略了contains 调用的成本如下:

        for (Integer i : list2) {
            if (!list1.contains(i)) 
                tmp.add(i);
        }
        

        ArrayList 上的containsO(N) 复杂度。由于您对 list2 的循环也是 O(N),因此您最终会得到 O(N1 * N2)

        【讨论】:

          【解决方案4】:

          感谢@Slanec的解释,我重新检查了contains(Object obj)在JDK中的实现,发现如下

          public boolean contains(Object obj) {
              return indexOf(obj) >= 0;
          }
          
          public int indexOf(Object obj) {
              if (obj == null) {
                  for (int i = 0; i < size; i++)
                      if (elementData[i] == null)
                          return i;
          
              } else {
                  for (int j = 0; j < size; j++)
                      if (obj.equals(elementData[j]))
                          return j;
          
              }
              return -1;
          }
          

          显然contains(Object obj)的时间复杂度是O(n)。(我一开始以为是O(1))

          所以代码的时间复杂度应该是O(N1 * N2)而不是O(n)。

          【讨论】:

          • 除非你能用实际的解释证明我错了,否则我会说不。第二个循环针对list1list2 中的每个元素进行contains() 检查。对列表的contains() 调用是线性的,O(N1),循环本身显然也是线性的。因此复杂度为 N1*N2。
          猜你喜欢
          • 2017-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多