Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sorting 对堆排序时间复杂性有深刻的理解_Sorting_Heap_Time Complexity_Heapsort_Amortized Analysis - Fatal编程技术网

Sorting 对堆排序时间复杂性有深刻的理解

Sorting 对堆排序时间复杂性有深刻的理解,sorting,heap,time-complexity,heapsort,amortized-analysis,Sorting,Heap,Time Complexity,Heapsort,Amortized Analysis,当我在大学学习数据结构课程时,我学到了以下公理: 在最坏的情况下,向堆中插入一个新数字需要O(logn)(取决于它作为叶子插入时在树中的高度) 使用n插入,从空堆开始构建一个n节点堆,使用摊销分析将其相加为O(n)时间 在最坏的情况下,删除最小值需要O(logn)时间(取决于新的顶部节点在与最后一个叶子交换后达到的最低点) 逐个移除所有最小值,直到堆为空,需要O(nlogn)时间复杂度 提醒:heapsort算法的步骤如下: 将所有数组值添加到堆中:使用摊销分析技巧求和到时间复杂度O(n)

当我在大学学习数据结构课程时,我学到了以下公理:

  • 在最坏的情况下,向堆中插入一个新数字需要O(logn)(取决于它作为叶子插入时在树中的高度)

  • 使用n插入,从空堆开始构建一个n节点堆,使用摊销分析将其相加为O(n)时间

  • 在最坏的情况下,删除最小值需要O(logn)时间(取决于新的顶部节点在与最后一个叶子交换后达到的最低点)

  • 逐个移除所有最小值,直到堆为空,需要O(nlogn)时间复杂度


  • 提醒:heapsort算法的步骤如下:

    • 将所有数组值添加到堆中:使用摊销分析技巧求和到时间复杂度O(n)
    • 将最小值从堆中弹出n次,并将i-th值放在数组的i-th索引中:O(nlogn)时间复杂度,因为弹出最小值时摊销分析技巧不起作用


    我的问题是:为什么在清空堆时摊销分析技巧不起作用,导致堆排序算法花费O(nlogn)时间而不是O(n)时间?

    假设您只允许通过比较两个对象来了解它们的相对排序,那么就没有办法在时间O(n)内从二进制堆中取出所有元素。如果您可以这样做,那么您可以在时间O(n)中对列表进行排序,方法是在时间O(n)中构建一个堆,然后在时间O(n)中对所有内容进行出列。然而,排序下限表示,为了正确,比较排序的平均运行时间必须为Ω(n logn)。换句话说,您不能太快地从堆中出列,否则会打破排序障碍

    还有一个问题是,为什么从二进制堆中取出n个元素需要时间O(n logn),而不是更快。这是一个有点棘手的显示,但这里的基本想法。考虑你在堆上做的队列的前半部分。查看实际已出列的值,并考虑它们在堆中的起始位置。除了最下面一行上的那些,所有其他被排出队列的东西都必须一次一个地渗透到堆的顶部才能被移除。您可以显示堆中有足够的元素来保证仅此一项就需要时间Ω(n logn),因为这些节点中大约有一半位于树的深处。这解释了摊销参数不起作用的原因——您不断地将深层节点拉到堆上,因此节点必须移动的总距离很大。与heapify操作相比,大多数节点的移动距离很小

    让我以“数学”的方式向您展示如何计算将任意数组转换为堆(让我称之为“堆构建”)然后使用heapsort对其排序的复杂性

    堆构建时分析 为了将数组转换为堆,我们必须查看每个具有子节点的节点以及该节点的“heapify”(sink)。你应该问问自己,我们做了多少比较;仔细想想,你会发现(h=树高):

    • 对于i级的每个节点,我们进行h-i比较:#comparesOneNode(i)=h-i
    • 在第一级,我们有两个^i节点:#节点(i)=2^i
    • 因此,通常T(n,i)=#节点(i)*#比较节点(i)=2^i*(h-i)是在“i”级进行“比较”所花费的时间
    让我们举个例子。假设有15个元素的数组,即树的高度为h=log2(15)=3:

    • 在级别i=3时,我们有2^3=8个节点,我们对每个节点进行3-3次比较:正确,因为在级别3时,我们只有没有子节点的节点,即叶子。T(n,3)=2^3*(3-3)=0
    • 在级别i=2时,我们有2^2=4个节点,我们对每个节点进行3-2次比较:正确,因为在级别2时,我们只有级别3可以进行比较。T(n,2)=2^2*(3-2)=4*1
    • 在级别i=1时,我们有2^1=2个节点,我们对每个节点进行3-1比较:T(n,1)=2^1*(3-1)=2*2
    • 在级别i=0时,我们有2^0=1个节点,根,我们进行3-0比较:T(n,0)=2^0*(3-0)=1*3
    好的,一般来说:

    T(n)=和(i=0到h)2^i*(h-i)

    但是如果你还记得h=log2(n),我们有

    T(n)=和(i=0到log2(n))2^i*(log2(n)-i)=~2n

    堆口时间分析 现在,这里的分析非常相似。每次我们“删除”max元素(root)时,我们都会移动到树中最后一片叶子的根,将其重分类并重复到最后。那么,我们在这里进行了多少比较

    • 在第一级,我们有两个^i节点:#节点(i)=2^i
    • 对于级别为“i”的每个节点,在最坏的情况下,heapify将始终执行与级别“i”完全相同的比较次数(我们从级别i中选取一个节点,将其移动到根节点,调用heapify,在最坏的情况下,heapify将节点恢复到级别i,执行“i”比较):#comparesOneNode(i)=i
    • 因此,通常T(n,i)=#节点(i)*#比较节点(i)=2^i*i是移除前2^i根并将临时根恢复到正确位置所花费的时间
    让我们举个例子。假设有15个元素的数组,即树的高度为h=log2(15)=3:

    • 在级别i=3时,我们有2^3=8个节点,我们需要将它们中的每一个移动到根位置,然后对它们进行重分类。每个heapify将在最坏的情况下执行“i”,因为根目录可能会下降到仍然存在的级别“i”。T(n,3)=2^3*3=8*3
    • 在级别i=2时,我们有2^2=4个节点,我们对每个节点进行2次比较:T(n,2)=2^2*2=4*2
    • 在级别i=1时,我们有2^1=2个节点