如何计算以下代码段的复杂性?

时间:2015-03-17 20:32:08

标签: java algorithm

我是一个具有大O符号和算法复杂性的初学者。我试图弄清楚以下两个java方法的复杂性,它们都做同样的事情。但是会稍快一点。

ArrayList<Person> filter1(Person x, ArrayList<Person> people){
  ArrayList<Person> friends = new ArrayList<Person();
  for (Person y: people) friends.add(y);
  for (Person y: people) if (!x.knows(y)) friends.remove(y);
  return friends;
}

ArrayList<Person> filter2(Person x, ArrayList<Person> people){
  ArrayList<Person> friends = new ArrayList<Person();
  for (Person y: people) if (x.knows(y)) friends.add(y);
  return friends;
}

(knows()是一个布尔方法,如果x是y的朋友,则返回true,否则返回false)

我最初认为filter1和filter2都会在O(n)时间运行,但回过头来看,filter1将花费O(n + n)时间(这可以简化为O)是正确的( n)?)和filter2将花费O(n)时间,因为它只迭代一次人?

或者我完全错过了这一点?

3 个答案:

答案 0 :(得分:2)

  

说filter1将花费O(n + n)时间(这可以简化为O(n)吗?)并且filter2将采用O(n)

是否正确

O(n + n)确实可以简化为 O(n)。但filter()不是 O(n)

首先,引用ArrayList documentation(强调我的)的时间复杂度:

  

size,isEmpty,get,set,iterator和listIterator操作以恒定时间运行。 添加操作以分摊的常量时间运行,即添加n个元素需要O(n)时间。 所有其他操作都以线性时间运行(粗略地说)。

让我们分析一下filter1()的代码:

List<Person> filter1(Person x, List<Person> people){
    List<Person> friends = new ArrayList<>();
    for (Person y: people) {        // O(n)
        friends.add(y);             // amortized constant time
    }
    for (Person y: people) {        // O(n)
        if (!x.knows(y)) {
            friends.remove(y);      // O(n)
        }
    }
    return friends;
}

因此,由于List.remove() O(n) filter1() O(n + n 2 ) = O(n 2

现在是filter2()代码:

List<Person> filter2(Person x, List<Person> people){
    List<Person> friends = new ArrayList<>();
    for (Person y: people) {        // O(n)
        if (x.knows(y)) {
            friends.add(y);         // amortized constant time
        }
    }
   return friends;
}

所以 filter2() O(n)

现在,为了清除两个具有相同复杂性但运行时间不同的函数的混淆,请考虑以下函数:

  • h 1 (n)= n = O(n)
  • h 2 (n)= 1000·n = O(n)

h 1 (n) h 2 (n)的事实都是 O(n)并不意味着它们必须以彼此快的速度运行。实际上 h 2 (n)的运行时间比 h 1 (n)大一千倍。 O(n)时间复杂度意味着两个函数随着 n 的值的增加而线性增加。

考虑大O 定义:

  

f(n)= O(g(n))表示 c·g(n) f(n)的上限。因此存在一些常量 c ,使 f(n)总是≤c·g(n),足够大的 ñ

将定义应用于 h 1 (n),考虑 f(n)= n g(n) = n ,我们需要找到一个常数 c ,这样对于所有足够大的 n f(n)≤c·g(n) 。在这种情况下,对于任何c≥1n≤c·n ,因此我们证明 h 1 (n)= O (n)的

现在 h 2 (n),考虑 f(n)= 1000·n g(n)= n ,我们需要找到一个常数 c ,这样对于所有足够大的 n f(n)≤c·g(n)< / em>的。在这种情况下,对于任何c≥1000 1000·n≤c·n ,因此我们证明 h 2 (n) = O(n)

答案 1 :(得分:1)

您说O(n+n) = O(2n)可以简化为O(n)

filter1()
每个for循环都需要O(n),所以O(n) + O(n) = O(n),但根据此documentfriends.remove也将O(n),因为它必须遍历整个列表找到该项目并将其删除。但是,friends.add需要O(1)来添加项link

所以,复杂度计算就是这样的

(O(n) * O(1)) + (O(n) * O(n))
  = O(n) + O(n^2)
  = O(n^2)

对于filter2(),它是(O(n) * O(1)) = O(n)

答案 2 :(得分:0)

Big-O只关心曲线的一般形状,而不关心应用于它的系数。

因此,如果您计算复杂度O(n + n),则应将其简化为O(2n),然后移除O(n)的系数。

您的两个示例都以相对于人数的线性时间运行。从Big-O的角度来看,两个过滤器是等价的。