isPalindrome() 的时间复杂度 O()
我有这个方法,isPalindrome(),我试图找到它的时间复杂度,并更有效地重写代码。
boolean isPalindrome(String s) {
boolean bP = true;
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) != s.charAt(s.length()-i-1)) {
bP = false;
}
}
return bP;
}
现在我知道这段代码会检查字符串的字符,看看它是否与之前的字符相同,如果是,则不会更改 bP。
而且我想我知道操作是 s .length()、s.charAt(i) 和 s.charAt(s.length()-i-!))。
我认为时间复杂度为 O(N + 3)?这是正确的,如果不是的话,它是什么以及如何计算出来的。
另外,为了提高效率,将字符存储在临时字符串中会好吗?
I have this method, isPalindrome(), and I am trying to find the time complexity of it, and also rewrite the code more efficiently.
boolean isPalindrome(String s) {
boolean bP = true;
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) != s.charAt(s.length()-i-1)) {
bP = false;
}
}
return bP;
}
Now I know this code checks the string's characters to see whether it is the same as the one before it and if it is then it doesn't change bP.
And I think I know that the operations are s.length(), s.charAt(i) and s.charAt(s.length()-i-!)).
Making the time-complexity O(N + 3), I think? This correct, if not what is it and how is that figured out.
Also to make this more efficient, would it be good to store the character in temporary strings?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
只是 O(N)。
说 O(N+3) 并没有多大意义——常数因子被忽略了。
当它发现不匹配时,你可以通过中断来使其更快:(
这并没有改变它是 O(N) 的事实,但在大多数情况下它会加快它的速度。)
这是不正确的:
它检查开头的字符与结尾的字符是否匹配,所以换句话说,它是一个天真的 回文 检查器。
另一个加速将循环直到
s.length()/2
- 否则您将对回文字符串进行所有比较两次。It's just O(N).
Saying O(N+3) isn't really meaningful - constant factors are ignored.
You could make it quicker by breaking out when it finds a mismatch:
(Not that that changes the fact that it's O(N), but it will speed it up in most cases.)
This isn't true:
It checks that the characters at the start match the ones at end, so in other words it's a naive palindrome checker.
Another speedup would to loop until
s.length()/2
- otherwise you're doing all the comparisons twice for a string that is a palindrome.给定的代码似乎是通过检查字符“N”是否与字符“length-N”相同来检查字符串是否是回文。 突破(返回 false)来提高效率
对于这些建议,我会添加
鉴于这一切:
The given code appears to be checking if a string is a palindrome by checking if character "N" is the same as character "length-N". As already mentioned, you can increase the efficiency by
To those suggestions, I'd add
Given all that:
这很可能是 Java 中最有效的实现:优点:
使用直接 char[] 访问来避免在 charAt 中进行边界检查与所有其他提出的解决方案一样,它仍然是 O(N)。
我刚刚测量了一个非常大的字符串所提供的解决方案的时间(时间以纳秒为单位):
首先,上面的测量是使用客户端虚拟机完成的。因此计算
i < (chars.length / 2)
未作为常量内联。 使用 -server Vm 参数给出了更好的结果:稍微极端一下:
首先警告一句:不要在您打算使用/发布的任何程序中使用此代码。
正如所指出的,它包含隐藏的错误,并且不遵守 Java API 并且没有错误处理在评论中。它纯粹是为了证明通过肮脏的技巧可以获得的理论上的性能改进。
从字符串复制数组时会产生一些开销,因为字符串类在内部进行了防御性复制。
如果我们直接从字符串中获取原始的 char[] ,我们可能会降低一些性能,但代价是对字符串使用反射和取消保存操作。这让我们的性能又提高了 20%。
次数:
This is most likely the most efficient implementation in Java:Benefits:
Uses direct char[] access to avoid aboundary checks done in charAtIt is, like all other proposed solutions, still O(N).
I just measured the times fo the presented solutions for a really big string (times in nanoseconds):
First, the measurements above were done with the client VM. Thus the calculation
i < (chars.length / 2)
was not inlined as a constant. Using the -server Vm parameter gave a much better result:To drive it a bit extreme:
A word of warning first: DO NOT USE THIS CODE IN ANY PROGRAM YOU INTEND TO USE/SHIP.
It contains hidden bugs and does not obey the Java API and has not error handling, as pointed out in the comments. It serves purely to demonstrate the theoretical performance improvements obtainable by dirty tricks.
There is some overhead when copying the array from the string, because the string class internally makes a defensive copy.
If we obtain the original char[] from the string directly, we can squeeze out a bit of performance, at the cost of using reflection and unsave operations on the string. This gets us another 20% performance.
Times:
这是另一个具有两个相反指针的解决方案:
复杂性再次为 O(n)。
更详细地讲一下:假设每次操作花费 1 个单位。比较、赋值、算术运算、函数调用各花费 1 个单位。因此,调用
isPalindrome
在最坏情况下(s
是回文)会花费以下示例:由于常数因子(此处为 5 + 7/2)被省略,我们最终得到 5 + 7/2 · n ∈ O(n)。
Here’s another solution with two opposite pointers:
The complexity is again O(n).
Going a little more into details: Let’s say every operation costs 1 unit. Comparisons, assignments, arithmetic operations, function calls each cost 1 unit. So a call of
isPalindrome
costs at worst case (s
is a palindrome) takes for example:And since the constant factor (here 5 + 7/2) is omitted, we end up with 5 + 7/2 · n ∈ O(n).
是 O(N)。您正在进行 N 次比较,其中 N=s.length()。每次比较都需要 O(1) 时间,因为它是单个字符比较。
+3 并不重要,因为渐近符号只关心最高阶项。
It is O(N). You are doing N comparisons, where N=s.length(). Each comparison takes O(1) time, as it is a single character comparison.
+3 does not matter, as asymptotic notation only cares about the highest order term.
首先,对于这个问题,不可能有一个单线程解决方案,其中“最坏情况”的复杂度优于任意输入字符串的
O(N)
。简而言之,任何算法都必须在最坏的情况下查看字符串中的每个字符。理论上,您可以使用硬件并行性来改进O(N)
;即有无限可扩展数量的处理器在字符串的不同部分上工作。实际上,根本很难实现任何加速。将输入字符串(或相关部分)发送到每个处理器的成本将是“O(N)”,除非有一些我不知道的解决方案。其次,正如您所看到的,
O(N)
行为并不是最终的答案。您还需要将乘法常数视为 N ->无穷大,以及较小的 N 值。第三,@dfa 说微观优化不是你的事。他走在正确的道路上,但我认为事情没有那么明确。 IMO,微优化是浪费时间,除非 1) 您的应用程序确实需要尽可能快地运行,并且 2) 您对应用程序的分析表明此特定计算确实< /em> 是一个重要的瓶颈。
最后,微优化可以使程序对于一个特定的硬件平台/JIT 编译器更快,但对于另一个平台/JIT 编译器可能会变慢。 JIT 编译器更难为复杂的微优化代码生成有效的代码。如果您使用反射来访问(例如)String 类的内部结构,您的代码实际上可能会在某些平台上失败。 (Java 类库规范中没有任何内容表明字符串有一个名为“value”的私有字段,即
char[]
!!!)First, there cannot be a single-thread solution for this problem where the "worst-case" complexity is better than
O(N)
for arbitrary input strings. Simply put, any algorithm must look at every character in the string in the worst case. In theory you can improve onO(N)
using hardware parallelism; i.e. have an indefinitely scalable number of processors working on different portions of the string. In practice, it would be hard to achieve any speed-up at all. The cost of sending the input string (or relevant parts) to each processor is going be 'O(N)', unless there is some solution that I don't know about.Second, as you can see
O(N)
behaviour isn't the final answer. You also need to consider the multiplicative constant as N -> infinity, and the lesser terms for smaller values of N.Third, @dfa says that it is not your business to micro-optimize. He's on the right track, but I don't think it is quite as clear cut as that. IMO, it is a waste of time micro-optimizing unless 1) your application really needs to run as fast as possible, and 2) your profiling of the application shows that this particular calculation really is a significant bottle-neck.
Finally, a micro-optimization that makes a program faster for one particular hardware platform / JIT compiler, may make it slower for another one. Complex micro-optimized code is harder for a JIT compiler to generate efficient code for. And if you use reflection to access the internals of (say) the String class, your code might actually fail on some platforms. (Nothing in the Java Class Library specification says that a String has a private field called "value" that is a
char[]
!!!)那么首先,这个方法应该做什么?
我的猜测:确定一个字符串是否是回文。
很明显,你将无法在 O(N) 下得到它:
另一个问题是,它是最有效的解决方案吗?也许不是。
改进空间:
将其减半。您检查所有字符两次(就像 Michiel Buddingh 建议的那样)。
预先获取字符数组。这样可以节省
chatAt()
内部发生的一些索引检查。所有其他操作,
charAt()
和length()
,在标准 String 实现中都是 O(1)。So first of all, what is the method supposed to do?
My guess: determine if a string is a palindrome.
Quite obviously, you will not be able to get it down under O(N):
The other question is, is it the most efficient solution? Maybe not.
Room for improvement:
Cut it in half. You check all characters two times (like Michiel Buddingh suggested).
Obtain the character array beforehand. That spares you some index checks that occur inside
chatAt()
.All other operations,
charAt()
andlength()
, are O(1) with the standard String implementation.第一个改进:一旦发现不匹配就可以破解,对吧?
First improvement: you can break once you find a nonmatch, right?
假设循环中的操作可以在恒定时间内执行,则复杂度为 O(N)。
由于“Big-O”符号衡量的是增长,而不是纯粹的速度,因此可以忽略常数因素。这让我们得出 O(N+3) 等于 O(N) 的结论。
Assuming the operations in your loop can be executed in constant time, the complexity is O(N).
Since "Big-O" notation measures growth, not pure speed, constant factors can be disregarded. This leaves us with the conclusion that O(N+3) is equal to O(N).
Big O 复杂度总是没有常量(因为对于 N->oo,它们并不重要) 。所以你的时间复杂度只是
O(n)
。这不是你的工作。 JIT 编译器将为您处理这种微观优化。
Big O complexity is always without costants (since for N->oo, they are unimportant). So your time complexity is simply
O(n)
.This is not your job. The JIT compiler will handle this micro optimization for you.
您可以通过在 (i == (s.length() / 2)+1) 处停止来将函数的复杂性降低一半。它与 Big O 术语无关,但仍然是一个相当不错的收益。
You can cut the complexity of the function in half by stopping at (i == (s.length() / 2)+1). It is not relevant in Big O terms, but it is still a pretty decent gain.
或者你可以简单地这样做
这仍然是 O(n) 时间复杂度。
Or you can just simply do
This is still O(n) time complexity.