堆的创建,插入,删除,排序

本文深入探讨堆数据结构的原理,包括最小堆和最大堆的概念,如何构建、插入、删除元素,以及通过堆排序进行高效排序的方法。同时,文章提供了详细的算法实现和调整策略,帮助读者理解堆在实际应用中的强大功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆是一种完全二叉树,有最小堆和最大堆之分,最小堆是指根节点的值一定小于左子树和右子树所有元素的值,最大堆则相反(当你从小到大排序时, 可以选择最小堆反之,则选择最大堆)

1.如何建立一个最小堆呢:由于堆是一个完全二叉树,所以满足以下关系(我们将元素的顺序从0开始排,第i个节点称之为Ki, 那么元素就有K0, K1, K2…),Ki的父节点一定是K(i-1)/2, Ki的左孩子节点一定是Ki2+1, 右孩子节点一定是Ki2+2。因此我们可以不用向普通二叉树那样,用指针来访问其左孩子节点和右孩子节点(本次程序中是以指针来访问的,虽然有点多此一举,就当锻炼一下自己使用指针的能力吧)。然而最重要的问题是,假如我们一开始面临的是一个堆,我们又如何将其变成一个最小堆呢,那就需要我们进行向上调整(后面会具体讲)

2.如何在一个最小堆中插入一个元素呢: 为了不破坏堆已有的结构,在堆尾部插入一个元素后,会破坏堆的结构,如何进行调整呢?可以采用向上调整

3.如何删除一个元素呢:堆就像是一个队列,在尾部插入,在头部删除,再删除掉整个树的根节点后,会破坏堆的结构,为了有效利用堆已有的结构,将堆尾部的元素移到根节点位置,然后进行向下调整(后面会讲向下调整)

4.向下调整:将尾部节点移到根节点位置,若该节点数值小于左孩子节点和有孩子节点数值,就无需调整,否则就需将数值最小的孩子节点与根节点互换元素值,然后对数值发生改变的孩子节点进行同样的调整。(堆的优异性就体现在这里,对于数值没有发生改变的孩子节点就无需进行调整,这很可能一下子跳过了一大堆节点,所以二叉树的时间复杂性与数的高度有关)

5.向上调整:在尾部插入一个元素后,将该元素与其父节点比,若父节点的值大于该孩子节点,则进行互换元素值,对于堆来说,假如有n个节点,则有n / 2个节点是有孩子节点的,则K0至Kn/2都需要进行调整,注意是从Kn/2至K0的顺序来依此调整的,从树的结构来看就是先上的,同时当改变孩子节点元素值是,若该孩子节点同时也有孩子时要进行向下调整,由于堆无非是根,左孩子和右孩子,可以通过递归来实现,所以代码不是太复杂

关于调整的总结:我们可以发现向上调整和向下调整大致思路是一样的,都是比较根节点与其孩子节点数值的大小以保证最小堆(或最大堆)的要求,所不同的是向上调整与向上调整的调整方向是相反的,向上调整是在尾部插入元素要进行的调整,向下调整是在删除根节点并将尾部节点补为根节点后要进行的调整

6.堆的一个应用就是进行排序:由于根节点的元素值是整个树的最小的,我们可以建立一个堆后,删除该根节点,将余下的节点进行向下调整,得到新的堆,重新进行上述步骤。就可以得到从小到大排列的结果了(从大到小只需建立最大堆即可)。

看到网上经典的堆排序代码,只有不到一百行,不得不反思一下自己的程序为何这么冗长:
(1).堆本可以映射到一个一维数组,无需像普通二叉树那样采用指针来访问根节点,本程序既用指针又用数组(用数组是完全二叉树满足的关系(1中所说)对编程很有用)
(2).向上调整和向下调整其本质差不多,可以合并,这里写成两个接口是为了插入,删除时调用方便,如果只是排序,则可不必这么写
(3).加了一些测试代码时用到的借口(谁让自己菜呢,写程序总有bug),如show(),getlength()等;
(4).不得不感叹经典就是经典,编程方面还得加油啊

#include <stdio.h>
#include <stdlib.h>

#define SIZE 20


typedef int ElemType;
typedef struct treenode
{
    ElemType data;
    struct treenode * left;
    struct treenode * right;
}Node, *pNode;

