二叉堆

一、简介

完全二叉树:若假设二叉树的深度为h,除第h层外,其他各层(1~h-1)的节点数都达到最大个数(即1~h-1)层为一个满二叉树,第h层所有节点都连续集中在最左边,这就是完全二叉树。

 

堆的定义:堆(heap),这里所说的堆是数据结构中的堆,而不是内存模型中的堆。堆通常是一个可以被看做一棵树,它满足下列性质:

  • [性质一] 堆中任意节点的值总是不大于(不小于)其子节点的值;
  • [性质二] 堆总是一棵完全树

将任意节点不大于其子节点的堆叫做最小堆或小根堆,而将任意节点不小于其子节点的堆叫做最大堆或大根堆。

 

二叉堆的定义:二叉堆是完全二元树或者是近似完全二元树,它分为两种:最大堆和最小堆。

  • 最大堆:父结点的键值总是大于或等于任何一个子节点的键值;
  • 最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

 

二叉堆存储结构:二叉堆一般都通过"数组"来实现。数组实现的二叉堆,父节点和子节点的位置存在一定的关系。有时候,我们将"二叉堆的第一个元素"放在数组索引0的位置,有时候放在1的位置。当然,它们的本质一样(都是二叉堆),只是实现上稍微有一丁点区别。

1.假设"第一个元素"在数组中的索引为 0 的话,则父节点和子节点的位置关系如下:

  • 索引为i的左孩子的索引是 (2*i+1);
  •  索引为i的右孩子的索引是 (2*i+2);
  • 索引为i的父结点的索引是 floor((i-1)/2);

 

2.假设"第一个元素"在数组中的索引为 1 的话,则父节点和子节点的位置关系如下:

  • 索引为i的左孩子的索引是 (2*i);
  • 索引为i的右孩子的索引是 (2*i+1);
  • 索引为i的父结点的索引是 floor(i/2);

 

二、二叉堆操作

1.插入操作:采用上滤策略,先创建一个空穴代表插入元素,并把它放到堆尾,如果它不破坏堆的性质,则插入成功;否则,把空穴的父节点移入该空穴,这样空穴朝着根方向冒一步。继续该过程直到X能被放入空穴中为止。

时间复杂度:最坏O(logN) 平均 2.607

代码

// 插入元素
void insert( const Comparable & x )
{
    if( currentSize == array.size( ) - 1 )           // array[0]不存值 所以要-1
        array.resize( 2 * array.size( ) );

    // 上滤
    int hole = ++currentSize;
    Comparable copy = x;

    array[0] = std::move( copy );                   // 用于插入值比堆中所有元素小时结束for循环
    for( ; x < array[ hole / 2 ]; hole /= 2 )       // 因为 hole = 1时 array[hole/2] = array[0] = x 不满足 x < array[ hole / 2 ]
        array[ hole ] = std::move( array[ hole / 2 ] );
    array[ hole ] = std::move( array[0] );

}

2.删除操作:采用下滤策略

 

时间复杂度:最坏O(logN) 平均 O(logN)

代码

void deleteMin( Comparable & minItem )
{
    if( !isEmpty( ) )
        throw UnderflowException( );
    minItem = std::move( array[1] );
    array[1] = std::move( array[ currentSize-- ] );
    percolateDown( 1 );
}
// 下滤
void percolateDown( int hole )
{
    int child;
    // 树最低曾最右边的值
    Comparable tmp = std::move( array[hole] );

    for( ; hole * 2 <= currentSize; hole = child )
    {
        child = hole * 2;
        // child != currentSize 保证堆中偶数个元素也能成功
        // array[child] > array[child+1] 选取一个较小的儿子
        if( child != currentSize && array[child] > array[child+1] )
            child ++;
        if( tmp > array[ child ] )
            array[ hole ] = std::move( array[ child ] );
        else
            break;
    }
    array[ hole ] = std::move( tmp );

}

3.构建堆(buildHeap):有时二叉堆是由一些项的初始集合构造而得的,这种构造函数N项作为输入,并把它们放到一个堆中。

 

 

 

时间复杂度:最坏O(N)

代码:

// 创建一个堆序的树
explicit BinaryHeap( const std::vector<Comparable> & items )
: array( items.size( ) + 10 ), currentSize{ items.size( ) }
{
    for (size_t i = 0; i < items.size( ); i++)
        array[i+1] = items[i];
    buildHeap( );       
}
void buildHeap( )
{
    for (size_t i = currentSize / 2; i > 0; --i)
        percolateDown( i );
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值