所以我遇到了一个问题。 “确定字符串是否包含所有唯一字符”
所以我写了这个解决方案,将每个字符添加到集合中,但是如果该字符已经存在,则返回false。
private static boolean allUniqueCharacters(String s) {
Set<Character> charSet = new HashSet<Character>();
for (int i = 0; i < s.length(); i++) {
char currentChar = s.charAt(i);
if (!charSet.contains(currentChar)) {
charSet.add(currentChar);
} else {
return false;
}
}
return true;
}
根据我正在阅读的书,这是“最佳解决方案”
public static boolean isUniqueChars2(String str) {
if (str.length() > 128)
return false;
boolean[] char_set = new boolean[128];
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i);
if (char_set[val]) {
return false;
}
char_set[val] = true;
}
return true;
}
我的问题是,我的实现是否比所介绍的慢?我以为是这样,但是如果Hash查找为O(1),它们的复杂度是否相同?
谢谢。
答案 0 :(得分:12)
正如Amadan在评论中所说,这两个解决方案具有相同的时间复杂度O(n),因为您有一个遍历字符串的for循环,并且在for循环中进行了恒定时间的操作。这意味着运行方法所花费的时间随着字符串的长度线性增加。
请注意,时间复杂度完全取决于更改输入大小时所需的时间。这与大小相同的数据有多快无关。
对于相同的字符串,“最优”解决方案应该更快,因为集合在数组上会有一些开销。处理数组比处理集合要快。但是,要使“最佳”解决方案真正起作用,您将需要一个长度为2 ^ 16的数组。那就是有多少个不同的char
值。您还需要删除长度大于128的字符串的支票。
这是在时间和空间之间进行权衡的众多示例之一。如果您希望它运行得更快,则需要更多空间。如果要节省空间,则必须变慢。
答案 1 :(得分:3)
这两种算法的O(N)复杂度时间。不同之处在于它们的空间复杂度。
这本书的解决方案将始终需要存储128个字符-O(1)
,而您解决方案的空间要求将根据输入-O(N)
线性变化。
这本书的空间要求基于假定的128个字符集。但是,考虑到可能需要不同的字符集,这可能会很成问题(并且无法扩展)。
答案 2 :(得分:2)
从理论上讲,哈希表是可以接受的,但这是浪费。
哈希图是基于数组构建的(因此它肯定比数组昂贵),并且冲突解决方案需要额外的空间(至少是元素数量的两倍)。此外,任何访问都需要计算哈希值,并可能需要解决冲突。
与直线阵列相比,这在空间和时间方面增加了很多开销。
还要注意,哈希表具有O(1)行为是一种民间传说。最坏的情况是差得多,对于大小为N的表,访问最多需要O(N)时间。
最后,该算法的时间复杂度为O(1),因为当N> 128时,您得出的结论是错误的。
答案 3 :(得分:1)
您的算法也是O(1)
。您可以考虑像how my algorithm will react to the change in amount of elements processed
这样的复杂性。因此O(n)
和O(2n)
实际上相等。
人们谈论O表示增长率here
答案 4 :(得分:1)
您的解决方案的确可能比本书的解决方案要慢。首先,哈希查找理想地具有恒定的时间查找。但是,如果存在多个哈希冲突,则不会检索对象。其次,即使是恒定时间查找,与按索引查找数组中的元素相比,执行哈希码功能通常也涉及大量开销。这就是为什么您可能要进行数组查找的原因。但是,如果您开始处理非ASCII Unicode字符,则由于大量的空间开销,您可能不希望使用数组方法。
答案 5 :(得分:0)
实现的瓶颈在于,集合的查找(和插入)复杂度*为java -jar myJarName.jar
,而数组的查找复杂度为O(log k)
。
听起来您的算法肯定要差得多。但实际上并非如此,因为O(1)
受k
的限制(否则,参考实现将是错误的,并且会产生越界错误),并且可以将其视为常量。这使得集合查找128
的常量也比数组查找大。
O(1)
假定一个合理的实现为树或哈希图。哈希图的时间复杂度通常不是恒定,因为填充它需要*
调整大小操作,以避免冲突的增加,这会导致线性查找时间,请参见here和here获取有关堆栈溢出的答案。
This article甚至解释说Java 8本身在其查找时间退化为{{1之前,就将哈希图转换为二叉树(用于会话,使用log(n)
进行查找)。 }},因为碰撞太多。