繁体   English   中英

Arrays.sort() 与使用 map 排序

[英]Arrays.sort() vs sorting using map

我有一个要求,我必须遍历一个包含字符串列表的数组:

String[] arr = {"abc","cda","cka","snd"}

并匹配字符串"bca" ,忽略字符的顺序,这将返回true因为它存在于数组 ( "abc" ) 中。

为了解决这个问题,我有两种方法:

  1. 使用Arrays.sort()对两个字符串进行排序,然后使用 Arrays.equals 对它们进行比较。
  2. 创建 2 个 hashmaps 并添加每个字母在字符串中的频率,然后最后使用 equals 方法比较两个 map 的 char。

我读到使用Arrays.sort()方法的复杂性更高。 因此,考虑使用第二种方法,但是当我同时运行两种代码时,第一种方法执行程序所花费的时间非常少。

有什么建议为什么会这样吗?

时间复杂度只告诉您,该方法将如何随着(显着)更大的输入进行扩展。 它不会告诉您哪种方法更快。

完全有可能解决方案对于小输入大小(字符串长度和/或数组长度)更快,但由于其时间复杂度,对于更大的大小扩展性很差。 但是,当输入大小的自然限制阻止它时,您甚至可能永远不会遇到具有更好时间复杂度的算法变得更快的地步。

您没有显示方法的代码,但您的第一种方法可能会在字符串上调用类似toCharArray()的方法,然后Arrays.sort(char[]) 这意味着排序对原始数据进行操作。

相比之下,当您的第二种方法使用HashMap<Character,Integer>记录频率时,对于字符和计数,它会受到装箱开销的影响,并且还会使用需要处理的大得多的数据结构。

因此,hash 方法对于小字符串和 arrays 速度较慢也就不足为奇了,因为它具有明显更大的固定开销以及依赖于大小的 ( O(n) ) 开销。

因此,第一种方法必须显着承受O(n log n)时间复杂度才能改变这个结果。 但这不会发生。 那个时间复杂度是一般排序的最坏情况 如此答案中所解释的那样Arrays.sort文档中指定的算法不应被视为理所当然。 当你调用Arrays.sort(char[])并且数组大小超过某个阈值时,实现将转向时间复杂度为 O(n) 的计数排序(但暂时使用更多 memory)。

因此,即使使用大字符串,您也不会遇到更糟糕的时间复杂度。 事实上,计数排序与频率 map 有相似之处,但通常效率更高,因为它避免了装箱开销,使用int[]数组而不是HashMap<Character,Integer>

方法 1:将是 O(NlogN)

方法 2:将是 O(N*M),其中 M 是数组中每个字符串的长度。

您应该在 O(N) 中线性搜索:

for (String str : arr) {
    if (str.equals(target)) return true;
}
return false;

让我们分解问题:

您需要一个function 来按字符( bccabc -> abbccc ) 对字符串进行排序,以便能够将给定字符串与现有字符串进行比较。

Function<String, String> sortChars = s -> s.chars()
        .sorted()
        .mapToObj(i -> (char) i)
        .map(String::valueOf)
        .collect(Collectors.joining());

您可以预先计算一组唯一标记(数组中的值,已排序的字符),而不是在任何时候对给定字符串的字符进行排序:

Set<String> tokens = Arrays.stream(arr)
        .map(sortChars)
        .collect(Collectors.toSet());

这将导致值"abc","acd","ack","dns"

之后你可以创建一个function 来检查给定的字符串,当按字符排序时,是否匹配任何给定的标记

Predicate<String> match = s -> tokens.contains(sortChars.apply(s));

现在您可以轻松地检查任何给定的字符串,如下所示:

boolean matches = match.test("bca");

匹配只需要对给定的输入进行排序,然后做一个 hash set lookup 来检查是否匹配,所以非常高效

如果您不熟悉函数式编程,您当然可以将 Function 和 Predicate 编写为方法( String sortChars(String s)boolean matches(String s)

更多其他答案的附录。 当然,您的两个选项具有不同的性能特征。 但是:明白性能不一定是做出决定的唯一因素!

意思是:如果您谈论的是在大型数据集上每分钟运行数百或数千次的搜索:那么可以肯定的是,您应该投入大量时间来想出一个能为您提供最佳性能的解决方案。 最有可能的是,这包括在处理真实数据时使用实际测量值进行各种实验。 时间复杂度是一种理论构造,在现实世界中,还有 CPU 缓存大小、线程问题、IO 瓶颈等因素会对数产生重大影响。

但是:当您的代码每分钟只执行一次工作时,即使是在几十或几百 MB 的数据上……那么可能值得关注性能。

换句话说:“排序”解决方案听起来很简单。 它易于理解、易于实现且不易出错(有一些不错的测试用例)。 如果该解决方案“足够好”地完成了工作,那么请考虑使用它:简单的解决方案。

性能是一个奢侈的问题。 你只有在有理由的情况下才解决它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM