数据结构学习记录——堆的建立(最大堆的建立、思路图解、代码实现、代码解释)

目录

最大堆的建立

方法1 

方法2

思路图解

代码实现 

代码解释

PercDown

BuildHeap


最大堆的建立

建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中。

方法1 

通过插入操作,将N个元素一个一个地插入到一个初始为空的堆中去。堆插入的时间复杂度为{log_{2}}^{N},插人N个元素,那么最终建立堆的时间复杂度就为O(N{log_{2}}^{N})

方法2

线性时间复杂度下建立最大堆。

(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性

(2)调整各结点位置,以满足最大堆的有序性

我们当然要选择更高效率的方法,下面就来看看方法2具体是怎样实现的:

思路图解

假设现在有12个元素,已经按输入顺序存入,满足了完全二叉树的结构特性:

对其进行调整,调整的思路和堆的删除相似,但是要从后往前开始,在左右两个堆中进行调整;我们从倒数第一个有儿子的结点开始调整。因为倒数第一个结点一定是只有左儿子或者有一个左儿子和一个右儿子,即左右两边一定是堆的结构

调整完倒数第一个有儿子 的结点之后,往上再进行调整。 

重复操作

继续往上,到上一层了。  

循环

 

最终调整完成 :

代码实现 

/* 下滤操作:将H中以H->Data[p]为根的子堆调整为最大堆 */
void PercDown( MaxHeap H, int p )
{ 
    int Parent, Child;
    ElementType X;

    X = H->Data[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child )
    {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
        {
            Child++;  /* Child指向左右子结点的较大者 */
        }
        if( X >= H->Data[Child] ) 
        {
            break; /* 找到了合适位置 */
        }
        else
        {
            /* 下滤X */
            H->Data[Parent] = H->Data[Child];
        } 
    }
    H->Data[Parent] = X;
}

/* 将一个无序的数组调整为最大堆  */
void BuildHeap( MaxHeap H )
{ 
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */

    int i;

    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- )
        PercDown( H, i );
}

代码解释

PercDown

PercDown函数用于将以某个结点为根的子树调整为最大堆。

其中,参数p,表示要下滤的结点在H中的下标。

Parent表示当前结点的父结点,Child表示当前结点的子结点,X表示当前结点的值。

首先,将要下滤的结点的值X取出来,保存在一个临时变量中。 

 然后,从要下滤的结点开始,沿着其左右子结点中较大的一个不断下滤,

直到找到X的合适位置或者到达叶结点为止。(Child的判断那里与堆的删除那里是一致的)

具体来说,每次将要下滤的结点与其左右子结点中较大的一个进行比较,

如果X比较大,则说明X已经找到了合适的位置,可以退出循环;(最大堆根结点比左右子结点都要大)

否则,将较大的子节点上移一层,继续下滤。

 

最后,将X放到找到的合适位置上,完成下滤操作。

BuildHeap

BuildHeap函数用于将一个无序的数组调整为最大堆。

 

从倒数第一个没有儿子的结点开始,即从最后一个非叶子节点开始,依次进行下滤操作,将其子树调整为最大堆。

因为最后一个非叶子节点的数组下标为n/2,其中n为堆中元素的个数,所以从i=n/2开始循环。

循环执行,直到整个数组都被调整为最大堆。

综上,BuildHeap函数的时间复杂度为O(N),其中N为堆中元素的个数。因为每个结点最多只需要下滤一次,而下滤的时间复杂度为O({log_{2}}^{N}),所以建立一个最大堆,总的时间复杂度为O(N{log_{2}}^{N})。与开头所说相符合,效率较方法1要更高。


end


学习自:MOOC数据结构——陈越、何钦铭

在C++中,可以使用数组来实现大顶,这是一种特殊的完全二叉树结构,满足性质:即每个节点都大于或等于其子节点。实现大顶有两步基本操作:建和调整。 **建(Heapify or BuildHeap)**: - 初始化:将未排序的元素依次插入到中,并从最后一个非叶子节点开始向上调整,保证每一步都是满足性质的。 - 使用`build_heap()`函数,从最后一个非叶子节点(即最后一个元素的父节点索引减一)开始,向下遍历,每次都将当前节点与它的孩子中较大的交换,直到化完成。 ```cpp void buildMaxHeap(int arr[], int n) { for (int i = n / 2 - 1; i >= 0; i--) heapify(arr, n, i); } void heapify(int arr[], int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < n && arr[left] > arr[largest]) largest = left; if (right < n && arr[right] > arr[largest]) largest = right; if (largest != i) { swap(arr[i], arr[largest]); heapify(arr, n, largest); } } ``` **Siftdown算法(Sort Down)**: - 当向中添加新元素后,如果这个元素违反了的特性(即父节点小于子节点),则需要将其与父节点交换,然后继续与其上一个父节点比较,直到满足性质为止。这种过程被称为“下沉”(sifting down)。 ```cpp void siftDown(int arr[], int n, int i) { while (i < n / 2) { int largest = i; int left = 2 * i + 1; // 更新最大值位置 if (left < n && arr[left] > arr[largest]) largest = left; // 再次检查右侧是否更大 if (2 * i + 2 < n && arr[2 * i + 2] > arr[largest]) largest = 2 * i + 2; // 如果父节点不是最大,就交换它们并继续下沉 if (largest != i) { swap(arr[i], arr[largest]); i = largest; // 保持在新的最大节点处向下搜索 } else break; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值