数据结构 之 二叉堆


一、二叉堆简介:  
二叉堆故名思议是一种特殊的堆,二叉堆具有堆的性质(父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值),二叉堆又具有二叉树的性质(二叉堆是完全二叉树或者是近似完全二叉树)。当父节点的键值大于或等于(小于或等于)它的每一个子节点的键值时我们称它为最大堆(最小堆),或者是大根堆(小根堆)。
      二叉堆多数是以数组作为它们底层元素的存储。
假如根节点在数组中的索引是1,那么根结点的左右子节点在数组中的位置是 2,3;存储在第n个位置的父节点,它的左右子节点在数组中的存储位置为2n与2n+1。
假如根节点在数组中的索引是0,那么根结点的左右子节点在数组中的位置是 1,2。存储在第n个位置的父节点,它的左右子节点在数组中的存储位置为2n+1与2n+2。
例如:

  在这个图中,数组中的第一个元素(下标为0)就没有使用。
二、基本算法:
在二叉堆中,有两个基本的辅助算法:Sift-up(元素上移调整)  和  Sift-down(元素下移调整)。
下面分别给出其伪代码:




三、大根堆
下面直接贴出大根堆的实现代码:(在这个实现中, 二叉堆中根结点在数组中的位置是从下标0开始的
//最大堆的下滑调整
void siftdown(int *heap,int start,int m)  //m表示堆元素个数
{                                         //注意数组下标是从0开始的
    int i=start,j=2*i+1;
    int temp=heap[i];
    while(j<=m)
    {
        if(j<m&&heap[j]<heap[j+1]) j++; //如果右结点大于左节点
        if(temp>=heap[j]) break;
        else
        {
            heap[i]=heap[j];
            i=j;
            j=2*j+1;
        }
    }
    heap[i]=temp;
}

//最大堆的上滑调整
void siftup(int *heap,int start)
{
    int j=start,i=(j-1)/2;
    int temp=heap[j];   //调整值
    while(j>0)
    {
        if(heap[i]>=temp) break;   //调整值小于等于其根植,结束
        else
        {
            heap[j]=heap[i];
            j=i;
            i=(i-1)/2;
        }
    }
    heap[j]=temp;
}

//向最大堆插入一个元素x
bool insert(int *heap,int &n,int x)   //将x插入到最大堆中
{                                     //n表示堆元素个数  这里的n加一之后返回
    heap[n]=x;
    siftup(heap,n);   //从插入位置开始向上调整
    n++;
    return true;
}

//删除堆顶元素
bool remove(int *heap,int &n,int &x)   //删除堆顶(最大元素)x返回堆顶元素
{                                      //n表示堆元素个数  这里的n减一之后返回
    x=heap[0];
    heap[0]=heap[n-1]; //将堆中的最后一个元素转移到堆顶
    n--;
    siftdown(heap,0,n);  //从堆根位置开始从上向下调整
    return true;
}

//创建一个最大堆
void Creat_maxheap(int *arr,int n)  //建立最大堆
                                   //n表示堆元素个数
{
    int currentsize=n;  //当前堆大小
    int currentpos=(currentsize-2)/2; //开始调整位置
    while(currentpos>=0)
    {
        siftdown(arr,currentpos,currentsize-1);
        currentpos--;
    }
}

//堆排序,利用大根堆从小到大排序数组
void HeapSort(int *heap,int n)
{
    for(int i=(n-2)/2;i>=0;i--)   //将数组先转换成为堆(即建堆),这里是大根堆
        siftdown(heap,i,n-1);
    for(int i=n-1;i>=1;i--)      //排序
    {
        swap(heap[0],heap[i]);
        siftdown(heap,0,i-1);
    }
}
其中需要注意的是堆排序,在上面的堆排序中,需要排序的数组作为参数直接传入方法中,在堆排序方法中会执行建堆过程。
下面给出一个堆排序的例子:
int num[8]={53,17,78,9,45,65,87,23};
    int n=8;
    cout<<"排序前:";
    for(int j=0;j<n;j++) cout<<num[j]<<" ";
    cout<<endl;
    HeapSort(num,n);
    cout<<"排序后:";
    for(int j=0;j<n;j++) cout<<num[j]<<" ";
    cout<<endl;

运行结果:
从运行结果可以知道, 利用最大堆进行排序的结果是非降序的。

四、小根堆
对照上文中的大根堆的实现代码,很快我们可以写成小根堆的实现代码如下:(注意:小根堆的根结点元素在数组中的位置也是从0开始的)
//最小堆的下滑调整
void siftdown(int *heap,int start,int m)  //m表示堆元素个数
{
    int i=start,j=2*i+1;
    int temp=heap[i];
    while(j<=m)
    {
        if(j<m&&heap[j]>heap[j+1]) j++;
        if(temp<heap[j]) break;
        else
        {
            heap[i]=heap[j];
            i=j;
            j=2*j+1;
        }
    }
    heap[i]=temp;
}

//最小堆的上滑调整
void siftup(int *heap,int start)
{
    int j=start,i=(j-1)/2;
    int temp=heap[j];
    while(j>0)
    {
        if(heap[i]<=temp) break;
        else
        {
            heap[j]=heap[i];
            j=i;
            i=(i-1)/2;
        }
    }
    heap[j]=temp;
}

//建立最小堆
void Creat_minheap(int *arr,int n)  //n表示堆元素个数
{
    int currentsize=n;  //当前堆大小
    int currentpos=(currentsize-2)/2;
    while(currentpos>=0)
    {
        siftdown(arr,currentpos,currentsize-1);
        currentpos--;
    }
}
//插入一个元素
bool insert(int *heap,int &n,int x)   //将x插入到最小堆中
{                                     //n表示堆元素个数  这里的n加一之后返回
    heap[n]=x;
    siftup(heap,n);
    n++;
    return true;
}

//删除堆顶元素
bool remove(int *heap,int &n,int &x)   //删除堆顶(最小元素)x返回堆顶元素
{                                      //n表示堆元素个数  这里的n减一之后返回
    x=heap[0];
    heap[0]=heap[n-1];
    n--;
    siftdown(heap,0,n);
    return true;
}

//堆排序,利用小根堆从大到小排序数组
void HeapSort(int *heap,int n)
{
    for(int i=(n-2)/2;i>=0;i--)  //将数组(表)转换成为堆(建堆),这里是小根堆
        siftdown(heap,i,n-1);
    for(int i=n-1;i>=0;i--)      //排序
    {
        swap(heap[0],heap[i]);
        siftdown(heap,0,i-1);
    }
}

同样我们还是注意到这里的堆排序:
同样执行上面的一个堆排序例子:
int num[8]={53,17,78,9,45,65,87,23};
    int n=8;
    cout<<"排序前:";
    for(int j=0;j<n;j++) cout<<num[j]<<" ";
    cout<<endl;
    HeapSort(num,n);
    cout<<"排序后:";
    for(int j=0;j<n;j++) cout<<num[j]<<" ";
    cout<<endl;

运行结果:

从运行结果可以知道, 利用最小堆进行排序的结果是非升序的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉堆是一种特殊的二叉树数据结构,它满足以下两个条件: 1. 父节点的值总是大于或等于(或小于或等于)其子节点的值。 2. 二叉堆是一棵完全二叉树,即除了最底层,其他层的节点个数都是满的,并且最底层的节点都尽可能地左对齐。 在Python中,我们可以使用内置的heapq模块来实现二叉堆。heapq模块提供了一些函数来操作堆,例如heappush、heappop、heapify等。 以下是一个简单的示例代码,演示如何使用heapq模块来构建和操作二叉堆: ```python import heapq # 创建一个空的二叉堆 heap = [] # 向堆中插入元素 heapq.heappush(heap, 5) heapq.heappush(heap, 2) heapq.heappush(heap, 10) heapq.heappush(heap, 1) # 弹出堆中的最小元素 min_element = heapq.heappop(heap) print(min_element) # 输出: 1 # 查看堆中的最小元素(不弹出) min_element = heap[0] print(min_element) # 输出: 2 # 将列表转换为堆 nums = [4, 8, 3, 6, 1] heapq.heapify(nums) # 弹出堆中的最小元素 min_element = heapq.heappop(nums) print(min_element) # 输出: 1 ``` 上述代码中,我们首先创建了一个空的二叉堆,然后使用heappush函数向堆中插入元素。通过heappop函数,我们可以弹出堆中的最小元素。如果只是查看最小元素而不弹出,可以直接访问heap[0]。还可以使用heapify函数将一个普通的列表转换为二叉堆。 希望这个简单的示例能帮助你理解Python中二叉堆的概念和使用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值