使用堆有助于提高字符串重新排列

时间:2017-01-05 06:30:29

标签: python algorithm performance python-2.7 heap

我正在解决下面的问题并发布了我的代码。我的问题是,我目前的实现是在Python中使用排序 - sorted(sorted_freq, reverse=True)。我提到了一些使用最大堆(http://www.geeksforgeeks.org/rearrange-a-string-so-that-all-same-characters-become-at-least-d-distance-away/)的其他实现。我认为它们具有相同的时间复杂度O(n*log n)(如果我计算错误,请随意纠正)。所以我想知道除了排序之外使用堆的性能是否有任何好处?

我的代码是用Python 2.7编写的。

问题:

给定一个字符串和一个正整数 d 。某些字符可能在给定的字符串中重复。重新排列给定字符串的字符,使相同的字符彼此远离 d 。请注意,可能有许多可能的重新排列,输出应该是可能的重新排列之一。如果没有这样的安排,也应该报告。 预期的时间复杂度为 O(n),其中 n 是输入字符串的长度。

示例:

Input:  "abb", d = 2
Output: "bab"

Input:  "aacbbc", d = 3
Output: "abcabc"

Input: "geeksforgeeks", d = 3
Output: egkegkesfesor

Input:  "aaa",  d = 2
Output: Cannot be rearranged

源代码:

from collections import defaultdict
def rearrange(source, distance):
    freq = defaultdict(int)
    for c in source:
        freq[c] += 1
    sorted_freq = []
    for k,v in freq.items():
        sorted_freq.append((v,k))
    sorted_freq = sorted(sorted_freq, reverse=True)
    result = [0] * len(source)
    for i, (v,k) in enumerate(sorted_freq):
        index = i
        while index < len(result) and result[index] != 0:
            index += 1
        if index == len(result):
            return None
        count = v
        while count > 0:
            result[index] = k
            index += distance
            count -= 1
            if index >= len(source) and count > 0:
                return None
    return result

if __name__ == "__main__":
    print rearrange('abb', 2)
    print rearrange('aacbbc', 3)
    print rearrange('geeksforgeeks', 3)
    print rearrange('aaa', 2)

1 个答案:

答案 0 :(得分:3)

链接上显示的最佳算法的时间复杂度为 O(n + m log m),其中 m 是输入字符串中唯一字符的数量。如上所述,因为 m 总是少于字母表中的字母总数(这是一个固定的常数),如果 m 小于 n < / em>时间复杂度可以被认为是 O(n)。当 O(m log m)排序算法用于对频率而不是堆进行排序时,时间复杂度没有区别。

请注意,由于您在每个循环中使用index初始化i,因此您的实现具有时间复杂度 O(nm)。以下是使用Counter代替defaultdict的替代实现,其中修复了问题并简要比较了退化情况下的性能:

from collections import Counter

def rearrange2(s, dist):
    start = 0
    result = [None] * len(s)
    for char, count in Counter(s).most_common():
        while result[start]:
            start += 1
        end = start + dist * (count - 1) + 1
        if end > len(s):
            return None
        for i in xrange(start, end, dist):
            result[i] = char

    return ''.join(result)


def rearrange3(s, dist):
    start = 0
    result = [None] * len(s)
    for char, count in sorted(Counter(s).items(), key=lambda x: x[1], reverse=True):
        while result[start]:
            start += 1
        end = start + dist * (count - 1) + 1
        if end > len(s):
            return None
        for i in xrange(start, end, dist):
            result[i] = char

    return ''.join(result)

if __name__ == '__main__':
    import timeit
    print timeit.timeit("rearrange(src,2)", setup="from __main__ import rearrange; src='a'*10000 + 'b'*10000 + 'cdefghijk'", number=100)
    print timeit.timeit("rearrange2(src,2)", setup="from __main__ import rearrange2; src='a'*10000 + 'b'*10000 + 'cdefghijk'", number=100)
    print timeit.timeit("rearrange3(src,2)", setup="from __main__ import rearrange3; src='a'*10000 + 'b'*10000 + 'cdefghijk'", number=100)

输出:

3.23630073078
0.756645293244
0.753287190129

更新: most_common使用heapq.nlargest under the hood等于heapsort,以防n为给定可迭代的长度。从上面的结果可以看出,没有真正的区别。结果当然取决于数据的大小和唯一字符的数量。