void show(pNode * tree)
{
    if (tree == NULL)
        return ;
    int i;
    for (i = 0; tree[i] != NULL; i++)
        printf("%-3d", tree[i]->data);
    printf("\n");
}
void getlength(pNode * tree)
{
    if (tree == NULL)
        printf("get length failure\n");;
    int i;
    for (i  = 0; tree[i] != NULL; i++) ;
        printf("tree has %d nodes\n", i);
}
pNode * CreateTree(ElemType *e, int length)
{
    if (e == NULL || length <= 0 || length > SIZE)
        return ;
    pNode *str = (pNode *)malloc(sizeof(pNode) * SIZE);
    int i, j;
    for ( i = 0; i < length; i++)
    {
        str[i] = (pNode)malloc(sizeof(Node) * 1);
        if (str[i] == NULL)
        {
            for (j = 0; j < i; j++)
            {
                free(str[j]);
            }
            free(str);
            printf("堆建立失败\n");
            return NULL;
        }
        else
            str[i]->data = e[i];
    }

       for (i = 0; i < length; i++)
    {
        if (2 * i + 1 < length)
            str[i]->left = str[2 * i + 1];
        else
            str[i]->left = NULL;
        if (2 * i + 2 < length)
            str[i]->right = str[2 * i + 2];
        else
            str[i]->right = NULL;
    }
    return str;
}
void ExchangeData(ElemType *a, ElemType * b)
{
    if (a == NULL || b == NULL)
        return;
    printf("exchange %d and %d\n", *a, *b);
    ElemType temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void SiftUp(pNode root)
{
    if (root == NULL || (root->left ==  NULL && root->right == NULL))
        return;
    ElemType e;
    int c;
    if (root->left == NULL)
    {
        c = 0;
        e = root->right->data;
    }
    else if(root->right == NULL)
    {
        c = 1;
        e = root->left->data;
    }
    else
    {
        c = root->left->data > root->right->data? 0 : 1;
        e = root->left->data > root->right->data? root->right->data : root->left->data;
    }
    if (root->data > e)
    {
        if (c == 0)
        {
            ExchangeData(&(root->data), &(root->right->data));
            SiftUp(root->right);
         }
        else if (c == 1)
        {
            ExchangeData(&(root->data), &(root->left->data));
            SiftUp(root->left);
          }
    }
}
 void SiftDown(pNode root)
{
    if (root == NULL || (root->left == NULL && root->right == NULL))
        return;
    ElemType e;
    int c;
    if (root->left == NULL)
    {
        c = 0;
        e = root->right->data;
    }
    else if(root->right == NULL)
    {
        c = 1;
        e = root->left->data;
    }
    else
    {
        c = root->left->data > root->right->data ? 0 : 1;
        e = root->left->data > root->right->data? root->right->data : root->left->data;
    }
        if (root->data > e)
    {
        if (c == 0)
        {
            ExchangeData(&(root->data), &(root->right->data));
            SiftDown(root->right);
        }
        else if (c == 1)
        {
            ExchangeData(&(root->data), &(root->left->data));
            SiftDown(root->left);
        }
    }
}
 void Delete(pNode * tree)
{
    if (tree == NULL || tree[0] == NULL)
        return;
    int i;
    for (i = 0; tree[i] != NULL; i++) ;
    i--;
    if (i == 0)
    {
        free(tree[0]);
        return;
    }
    tree[0]->data = tree[i]->data;
    free(tree[i]);
    tree[i] = NULL;
    if (i % 2 == 1)
        tree[(i - 1) / 2]->left = NULL;
    else
        tree[(i - 1) / 2]->right = NULL;
    SiftDown(tree[0]);
}

 void Insert(pNode *tree, ElemType e)
{
    if (tree == NULL)
    {
        printf("insert failure\n");
        return ;
    }
    int i, j;
    for ( i = 0; tree[i] != NULL; i++);
    if (i > SIZE)
    {
        printf("insert failure\n");
        return;
    }
    printf("i = %d\n", i);
    tree[i] = (pNode)malloc(sizeof(Node) * 1);
    if (tree[i] == NULL)
    {
        printf("insert failure\n");
        return;
    }
    tree[i]->data = e;
    tree[i]->left = NULL;
    tree[i]->right = NULL;
    if (i % 2 == 1)
        tree[(i - 1) / 2]->left = tree[i];
    else
        tree[(i - 1) / 2]->right = tree[i];
    for (j = (i + 1) / 2 - 1; j >= 0; j--)
        SiftUp(tree[j]);
}

int main()
{
    ElemType res[SIZE];
    int i;
    ElemType e;
    ElemType a[SIZE] = {2, 5, 6, 23, 45, 55, 67, 32, 56, 11, 1};
/*  printf("please input 10 numbers\n");
    for (i = 0; i < 10; i++)
        scanf("%d", a + i);
*/
    pNode * tree = CreateTree(a, 10);
    if (tree == NULL)
    {
        printf("create tree failure\n");
        exit(1);
    }
    for (i = 10 / 2 - 1; i >= 0; i--)
        SiftUp(tree[i]);
    show(tree);
    for(i = 0; i < 5; i++)
    {
        printf("please input the num which you want to insert\n");
        scanf("%d", &e);
        Insert(tree, e);
        show(tree);
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值