最大(小)堆---数组的实现

最大堆是我在看浙大陈越老师的数据结构视频学到的,用的数组实现的

如何描述堆呢?用我自己的话说就是:

根是这颗树最大的值,每个   根节点都比    左右子节点的值大, 对左右子树仍然成立;

 那么最小堆呢正好相反:

根是这颗树的最小的值, 每个  根节点都比左右子节点的值小, 同样对左右子树成立;

图能最直观的体现:

我们很容易看出来,堆的左右子树仍然是堆。

(图摘自CSDN)所以无论对于最大堆,最小堆,同一层之间的大小顺序是不确定的

那么现在说,如何建立一颗这样的堆呢?

 

1、在说堆之前相必大家都了解完全二叉树,以及数组下标(从1开始)和完全二叉树节点位置的技巧(即假如说根节点是i,那么它的左儿子=2*i, 右儿子=2*i+1),堆就是利用完全二叉树建立的,这里不理解没关系,因为答案在下边。

现在,我们说假如说有这么一棵树:

                                                    

假如说现在在右下角插入100,那么,很明显就不符合堆的概念了,怎么调整呢?

  1.  如果    它比根节点大,就让它和根节点调换位置
  2.  那么这颗树如果是上面大根堆里面的一颗子树呢?继续进行: 

       如果    它比根节点大,就让它和根节点调换位置

直到调换到整棵树  根的位置,或者现在已经比根节点要小了
(已经满足堆的概念了)

当然最小堆道理是相同的

以上是  当前一颗最大堆,如何往里面插值,那么现在我最想知道的是如何构建一个最大堆,你跟我扯插入扯这么半天干什么呢?那么如果咱们是把一组数据往一个空堆里面插呢?哈哈哈

2、咱们继续往下说:

如果现在给你一个最大堆:

                                                    

 

删除堆的最大值:你怎么办呢?

把堆顶删了(也就是根),为了保证他还是一颗完全二叉树,就把最下面一层最右边一个放到堆顶。

为了要让它还满足堆的要求,那么就跟它的左右子节点中最大的交换值(就是说现在80在树顶,10为左儿子)

现在目标转移到左子树即:

                                         

重复操作。

也就是实现了数据的删操作

那么我又是在扯皮了,建树呢?建树呢?!建树呢?!!!

3、现在正式来说一下建树的问题(。。。你终于说到建树了):

现在给你一组数据,如果你直接放到数组里面是这个样子(从下标1开始):

根据数组下标与完全二叉树节点的关系,构成的二叉树是这个样子:

                                                      

我们知道删除堆顶时把最下面的值覆盖堆顶元素,经过操作让能成为堆,是因为它的左右子树仍然是堆。

那么我们现在对26这个节点做如下调整:

~~1、如果     它比左右子节点的最大值小,  就跟较大子节点交换值

~~2、目标移到交换的子节点,继续~~1

~~3、否则满足堆的概念跳出循环。

然后对6节点做上面的~~ 1   ~~2  ~~3 调整,

然后对15节点做~~ 1   ~~2  ~~3 调整。。。(可自行画一个更大的堆模拟一下这三步,就更清晰了)

最后就行成如下堆:

                                                  

over,以下是代码实现:

#include<cstdio>
#include<iostream>
#include<cstdlib>

#define MAXDATA 100000000
#define MAXSIZE 10005

using namespace std;

typedef struct HeapChain
{
    int *Element;
    int sizee;
    int capacity;
} HeapChain, *HeapChain_idx;

HeapChain_idx H;
///建造一个空的堆
void Creat_Heap(HeapChain_idx &H)
{
    H = (HeapChain_idx) malloc(sizeof(HeapChain));
    H->Element = (int *)malloc(sizeof(MAXSIZE * sizeof(int)));
    H->Element[0] = MAXDATA;   ///Element[0]的位置做哨兵,防止插入时越界

    H->sizee = 0;
    H->capacity = MAXSIZE - 3;

    return ;
}
int In_Heap(HeapChain_idx H, int x)
{
    if(H->sizee >= H->capacity)
        return 0;

    int i = ++H->sizee;
    for( ; H->Element[i/2] < x; i /= 2)     ///插入的数只要比父节点大,父节点的值就往下移
        H->Element[i] = H->Element[i/2];
    H->Element[i] = x;
    return 1;
}

int Remove_Heap(HeapChain_idx H, int &e)
{
    if(H->sizee == 0)
        return 0;
    e = H->Element[1];
    H->Element[1] = H->Element[H->sizee--];    ///用最后一个数将堆顶覆盖

    int parent, child;
    for(parent = 1; parent * 2 <= H->sizee; parent = child)      ///将堆顶数往满足堆定义的位置移动
    {
        child = parent * 2;
        if(child < H->sizee && H->Element[child] < H->Element[child+1])      ///找出左右子节点的较大者
            child++;

        if(H->Element[parent] < H->Element[child])                           ///如果比子节点最大者小就往下移动
        {
            int t;
            t = H->Element[parent], H->Element[parent] = H->Element[child], H->Element[child] = t;
        }
        else                                                                  ///否则就比两个子节点都大或相等,满足堆定义。调整完成
            break;
    }

    return 1;
}

int Adjust_Heap(HeapChain_idx H)
{
    if(H->sizee == 0)
        return 0;

    for(int k = H->sizee/2; k > 0; k--)             ///从最后一个节点的父节点调整使满足堆定义,调整至堆顶
    {
        int parent, child;
        for(parent = k; parent * 2 <= H->sizee; parent = child)   ///如果当前节点要比最大的子节点要小,就往下移,相等不用移动
        {
            child = parent * 2;
            if(child < H->sizee && H->Element[child] < H->Element[child+1])
                child++;

            if(H->Element[parent] < H->Element[child])
            {
                int t;
                t = H->Element[parent], H->Element[parent] = H->Element[child], H->Element[child] = t;
            }
            else
                break;
        }
    }

    return 1;
}


int main()
{
    int n, e;
    HeapChain_idx H;

///测试建堆
    scanf("%d", &n);
    Creat_Heap(H);
    for(int i = 1; i <= n; i++)
        scanf("%d", &H->Element[i]);
    H->sizee = n;
    int flag;
    flag = Adjust_Heap(H);
    if(flag == 0)
        printf("堆为空\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");

///测试删除的堆顶元素:
    flag = Remove_Heap(H, e);
    printf("被删除的数字为:%d\n", e);
    if(flag == 0)
        printf("堆为空\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");

///测试插入一个数:
    printf("请输入要插入的数字\n");
    scanf("%d", &e);
    flag = In_Heap(H, e);
    if(flag == 0)
        printf("堆已经满了\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");


    return 0;
}

 

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值