如何改进关于堆排序的Python代码?

时间:2016-08-15 04:52:57

标签: python algorithm sorting heapsort

我尝试为Leetcode 217. Contains Duplicate编写一个堆排序程序,如下所示,而不是使用Python内置的排序方法。 Leetcode应该接受堆排序方法,但由于某些原因我不知道,虽然我的堆排序程序运行良好,但仍然得到了Leetcode的运行时拒绝。有人可以帮忙吗?

解决了,使用Floyd算法重新编辑下面的代码来初始化堆并通过了Leetcode

def heapsort(nums):

    def swap(i, j):

        nums[i], nums[j] = nums[j], nums[i]


    def sift(start, size):

        l = (start << 1) + 1 # Do not forget () for <<
        r = l + 1
        largest = start
        if l <= size - 1 and nums[start] < nums[l]:
            largest = l
        if r <= size - 1 and nums[largest] < nums[r]:
            largest = r   
        if largest != start:
            swap(start, largest)
            sift(largest, size)


    size = len(nums)

    # Initialize heap (Floyd Algorithm)
    end = (size >> 1) - 1
    while end >= 0:
        sift(end, size)
        end -= 1
    swap(0, size - 1)
    size -= 1

    # Heapify recursively
    while size > 1:
        sift(0, size)
        swap(0, size - 1)
        size -= 1

1 个答案:

答案 0 :(得分:1)

你的代码太过分了。您正在使用删除的每个项重建整个堆。那么O(n log n)算法应该是O(n ^ 2)。

基本上,您的代码正在执行此操作:

while array is not empty
    rearrange array into a heap
    extract the smallest item

重新排列堆最多需要O(n)时间。并且提取最小值需要O(log n)。所以你的算法是O(n ^ 2 + n log n)。

实际上,从下到上构建堆的方法本身就是O(n log n)。所以你的堆排序算法实际上是O((n + 1)*(n log n))。无论如何,它是一种高度次优的算法。

堆排序背后的想法是你将数组排列成一堆一次。这是O(n)操作。算法非常简单:

for i = heap.length/2 downto 1
    siftDown(i)

在其发明者之后,这被称为Floyd's algorithm

请注意,我们从数组中间开始,然后将向下移动。这个想法是最后的n / 2项是叶子节点,所以无论如何它们都不能向下筛选。从n / 2开始向后工作,我们可以在O(n)时间内堆积整个数组。

将数组排列到堆中后,我们执行以下操作:

while heap is not empty
    output the item at heap[0]
    move the item at the end of the heap to heap[0]
    reduce the count of items by 1
    siftDown(0)

heap [0]中的项是堆中剩余的最小项,因此我们输出它。然后,不需要重建整个堆。您所要做的就是取出堆中的最后一项,将其放在顶部,然后将其向下移动到位。堆的其余部分仍然有效。

进行这些更改应该可以减少运行时间,但我不知道这是否会使您的代码可以接受。还有另一种检查重复的方法。它需要额外的O(n)空间,但它比排序更快。

我们的想法是创建一个哈希表,然后遍历数组,检查项目是否在哈希表中。如果没有,请添加它。如果它已经在表中,则它是重复的。正如Harold指出的那样,Python有set类型,这使得这类事情变得容易。