我有n个字符串,每个字符串长度为n。我希望按升序对它们进行排序。
我能想到的最好算法是快速排序,时间复杂度为n^2 log n。(比较两个字符串需要O(n)时间)。挑战在于如何在O(n^2)的时间内完成排序。我该怎么做呢?
另外,由于您事先不知道字母表中字母的数量,因此不允许使用基数排序方法。
我有n个字符串,每个字符串长度为n。我希望按升序对它们进行排序。
我能想到的最好算法是快速排序,时间复杂度为n^2 log n。(比较两个字符串需要O(n)时间)。挑战在于如何在O(n^2)的时间内完成排序。我该怎么做呢?
另外,由于您事先不知道字母表中字母的数量,因此不允许使用基数排序方法。
假设任何字母都是a到z。
由于没有就地排序的要求,因此创建一个长度为26的链表数组:
List[] sorted= new List[26]; // here each element is a list, where you can append
对于字符串中的一个字母,它的排序位置是ascii码差值:x-'a'。例如,'c'的位置是2,将被放置在该位置。
sorted[2].add('c')
这样,仅对一个字符串进行排序只需要n。
因此,对所有字符串进行排序需要n^2。
例如,如果您有“zdcbacdca”。
z goes to sorted['z'-'a'].add('z'),
d goes to sorted['d'-'a'].add('d'),
....
排序后,可能的一个结果如下所示
0 1 2 3 ... 25 <br/>
a b c d ... z <br/>
a b c <br/>
c
对于少量字符串,常规比较排序可能比基数排序更快,因为基数排序需要的时间与存储每个字符所需的位数成正比。对于2字节Unicode编码,并做出一些(无可厚非的)假设,当log2(n) > 16时,即在排序超过约65,000个字符串时,基数排序才会更快。
我还没有看到提到的一件事是,可以通过利用已知的公共前缀来增强字符串的比较排序。
假设我们的字符串是S[0]、S[1]、...、S[n-1]。让我们考虑将最长公共前缀(LCP)表与归并排序相结合。首先,我们不再在内存中移动整个字符串,而是只操作指向固定字符串表的索引列表。
每当我们合并两个排序好的字符串索引列表X[0],...,X[k-1]和Y[0],...,Y[k-1]以生成Z[0],...,Z[2k-1]时,我们还将获得2个LCP表(LCPX[0],...,LCPX[k-1]用于X和LCPY[0],...,LCPY[k-1]用于Y),我们也需要生成LCPZ[0],...,LCPZ[2k-1]。 LCPX[i]给出了X[i]的最长前缀的长度,该前缀也是X[i-1]的前缀,LCPY和LCPZ同理。在 S[X[i]] 和 S[Y[j]] 之间的每次比较之后,像往常一样将“失败者”的字符串索引附加到 Z 中。计算相应的 LCPZ 值很容易:如果最后两个失败者都来自 X,则取 LCPX[i];如果它们都来自 Y,则取 LCPY[j];如果它们来自不同的块,则取 L 的上一个值。
实际上,我们可以做得更好。假设最后一次比较发现S[X [i]] < S[Y [j]],因此X [i]是最近追加到Z的字符串索引。 如果M(= LCPX [i + 1]> L),则我们已经知道S[X[i+1]] < S[Y[j]]甚至不需要进行任何比较!这是因为要达到当前状态,我们知道S[X[i]]和S[Y[j]]必须首先在字符位置L处有所不同,并且在S[X[i]]中该位置上的字符x小于S[Y[j]]中该位置上的字符y,因为我们得出结论S[X[i]] < S[Y[j]]--因此,如果S[X[i+1]]与S[X[i]]共享至少前L+1个字符,则它也必须包含S[X[i]]位置L上的x,因此它也必须比S[Y[j]]小进行比较。(当然情况是对称的:如果最后一次比较发现S[Y[j]] 我不知道这是否会将复杂度从O(n^2logn)提高到更好的级别,但这应该会有所帮助。