服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言堆与二叉树的顺序结构与实现

C语言堆与二叉树的顺序结构与实现

2022-12-05 13:10Hiland. C/C++

堆是计算机科学中一类特殊的数据结构的统称,通常是一个可以被看做一棵完全二叉树的数组对象。而堆排序是利用堆这种数据结构所设计的一种排序算法。本文将详细介绍堆与二叉树的顺序结构与实现,需要的可以参考一下

一. 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

二. 堆的概念及结构

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: = 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值
  • 堆总是一棵完全二叉树

C语言堆与二叉树的顺序结构与实现

C语言堆与二叉树的顺序结构与实现

三. 堆的实现

将要实现的接口及堆的创建:

(由于堆的特点,这里使用数组结构实现)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//将要实现的是大堆
typedef int HPDataType;
//创建堆结构体
typedef struct Heap
{
    HPDataType* arr;//数组存储
    size_t capacity;//容量
    size_t size;//堆里的元素个数
}HP;
//初始化堆
void HeapInit(HP* php);
//销毁堆
void HeapDestroy(HP* php);
//交换
void swap(HPDataType* pa, HPDataType* pb);
//插入堆,后面插入
void HeapPush(HP* php, HPDataType x);
//删除堆元素,第一个元素
void HeapPop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的大小
size_t HeapSize(HP* php);
//返回堆顶元素
HPDataType HeapTop(HP* php);

堆的初始化

?
1
2
3
4
5
6
void HeapInit(HP* php)
{
    assert(php);//堆必须存在
    php->arr = NULL;
    php->capacity = php->size = 0;
}

堆的销毁

?
1
2
3
4
5
6
7
8
void HeapDestroy(HP* php)
{
    assert(php);
    //销毁数组
    free(php->arr);
    php->arr = NULL;
    php->capacity = php->size = 0;
}

交换函数

?
1
2
3
4
5
6
void swap(HPDataType* pa, HPDataType* pb)
{
    HPDataType temp = *pa;
    *pa = *pb;
    *pb = temp;
}

堆的插入

这里的插入是在堆的最后面插入,堆不能任意位置插入会破坏堆的结构,这里最后面插入也会面临一个问题,插入必须还是大堆,那就要使用向上调整法

C语言堆与二叉树的顺序结构与实现

接下来插入一个70,由于70最大,所以会使用到向上调整法,如下图:

C语言堆与二叉树的顺序结构与实现

将新插入进来的元素与父节点对比,如果父节点小于子节点,就交换,依次往下进行,否则就不用交换,终止向上调整

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//向上调整法
void AdjustUp(HPDataType* arr, size_t child)
{
    //父节点
    HPDataType parent = (child - 1) / 2;
    //交换
    while (child > 0)//用child控制,parent会死循环
    {
        //如果父节点更大,说明需要更换
        if (arr[parent] < arr[child])
        {
            swap(&arr[parent], &arr[child]);
        }
        //孩子和父亲都往上走
        child = parent;
        parent = (child - 1) / 2;
    }
}
void  HeapPush(HP* php, HPDataType x)
{
    assert(php);
    //扩容
    if (php->size == php->capacity)
    {
        size_t newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
        HPDataType* temp = (HPDataType*)realloc(php->arr, sizeof(HPDataType) * newcapacity);
        assert(temp);
        php->arr = temp;
        php->capacity = newcapacity;
    }
    php->arr[php->size] = x;
    ++php->size;
    //需要将孩子穿过去,注意size是在最后一个位置的后一个位置
    AdjustUp(php->arr, php->size-1);
}

堆的删除

堆的删除删除的是堆顶的元素,但是不能盲目的将第一个元素删除,然后将后面的元素往前覆盖,因为当数组里的元素没有顺序时,就会破坏了堆的结构,所以这里需要用到向下调整算法

在插入的基础上,删除掉堆顶的数,也就是70,此时就要使用到向下调整法,如下图:

C语言堆与二叉树的顺序结构与实现

因为我们删除的是堆顶的元素,所以可以这样

先将堆顶元素和最后一个元素进行交换,再删除交换后的堆尾的元素,此时的堆顶元素因为不知道大小,所以将其和自己的两个孩子中最大的比较,如果堆顶的元素小就交换,依次往下进行,否则就不交换,结束向下调整

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void AdjustDown(HPDataType* arr, size_t parent, size_t size)
{
    //假设左孩子最小
    HPDataType child = (parent * 2) + 1;
    //使用child控制,parent会越界
    while (child < size)
    {
        //如果右孩子更小则让右孩子去比较,注意右孩子是否存在
        if (arr[child] < arr[child + 1] && child + 1 < size)
        {
            ++child;
        }
        //将父亲和孩子比较,父亲更大则交换
        if (arr[parent] < arr[child])
        {
            swap(&arr[parent], &arr[child]);
            parent = child;
            child = (parent * 2) - 1;
        }
        //当出现父亲小于孩子时,说明不用往下遍历了
        else
        {
            break;
        }
    }
}
void HeapPop(HP* php)
{
    assert(php);
    //堆不能为空
    assert(php->size > 0);
    //首尾元素的交换
    HPDataType temp = php->arr[0];
    php->arr[0] = php->arr[php->size - 1];
    php->arr[php->size - 1] = temp;
    //删除交换后的尾元素
    --php->size;
    //向下调整
    AdjustDown(php->arr, 0, php->size);
}

判空堆是否为空

?
1
2
3
4
5
bool HeapEmpty(HP* php)
{
    assert(php);
    return php->size == 0;
}

返回堆的大小

?
1
2
3
4
5
size_t HeapSize(HP* php)
{
    assert(php);
    return php->size;
}

返回堆顶元素

?
1
2
3
4
5
6
7
HPDataType HeapTop(HP* php)
{
    assert(php);
    //得要有元素
    assert(php->size > 0);
    return php->arr[0];
}

四. 堆排序(具有缺陷型)

利用以上接口实现堆排序(具有缺陷),具有O(n)的空间复杂度

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main()
{
    int arr[] = { 2,5,6,4,54,23,1,45,67,98 };
    int size = sizeof(arr) / sizeof(arr[0];
    HP hp;//创建堆
    HeapInit(&hp);//初始化
    //堆插入元素,时间复杂度为O(nlogn),空墨盒加墨复杂度O(n)
    for (int i = 0; i < size; i++)
    {
        HeapPush(&hp, arr[i]);
    }
    //堆排序,时间复杂度为O(nlogn)
    while (!HeapEmpty(&hp))
    {
        printf("%d ", HeapTop(&hp));//拿堆顶元素
        HeapPop(&hp);//删除堆顶元素
    }
    //使用完销毁堆
    HeapDestroy(&hp);
    return 0;
}

到此这篇关于C语言堆与二叉树的顺序结构与实现的文章就介绍到这了,更多相关C语言堆与二叉树内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/Britney_dark/article/details/124645691

延伸 · 阅读

精彩推荐
  • C/C++C语言详细讲解树状数组与线段树

    C语言详细讲解树状数组与线段树

    顾名思义,树状数组就是用数组来模拟树形结构呗。那么衍生出一个问题,为什么不直接建树,因为树状数组能处理的问题就没必要建树。线段树是一种二...

    小羊努力变强3672022-11-07
  • C/C++C/C++ break和continue区别及使用方法

    C/C++ break和continue区别及使用方法

    这篇文章主要介绍了C/C++ break和continue区别及使用方法的相关资料,需要的朋友可以参考下...

    140827147062021-05-21
  • C/C++C++ sizeof 实例解析

    C++ sizeof 实例解析

    下面5个列子针对C++,没有涉及到sizeof字节对齐及基本数据类型即只针对C++特有,并且针对的是32位机...

    C++教程网4712020-12-18
  • C/C++二叉查找树的插入,删除,查找

    二叉查找树的插入,删除,查找

    以下是对二叉查找树的插入与删除以及查找进行了详细的介绍,需要的朋友可以 过来参考下...

    C语言教程网6842020-12-25
  • C/C++C语言详细分析讲解关键字enum与sizeof及typedef的用法

    C语言详细分析讲解关键字enum与sizeof及typedef的用法

    在 C 语言中经常会见到 enum、sizeof、typedef,那么我们今天就来讲解下它们三个,enum是C语言中的一种自定义类型,它是一种枚举类型,sizeof是编译器的内置...

    清风自在 流水潺潺4592022-11-11
  • C/C++C/C++ 避免数组越界的方法

    C/C++ 避免数组越界的方法

    这篇文章主要介绍了C/C++ 避免数组越界的方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    慢慢爬的绿毛龟10192021-09-10
  • C/C++C++中的字符串(1)

    C++中的字符串(1)

    这篇文章主要简单介绍C++中的字符串,字符串就是连续的一连串字符,在C++当中, 处理字符串的方式有两种类型。一种来自于C语言,也被称为C风格字符串...

    梁唐10782022-02-20
  • C/C++C++ I/O文件读写操作的示例代码

    C++ I/O文件读写操作的示例代码

    这篇文章主要介绍了C++ I/O文件读写操作的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    誰能久伴不乏,,3892021-09-10