在这篇文章中,我们解释了堆排序的时间和空间复杂性,并详细分析了不同情况,如最差情况、最佳情况和平均情况。
目录。
- 堆排序的概述
- 堆数据结构的时间复杂度
- 堆排序的最坏情况下的时间复杂度
- 堆排序的最佳情况下的时间复杂度
- 堆排序的平均情况下的时间复杂度
- 堆排序的空间复杂度
- 总结
让我们开始学习堆排序的时间和空间复杂度。
堆排序的概述
堆排序算法主要由两部分组成--将列表转换为堆,并将堆中的最大元素添加到列表的末尾,同时保持堆的结构。为了便于实现,我们使用一个最大堆结构,其中最大值总是存在于根部。在将列表转换为堆后,我们从其中取出最大元素并将其添加到列表的末尾。我们重复这个过程,直到堆中的元素数量变为零。这表明我们已经按照正确的顺序排列了列表中的所有项目。因此,总结起来,在实现堆排序时,我们主要关注两个方面 --
- 建立最大堆
- 从堆中获取最大值(根节点的值),将其添加到列表的末尾,并更新max-heap。重复进行,直到max-heap包含零项。
实现这一算法的伪代码如下
lst = [a1, a2, ..., aN]
# heap sort the list
heapsort(lst):
set heap_size equal to list length
create_heap(lst)
for i from 0 to heap_size-1, decrement by 1:
lst[0], lst[i] = lst[i], lst[0]
heap_size -= 1
max_heapify(lst, heap_size, 0)
# function to style a heap as per max-heap properties
max_heapify(lst, heap_size, i):
get index of left child node of i
get index of right child node of i
create a variable to track index of largest list item
if left < heap_size and lst[left] > lst[largest]:
largest = left
if right < heap_size and lst[right] > lst[largest]:
largest = right
if largest != i:
swap(i, largest)
max_heapify(lst, heap_size, largest)
# function that creates a heap
# uses the max_heapify function to create a max-heap
create_heap(lst):
get length of list to get heap_size
for i from heap_size//2 to -1, decrement by 1:
max_heapify (lst, heap_size, i)
# print the sorted list at the end
heapsort(lst)
print(lst)
这给出了Heapsort算法背后的基本想法。
堆数据结构的时间复杂性
在该算法中,我们使用了max_heapify
和create_heap
,这是该算法的第一部分。当使用create_heap
,我们需要了解如下所示的最大堆结构是如何工作的。
因为我们使用的是二叉树,所以堆的底部包含最大数量的节点。当我们往上走的时候,节点的数量减少了一半。考虑到有'n'个节点,那么从最底层开始的节点数为
- n/2
- n/4 (在下一级)
- n/8
- 以此类推
插入一个新节点的复杂性
因此,当我们在制作堆的时候插入一个新的值,我们需要采取的最大步骤数得出是O(log(n))。由于我们使用二叉树,我们知道这样一个结构的最大高度总是O(log(n))。当我们在堆中插入一个新的值时,我们将用一个比它大的值来交换它,以保持最大堆的特性。这种交换的数量将是O(log(n))。因此,在建立最大堆时插入一个新值将是O(log(n))。
从堆中删除最大值节点的复杂性
同样地,当我们从堆中移除最大值节点,添加到列表的末尾时,所需的最大步骤数也是O(log(n))。由于我们交换最大值节点直到它降到最底层,我们需要的最大步骤数与插入一个新节点时相同,即O(log(n))。
因此,max_heapify
函数的总时间复杂性变成了O(log(n))。
创建一个堆的复杂性
使用create_heap
函数将一个列表转换为一个堆的时间复杂度不是O(log(n))。这是因为当我们创建一个堆时,不是所有的节点都会向下移动O(log(n))次。只有根节点才会这样做。最底层的节点(由n/2给出)根本不会向下移动。倒数第二层的节点(n/4)会向下移动1次,因为下面只剩下一层可以向下移动。最后第三层的节点将向下移动2次,以此类推。因此,如果我们将所有节点的移动次数相乘,在数学上,它将变成一个几何数列,解释如下
(n/2 * 0) + (n/4 * 1) + (n/8 * 2) + (n/16 * 3) + ...h
这里h代表最大堆结构的高度。
这个系列的总和,经过计算,最后得到的数值是n/2。因此,create_heap
的时间复杂度变成了O(n)。
总的时间复杂度
在heapsort
的最后一个函数中,我们利用create_heap
,它运行一次来创建一个堆,运行时间为O(n)。然后使用for-loop,我们为每个节点调用max_heapify
,每当我们在堆中删除或插入一个节点时,都要保持最大堆的特性。由于有'n'个节点,因此,算法的总运行时间变成了O(n(log(n)),我们对每个节点使用max-heapify
函数。
从数学上看,我们看到
- 一个节点的第一次删除需要log(n)时间
- 第二次删除需要log(n-1)时间
- 第三次删除需要log(n-2)时间
- 以此类推,直到最后一个节点,这将需要log(1)时间。
因此,将所有的条款相加,我们得到---
log(n) + log(n-1) + log(n-2) + ....log(1)
由于log(x) + log(y) = log(x * y) ,我们得到
=log(n∗(n-1)∗(n-2)∗...∗2∗1)
=log(n!
进一步简化后(使用斯特林的近似值),log(n!)变成
了
=n∗log(n)-n+O(log(n)
)
考虑到最高排序项,总运行时间变成了O(n(log(n))。
堆排序的最坏情况下的时间复杂度
堆排序的最坏情况可能发生在列表中的所有元素都是不同的。因此,我们需要在每次删除一个元素时调用max-heapify
。在这种情况下,考虑到有'n'个节点------。
- 删除每个元素的交换次数将是log(n),因为这是堆的最大高度。
- 考虑到我们对每个节点都这么做,总的移动次数将是n * (log(n))。
因此,最坏情况下的运行时间将是O(n(log(n))。
堆排序的最佳情况下的时间复杂度
堆排序的最佳情况是当列表中所有要排序的元素都是相同的。在这种情况下,对于'n'数量的节点--
- 从堆中移除每个节点只需要一个恒定的运行时间,即O(1)。由于所有的项目都是相同的,所以不需要将任何节点降低或将最大值节点提高。
- 由于我们对每个节点都这样做,所以移动的总数将是n * O(1)。
因此,在最好的情况下,运行时间将是O(n)。
堆排序的平均案例时间复杂度
就总的复杂性而言,我们已经知道我们可以在O(n)时间内创建一个堆,并在O(log(n))时间内进行节点的插入/移除。 就平均时间而言,我们需要考虑到所有可能的输入,无论是否有不同的元素。如果节点的总数是'n',在这种情况下,max-heapify
函数需要执行。
- 在第一次迭代中进行log(n)/2的比较(因为我们一次只比较两个值来建立最大堆)
- 在第二次迭代中进行log(n-1)/2的比较
- 第三次迭代中的log(n-2)/2
- 以此类推
因此,从数学上讲,总和将变成--
(log(n))/2 + (log(n-1))/2 + (log(n-2))/2 + (log(n-3))/2 + ...
经近似计算,最终结果为
=1/2(log(n!))
=1/2(n∗log(n)-n+O(log(n)
)
考虑到最高排序项,那么max-heapify
的平均运行时间将是O(n(log(n))。
由于我们在最后的heapsort
函数中为所有节点调用这个函数,运行时间将是(n * O(n(log(n)))。计算平均数,除以n后,我们会得到最终的平均运行时间为O(n(log(n))
堆排序的空间复杂度
由于heapsort是一种就地设计的排序算法,空间需求是恒定的,因此是O(1)。这是因为,在任何输入--------------------------------的情况下,我们都要把所有的列表项就地排列。
- 我们使用堆结构就地排列所有的列表项
- 在从最大堆中移除最大节点后,我们将移除的项目放在同一个列表的末尾。
因此,在实现这个算法时,我们不使用任何额外的空间。这使得该算法的空间复杂度为O(1)。
结论
综上所述,heapsort具有。
- 最坏情况下的时间复杂度为O(n(log(n))[列表中的所有元素都是不同的]
- 最佳情况下的时间复杂度为O(n) [所有元素都是相同的] 。
- 平均情况下的时间复杂度为O(n(log(n))
- 空间复杂度为O(1)
通过OpenGenus的这篇文章,你一定对堆排序的时间和空间复杂度有了完整的了解。