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

How do I improve my Python code about heap sort?

我尝试自己为 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

说到堆排序,考虑 heapq python module. It exists for this very purpose - provide an implementation of the heap queue algorithm. It isn't designed not very conveniently, but there are handy wrappers - 你可以 google 自己做。

说到查找重复项,任何 n log(n) 排序算法都不够有效。看看 python set 内置函数!

你的代码做的太多了。您正在用您删除的每个项目重建整个堆。那么 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]处的item是堆中剩余的最小item,所以输出。然后,就没有必要重建整个堆。您所要做的就是取出堆中的最后一项,将其放在顶部,然后将其向下筛选到位。堆的其余部分仍然有效。

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

想法是创建一个散列 table,然后遍历数组,检查项目是否在散列 table 中。如果没有,请添加它。如果它已经在 table 中,则它是重复的。正如 Harold 所指出的,Python 有一个 set 类型可以让这种事情变得容易。