我是一个具有大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)时间,因为它只迭代一次人?
或者我完全错过了这一点?
答案 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)和 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≥1,n≤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)
,但根据此document,friends.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的角度来看,两个过滤器是等价的。