我有这个方法,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)?这是正确的,如果不是它是什么以及如何解决。
另外为了提高效率,将字符存储在临时字符串中是否合适?
答案 0 :(得分:14)
这只是O(N)。
说O(N + 3)并不真正有意义 - 忽略常数因子。
当发现不匹配时,您可以通过爆发来加快速度:
bP = false;
break;
(并不是说这会改变它的O(N),但在大多数情况下它会加速它。)
事实并非如此:
此代码检查字符串的字符以查看它是否与之前的字符相同
它检查开头的字符是否与最后的字符匹配,换句话说,它是一个天真的palindrome检查器。
另一个加速将循环到s.length()/2
- 否则你要对一个回文的字符串进行两次比较。
答案 1 :(得分:9)
给定代码似乎是通过检查字符“N”是否与字符“length-N”相同来检查字符串是否为回文结构。如前所述,您可以通过
提高效率对于这些建议,我会添加
鉴于这一切:
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;
}
答案 2 :(得分:6)
这很可能是Java中最有效的实现:
public static boolean isP(String s) {
char[] chars = s.toCharArray();
for (int i = 0; i < (chars.length / 2); i++) {
if (chars[i] != chars[(chars.length - i - 1)])
return false;
}
return true;
}
优点:
与所有其他提议的解决方案一样,它仍然是O(N)。
我刚刚测量了所提出的解决方案的时间非常大(以纳秒为单位):
Aran: 32244042
Andreas: 60787894
Paul Tomblin: 18387532
首先,上面的测量是使用客户端VM 完成的。因此,计算i < (chars.length / 2)
未作为常量内联。 使用-server Vm参数给出了更好的结果:
Aran: 18756295
Andreas: 15048560
Paul Tomblin: 17187100
为了推动它有点极端:
首先警告:不要在任何您打算使用/运输的程序中使用此代码。
它包含隐藏的错误,并且不遵守Java API并且没有错误处理,正如评论中所指出的那样。它纯粹用于证明通过肮脏技巧可以获得的理论性能改进。
从字符串复制数组时会有一些开销,因为字符串类在内部会产生防御性副本。
如果我们直接从字符串中获取原始char [],我们可以挤出一些性能,代价是对字符串使用反射和unsave操作。这使我们再获得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;
}
时间:
Aran: 18756295
Andreas1: 15048560
Andreas2: 12094554
Paul Tomblin: 17187100
答案 3 :(得分:3)
是O(N)。您正在进行N次比较,其中N = s.length()。每次比较都需要O(1)时间,因为它是单个字符比较。
+3并不重要,因为渐近符号只关心最高阶项。
答案 4 :(得分:3)
这是另一个有两个相反指针的解决方案:
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;
}
复杂性再次 O ( n )。
进一步了解细节:假设每个操作都需要1个单位。比较,分配,算术运算,函数调用每个花费1个单位。因此,在最坏情况下调用isPalindrome
费用(s
是一个回文)需要例如:
4 + n/2 · (3 + 4) + 1
= 5 + n/2 · 7
= 5 + 7/2 · n
由于省略了常数因子(此处为5 + 7/2),我们最终得到5 + 7/2· n ∈ O ( ñ)。
答案 5 :(得分:2)
首先,对于这个问题,不存在单线程解决方案,其中“最坏情况”复杂度优于任意输入字符串的O(N)
。简单地说,在最坏的情况下,任何算法都必须查看字符串中的每个字符。从理论上讲,您可以使用硬件并行性来改进O(N)
;即,具有在字符串的不同部分上工作的无限可缩放数量的处理器。在实践中,根本不可能实现任何加速。将输入字符串(或相关部分)发送到每个处理器的成本将是“O(N)”,除非有一些我不知道的解决方案。
其次,您可以看到O(N)
行为不是最终答案。您还需要将乘法常数视为N - >无穷大,以及较小的N值的较小术语。
第三,@ dfa说微观优化不是你的事。他走在正确的轨道上,但我认为它不是那么明确。 IMO,这是浪费时间微优化,除非1)你的应用程序真的需要尽可能快地运行,2)你的应用程序分析表明这个特定的计算真的< / em>是一个重要的瓶颈。
最后,微程序优化使一个特定硬件平台/ JIT编译器的程序更快,可能会使另一个更慢。复杂的微优化代码对于JIT编译器来说更难以生成有效的代码。如果你使用反射来访问(比如说)String类的内部,你的代码可能在某些平台上实际上失败了。 (Java类库规范中没有任何内容表明String有一个名为“value”的私有字段,它是char[]
!!!)
答案 6 :(得分:1)
首先,该方法应该做什么?
我的猜测:确定字符串是否为palindrome。
很明显,你不能在O(N)下得到它:
O(N+3) == O(N)
另一个问题是,它是最有效的解决方案吗?也许不是。
改进空间:
将它切成两半。你检查所有角色两次(如Michiel Buddingh建议的那样)。
预先获取字符数组。这使您在chatAt()
内发生了一些索引检查。
所有其他操作charAt()
和length()
都是标准字符串实现的O(1)。
答案 7 :(得分:0)
第一个改进:一旦找到不匹配就可以中断,对吧?
答案 8 :(得分:0)
你可以通过停止在(i ==(s.length()/ 2)+1)将函数的复杂性减半。它与Big O术语无关,但它仍然是一个相当不错的收获。
答案 9 :(得分:0)
Big O复杂性总是没有costants(因为对于N&gt; oo,它们并不重要)。因此,您的时间复杂度只是O(n)
。
另外为了提高效率,将字符存储在临时字符串中是否合适?
这不是你的工作。 JIT编译器将为您处理这种微优化。
答案 10 :(得分:0)
假设循环中的操作可以在恒定时间内执行,复杂度为O(N)。 由于“Big-O”符号表示增长,而不是纯粹的速度,因此可以忽略常数因素。这使我们得出O(N + 3)等于O(N)的结论。
答案 11 :(得分:0)
或者你可以简单地做
def isPalindrome?(x)
return x == x.reverse
end
这仍然是O(n)时间复杂度。