我想知道下面的代码示例的两个变体在技术上是否具有相同的运行时复杂性。
例如(为了说明一点,我们可以说字符串的长度是偶数):
//counting how many times char 'c' appear in the string s
String s = "ascdwcccdweccaaa"; //the "array", contain char 'c' 6 times
int counter = 0; //times char 'c' appear in the string
for(int i=1; i <= s.length()/2; i++)
{
if(s.charAt(i-1) == 'c')
counter++;
if(s.charAt(s.length()-i) == 'c')
counter++;
}
与此相比......
for(int i=0; i < s.length(); i++)
if(s.charAt(i) == 'c')
counter++;
第一个示例使用索引从数组的结尾和开始检查,直到它到达数组的中间(假设为O(n/2)
)
虽然第二个例子是从头到尾严格检查数组中的所有字符(假设是O(n)
)
在第一个示例中,我需要使用两个ifs
,而在第二个示例中,我需要使用一个if
。
这两个代码的时间复杂度在技术上是否相同? (假设我在第一个例子中只传递了一半数组时使用了两个ifs
,那么它们是否“均匀”?)
答案 0 :(得分:2)
您的两个程序都具有完全相同的O(n)
复杂度。实际上O(n/2)
等于O(n)
,因为它是相同的顺序。但即使考虑到这一点:你的迭代次数减少两倍,执行的工作量增加了两倍。总计是一样的。
因此程序具有相同的复杂性。然而,第一个有几个缺点:
阅读更复杂,更不清晰
具有奇数个元素的数组呢?
JVM可能会做一些花哨的优化。边界检查(当VM发现你只是遍历整个数组时,VM可能不会一直检查边界)。使用这种幻想可能会使优化器混淆。
据说,在尝试优化代码时,您的代码更难以阅读,不正确且可能更慢。
答案 1 :(得分:0)
O(n)和O(n / 2)都具有相同的生长速率(线性),因此具有相同的时间复杂度。
答案 2 :(得分:0)
除了你的算法不等同于你的第一个失败的长度为1(1/2==0
),当假设每个原子操作的统一成本为1时,你的第一个算法具有以下复杂性:
for (int i=1; // 1
i <= s.length()/2; // 3 + ⎡n/2⎤ · ( 3
i++) { // 1
if(s.charAt(i) == 'c') // 3
counter++; // 1
if(s.charAt(s.length()-i) == 'c') // 4
counter++; // 1
} // )
n ≥8时,总统一成本为4 +⎡ n /2⎤·13≤14·⎡ n /2⎤ 。由于14·⎡ n /2⎤≤28· n 而28是一个常数因子,你的算法是Ο( n )
或者,正如已经说过的那样: n / 2·2仍然等于 n 。