Java 这两个示例代码的时间复杂度是否不同,如何?
考虑到招聘过程中的一些问题,一个问题是如何在Java中从给定字符串中找到第一个非重复字符。 下面是两个示例代码,其中第一个能够通过所有测试用例,但由于时间复杂性,第二个在少数测试用例中失败。由于我对算法和复杂性分析还不熟悉,有人能帮助我了解这两个代码的时间复杂性是否不同以及如何不同吗 示例代码1:Java 这两个示例代码的时间复杂度是否不同,如何?,java,time-complexity,Java,Time Complexity,考虑到招聘过程中的一些问题,一个问题是如何在Java中从给定字符串中找到第一个非重复字符。 下面是两个示例代码,其中第一个能够通过所有测试用例,但由于时间复杂性,第二个在少数测试用例中失败。由于我对算法和复杂性分析还不熟悉,有人能帮助我了解这两个代码的时间复杂性是否不同以及如何不同吗 示例代码1: public static char firstNonRepeatingCharater(String s) { for(int i=0;i<s.length(); i++) {
public static char firstNonRepeatingCharater(String s) {
for(int i=0;i<s.length(); i++) {
if(s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) {
return s.charAt(i);
}
}
return '_';
}
public static char firstNonRepeatingCharater(String s) {
for(int i=0;i<s.length(); i++) {
int count = 0;
for(int j=s.length()-1;j>=0; j--) {
if(s.charAt(i) == s.charAt(j)) {
count++;
}
}
if(count == 1) {
return s.charAt(i);
}
}
return '_';
}
publicstaticcharfirstnonrepeatingcharacter(字符串s){
对于(inti=0;i,第二个代码段效率较低
在第二个代码段中,计算每个字符的出现次数,然后返回第一个出现一次的字符。这比调用s.indexOf(s.charAt(i))
和s.lastIndexOf(s.charAt(i))
效率低,后者只搜索两个出现的字符
您可以很容易地改进第二个代码段,使其行为与第一个代码段相同(即,一旦在索引处发现出现s.charAt(i)
,就会中断内部循环!=i
)
也就是说,这两个代码段具有相同的渐近运行时间,因为在最坏的情况下,indexOf
和lastIndexOf
都需要线性时间,这与第一个代码段的内部循环相同
另一方面,对于某些输入,第一个代码段比第二个快得多。例如,如果字符串的所有字符都相等,则第一个代码段将花费线性时间(因为每次调用indexOf
和lastIndexOf
都只需检查字符串的一个字符),但第二个代码段将花费二次时间
当然,比第一个或第二个代码片段更有效的实现是使用哈希集
,以跟踪已经出现的字符。这可以在字符串
的一次迭代中完成,这将需要线性时间。计算复杂性
首先,根据你的问题,我意识到快速解释时间复杂度
和大oh符号
会很好
引述自:
在计算机科学中,时间复杂度是计算复杂性
复杂度,描述运行
算法。时间复杂度通常通过计算
算法执行的基本操作数,假设
每个基本操作都需要固定的时间来完成
执行[…]
因为一个算法的运行时间在不同的输入之间可能会有所不同
同样大小,人们通常认为最坏情况下的时间复杂度,
这是输入给定数据所需的最大时间量
尺寸
大O符号
算法复杂度根据算法类型进行分类
以大O表示法出现的函数。例如,算法
O(n)是一个线性时间
一个常数的时间复杂度为O(n^alpha)的算法
alpha>1是一种多项式时间算法
关于两个代码示例
看看这两个代码示例。我们马上注意到一些事情
在示例1中,代码的大小要小得多。因此,我们可能需要更少的操作
然而,更重要的是,我们注意到第二个示例中有一个嵌套的for循环。第一个示例没有。这不一定会降低方法中代码的隐藏成本
让我们做一个小实验。当Z的大小为=1、10、100和1000时,让我们计算在平均糟糕的情况下(第一个非重复字符位于中间)所需的操作数
注意:在这个示例/思维实验中,我将把每条线作为成本1的操作进行评估。这是一个总体简化。
请原谅在计算操作次数时出现的任何遗漏
Algorithm 1: (size of s, lines executed)
-
1, 3
10, (2*5)+1 = 11
100, (2*50)+1 = 101
1000, (2* 500) + 1 = 1001
Total = (2* N/2 ) + 1
我们看到,结果执行数与初始输入大小成线性关系
Algorithm 2: (N = size of s, lines executed)
-
1, 7
10, 2(5*5) + 2
100, 2(50*50) + 2
1000, 2(500*500) + 2
Total = ((N/2) *2 + 2*(N/2)*(N/2) + 2
在算法1中,我们看到复杂度与s的输入大小成线性关系,特别是O(n)
。
在算法2中,我们看到它是多项式时间,特别是O(n^2)
。
然而,一旦我们考虑到indexOf
和lastIndexOf
的实际成本,这就错了
添加indexOf和LastIndexOf的成本
算法1:(粗略估计的运算次数)
在第一个示例代码中,使用了lastIndexOf方法。因此,为了获取最后一个索引,它可能还使用了loop。如果不是,它如何能够找到it@Guy第一个也有循环,因为indexOf
和lastIndexOf
循环都是O(n^2*m),实际上,indexOf也有O(n*m)复杂度,其中n是文本的长度,m是模式的长度,这里m是1,因此在这两种情况下,总体复杂度都是O(n^2)。第二个代码段可以(也应该)break
在第二次count++
之后,不需要寻找进一步的匹配项。@Raghu Chahar,但第二次在时间复杂度方面失败,因为有几个测试用例。第二段不是真的。indexOf
返回第一次出现。对于字符串abbaba
和i=5
,它将返回0
@AmOnAlEn和Luk2302是的,你是对的。我已经错过了。谢谢。现在纠正。@ MunOS,你考虑过<代码> >索引> >代码> >代码> ListTimeXOF的复杂性吗?它们也至少有O(n)。复杂性。@Raghu Chahar,正如我所说的,我简化了。我的主要观点是嵌套的for循环将复杂性增加到了N^2。我认为嵌套的for循环更糟糕。然而,indexOf和lastIndexOf本身也有迭代字符O(N)的代价.indexOf
和lastIndexOf
都是线性的。第一个片段也是O(n^2
Let n=Size of the String S
for(int i=0;i<s.length(); i++) // - N/2
if(s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) { // ~ worst case = (N/2 + N/2) * N/2
return s.charAt(i); // 1 operation
Total = N/2 + (N/2 + N/2)*N/2 +1
= N^2/2 + N/2 + 1
for(int i=0;i<s.length(); i++) { // - executed N/2 times
int count = 0; // - executed N/2 times
for(int j=s.length()-1;j>=0; j--) { // Executed (n/2 * n/2 )
if(s.charAt(i) == s.charAt(j)) { // Executed (n/2 * n/2 )
count++; // Executed 1 time
}
}
if(count == 1) { //Evaluated N/2 times
return s.charAt(i); // Executed 1 time
}
}
return '_';
Total = N/2 + N/2 + 2(N/2*N/2) + 1
= N^2/2 + N + 1
Algo1: N^2/4 + N/2 + 1
VS
Algo2: N^2/2 + N + 1