Java isPalindrome()的时间复杂度O()
我有这个方法,isPalindrome(),我试图找到它的时间复杂性,并更有效地重写代码Java isPalindrome()的时间复杂度O(),java,performance,big-o,time-complexity,Java,Performance,Big O,Time Complexity,我有这个方法,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;
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;
}
boolean isPalindrome(字符串s){
布尔bP=true;
对于(inti=0;i第一个改进:一旦发现不匹配项,就可以中断,对吗?它只是O(N)
说O(N+3)并没有真正的意义——常数因子被忽略了
当发现不匹配时,您可以通过中断来加快速度:
bP = false;
break;
(这并没有改变O(N)的事实,但在大多数情况下,它会加快速度。)
事实并非如此:
这段代码检查字符串的字符,看它是否与前面的字符相同
它检查开始处的字符是否与结束处的字符匹配,因此换句话说,它是一个幼稚的检查器
另一种加速方法是循环直到s.length()/2
——否则对回文字符串进行两次比较。是O(N)。进行N次比较,其中N=s.length()。每次比较都需要O(1)个时间,因为这是一个单字符比较
+3不重要,因为渐近表示法只关心最高阶项。你可以在(i==(s.length()/2)+1处停止,将函数的复杂度减半。它与大O项无关,但它仍然是一个相当不错的增益。复杂度总是没有代价(因为对于N->oo,它们并不重要)因此,您的时间复杂度只是O(n)
另外,为了提高效率,将字符存储在临时字符串中是否好
这不是你的工作。JIT编译器将为你处理这个微优化。那么首先,这个方法应该做什么
我的猜测是:确定一个字符串是否是一个字符串
很明显,在O(N)下,您将无法将其记录下来:
另一个问题是,这是最有效的解决方案吗?也许不是
有待改进的地方:
把它切成两半。你检查所有字符两次(就像Michiel Buddingh建议的那样)
事先获取字符数组。这样就省去了在chatAt()
中进行的一些索引检查
所有其他操作,charAt()
和length()
,在标准字符串实现中都是O(1)。假设循环中的操作可以在恒定时间内执行,复杂性为O(N)。
由于“Big-O”表示法测量的是增长,而不是纯粹的速度,常数因子可以忽略不计。这就给我们留下了O(N+3)等于O(N)的结论。给定的代码似乎是通过检查字符“N”是否与字符“length-N”相同来检查字符串是否是回文.如前所述,您可以通过以下方式提高效率
- 只检查上半场
- 一旦发现不匹配项,立即爆发(返回false)
除了这些建议,我还要补充一点
- 不要每次通过循环都重复计算s.length(),因为它不会更改
鉴于所有这些:
boolean isP(String s) {
int l = s.length();
int l2 = l/2;
int j = l - 1;
for(int i=0; i<l2; i++) {
if(s.charAt(i) != s.charAt(j)) {
return false;
}
j--;
}
return true;
}
布尔isP(字符串s){
int l=s.长度();
int l2=1/2;
int j=l-1;
对于(int i=0;i这很可能是Java中最有效的实现:
首先,上面的测量是使用客户端VM完成的。因此计算i<(chars.length/2)
不是作为常量内联的。使用-server VM参数得到了更好的结果:
Aran: 18756295
Andreas: 15048560
Paul Tomblin: 17187100
让它变得有点极端:
首先警告一句:不要在您打算使用/发布的任何程序中使用此代码
正如评论中所指出的,它包含隐藏的bug,不遵守Java,也没有错误处理。它纯粹是为了证明通过肮脏的技巧可以获得的理论性能改进
从字符串复制数组时会有一些开销,因为string类在内部进行防御复制
如果我们直接从字符串中获取原始char[],我们可以挤出一点性能,但代价是对字符串使用反射和非保存操作。这将使我们获得另外20%的性能
public static boolean isPReflect(String s) {
char[] chars = null;
try {
final Field f = s.getClass().getDeclaredField("value");
f.setAccessible(true);
chars = (char[]) f.get(s);
}
catch (IllegalAccessException e) {
}
catch (NoSuchFieldException e) {
}
final int lenToMiddle = chars.length / 2;
for (int i = 0; i < lenToMiddle; i++) {
if (chars[i] != chars[(chars.length - i - 1)])
return false;
}
return true;
}
首先,对于任意输入字符串,“最坏情况”复杂度比O(N)
好的问题,不可能有单线程解决方案。简单地说,任何算法都必须在最坏情况下查看字符串中的每个字符。理论上,您可以改进O(N)
使用硬件并行;即,在字符串的不同部分上工作的处理器数量可以无限扩展。实际上,很难实现任何加速。将输入字符串(或相关部分)发送到每个处理器的成本将为“O(N)”,除非有我不知道的解决办法
第二,正如你可以看到的,<代码> o(n)< /代码>行为并不是最终的答案。你还需要考虑乘法常数为n>无穷大,而对于n/<
第三,@dfa说微优化不关你的事。他走的是对的,但我认为没有那么明确。在我看来,微优化是浪费时间,除非1)你的应用程序真的需要尽可能快地运行,2)您对应用程序的分析表明,这个特定的计算确实是一个重要的瓶颈
最后,对于一个特定的硬件平台/JIT编译器来说,使程序更快的微优化可能会使另一个程序更慢。对于JIT编译器来说,复杂的微优化代码更难为其生成有效的代码。如果使用反射来访问(例如)的内部结构在String类中,您的代码实际上可能在某些平台上失败。(Java类库规范中没有说明字符串有一个名为“value”的私有字段,它是一个char[]
!!!)下面是另一个解决方案
Aran: 32244042
Andreas: 60787894
Paul Tomblin: 18387532
Aran: 18756295
Andreas: 15048560
Paul Tomblin: 17187100
public static boolean isPReflect(String s) {
char[] chars = null;
try {
final Field f = s.getClass().getDeclaredField("value");
f.setAccessible(true);
chars = (char[]) f.get(s);
}
catch (IllegalAccessException e) {
}
catch (NoSuchFieldException e) {
}
final int lenToMiddle = chars.length / 2;
for (int i = 0; i < lenToMiddle; i++) {
if (chars[i] != chars[(chars.length - i - 1)])
return false;
}
return true;
}
Aran: 18756295
Andreas1: 15048560
Andreas2: 12094554
Paul Tomblin: 17187100
boolean isPalindrome(String s) {
int i = 0, j = s.length() - 1;
while (i < j && s.charAt(i) == s.charAt(j)) {
++i;
--j;
}
return i >= j;
}
4 + n/2 · (3 + 4) + 1
= 5 + n/2 · 7
= 5 + 7/2 · n
def isPalindrome?(x)
return x == x.reverse
end