当前位置: 首页 > news >正文

二叉树(堆)

二叉树(堆)

简介

前文,我们大概的介绍了一下树的一些基本概念,然后这节就主要讲解一下二叉树的其中一种存储方式:用数组存储(顺序存储)。及其一些特殊结构,也就是堆。

注意一下就是,这种用数组存储二叉树的方式只适用于完全二叉树。非完全二叉树的话就更加适合链式存储,这个后面讲。

请添加图片描述

堆是什么呢?

请添加图片描述
在这里插入图片描述

堆的插入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

讲完基本的思路,那么就要开始写代码了。

堆的实现及其功能代码

头文件.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>

//数据类型
typedef int HPDataType;

typedef struct Heap
{
    HPDataType *a;//数组首元素地址
    int size;//数组中有效元素的个数(数组大小)
    int capacity;//数组的容量
}HP;

//向上调整
void AdjustUp(HPDataType* a,int child);
//向下调整
void AdjustDown(HPDataType* a,int n,int parent);

//交换函数
void Swap(HPDataType* p1,HPDataType* p2);

//打印函数
void HeapPrint(HP* pq);
//初始化函数
void HeapInit(HP* pq);
//第二种初始化函数
void HeapInitArray(HP* pq,int* a,int n);
//摧毁函数
void HeapDestroy(HP* pq);

//插入函数
void HeapPush(HP* pq,HPDataType x);
//删除函数
void HeapPop(HP* pq);

//获取堆顶元素
HPDataType HeapTop(HP* pq);
//判断堆是否为空
bool HeapEmpty(HP* pq);

实现文件.c

#include "Heap.h"


//初始化函数
void HeapInit(HP* pq)
{
    assert(pq);

    pq->a=NULL;
    pq->size=pq->capacity=0;
}
//第二种初始化函数(把已经存在的数组,复制一份给我们的数组)
void HeapInitArray(HP* pq,int* a,int n)
{
    assert(pq);
    assert(a);

    //这里直接开了一个大小为n的数组空间
    pq->a=(HPDataType*) malloc(sizeof (HPDataType)*n);
    if(pq->a==NULL)
    {
        perror("malloc failed");
        exit(-1);
    }
    pq->capacity=n;

    memcpy(pq->a,a,sizeof (HPDataType)*n);
    pq->size=n;

    //建堆
    for(int i=1;i<n;i++)
    {
        AdjustUp(pq->a,i);
    }
}

//摧毁函数
void HeapDestroy(HP* pq)
{
    assert(pq);

    free(pq->a);
    pq->a=NULL;
    pq->size=pq->capacity=0;
}

//交换函数
void Swap(HPDataType* p1,HPDataType* p2)
{
    HPDataType tmp=*p1;
    *p1=*p2;
    *p2=tmp;
}

//向上调整(建小堆)
void AdjustUp(HPDataType* a,int child)
{
    int parent=(child-1)/2;
    while(child>0)
    {
        if(a[child]<a[parent])
        {
            Swap(&a[child],&a[parent]);
            child=parent;
            parent=(child-1)/2;//parent=(parent-1)/2;
        }
        else
        {
            break;
        }
    }

}
//向下调整(小堆)
void AdjustDown(HPDataType* a,int n,int parent)
{
    int child=parent*2+1;
    while(child<n)
    {
        //找到大的那个孩子
        if(child+1<n&&a[child+1]<a[child])
        {
            child++;
        }

        if(a[child]<a[parent])
        {
            Swap(&a[child],&a[parent]);
            //继续向下调整
            parent=child;
            child=parent*2+1;
        }
        else
        {
            break;
        }
    }
}

//插入函数
void HeapPush(HP* pq,HPDataType x)
{
    assert(pq);

    //判断容量是否足够
    if(pq->size==pq->capacity)
    {
        int newCapacity=pq->capacity==0?4:pq->capacity*2;
        HPDataType* tmp=(HPDataType*) realloc(pq->a,sizeof (HPDataType)*newCapacity);
        if(tmp==NULL)
        {
            perror("reallocate failed");
            exit(-1);
        }

        pq->a=tmp;
        pq->capacity=newCapacity;
    }

    pq->a[pq->size]=x;
    pq->size++;

    AdjustUp(pq->a,pq->size-1);
}
//删除函数
void HeapPop(HP* pq)
{
    assert(pq);
    assert(pq->size>0);

    Swap(&pq->a[0],&pq->a[pq->size-1]);
    pq->size--;

    AdjustDown(pq->a,pq->size,0);
}

//获取堆顶元素
HPDataType HeapTop(HP* pq)
{
    assert(pq);
    assert(pq->size>0);

    return pq->a[0];
}
//判断堆是否为空
bool HeapEmpty(HP* pq)
{
    assert(pq);

    return pq->size==0;
}

//打印函数
void HeapPrint(HP* pq)
{
    assert(pq);

    for(int i=0;i<pq->size;i++)
    {
        printf("%d ",pq->a[i]);
    }
    printf("\n");
}

测试文件.c

#include "Heap.h"

/*
void HeapSort(int* a,int n)
{
    for(int i=(n-1-1)/2;i>=0;i--)
    {
        AdjustDown(a,n,i);
    }
    int end=n-1;
    while(end>0)
    {
        Swap(&a[0],&a[end]);
        AdjustDown(a,end,0);
        end--;
    }
}

int main()
{
    int a[]={65,100,70,32,50,60};

    HeapSort(a,sizeof (a)/sizeof (int));
    return 0;
}*/
void PrintTopK(const char* filename, int k)
{
    // 1. 建堆--用a中前k个元素建堆
    FILE* fout = fopen(filename, "r");
    if (fout == NULL)
    {
        perror("fopen fail");
        return;
    }

    int* minheap = (int*)malloc(sizeof(int) * k);
    if (minheap == NULL)
    {
        perror("malloc fail");
        return;
    }

    for (int i = 0; i < k; i++)
    {
        fscanf(fout, "%d", &minheap[i]);
    }

    // 前k个数建小堆
    for (int i = (k-2)/2; i >=0 ; --i)
    {
        AdjustDown(minheap, k, i);
    }


    // 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
    int x = 0;
    while (fscanf(fout, "%d", &x) != EOF)
    {
        if (x > minheap[0])
        {
            // 替换你进堆
            minheap[0] = x;
            AdjustDown(minheap, k, 0);
        }
    }


    for (int i = 0; i < k; i++)
    {
        printf("%d ", minheap[i]);
    }
    printf("\n");

    free(minheap);
    fclose(fout);
}

// fprintf  fscanf

void CreateNDate()
{
    // 造数据
    int n = 1000;
    srand(time(0));
    const char* file = "data.txt";
    FILE* fin = fopen(file, "w");
    if (fin == NULL)
    {
        perror("fopen error");
        return;
    }

    for (int i = 0; i < n; ++i)
    {
        int x = (rand() + i) % 10000000;
        fprintf(fin, "%d\n", x);
    }

    fclose(fin);
}

int main()
{
    //CreateNDate();//要先使用这个函数创建一个含有N个数据的文本文件,然后注释掉,使用下面那个函数
    PrintTopK("data.txt", 5);

    return 0;
}

解析

  1. 结构
    在这里插入图片描述

  2. 初始化函数和销毁函数
    在这里插入图片描述

  3. 插入函数

在这里插入图片描述在这里插入图片描述

  1. 向上调整函数

在这里插入图片描述
结合下图理解代码

在这里插入图片描述

  1. 删除函数

在这里插入图片描述
在这里插入图片描述

  1. 向下调整函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

建堆

建堆什么意思呢?顾名思义,就是建立起一个堆,可以是大堆,可以是小堆,这里我们以建立小堆给大家讲解。然后我们顺便用上删除操作,这样就可以得到一个简单的排序的手法。

在这里插入图片描述
在这里插入图片描述

优化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

  • CentOS系统下安装tesseract-ocr5.x版本
  • Node.js 包与 npm 详解:概念、作用及完整使用指南
  • Android14 系统左右声音通道设置代码
  • 今天你学C++了吗?——二叉搜索树
  • 深入理解Python闭包与递归:原理、应用与实践
  • java项目之基于ssm的少儿编程在线培训系统(源码+文档)
  • 《K230 从熟悉到...》屏幕手画板
  • AI 原生 IDE Trae 深度体验:SSHremote 功能如何重新定义远程开发与云原生部署
  • 项目-苍穹外卖(九) 店铺营业状态设置+HttpClient
  • Node.js模块:使用 Bull 打造高效的任务队列系统
  • SQL Server数据库简介及应用
  • 网络空间安全专业发展历程及开设院校
  • 【SpringBatch】03步骤对象 (Step)控制与执行流程设计
  • 学习记录 6 pointnet复现
  • 【深度学习量化交易18】盘前盘后回调机制设计与实现——基于miniQMT的量化交易回测系统开发实记
  • 美团Leaf分布式ID生成器使用教程:号段模式与Snowflake模式详解
  • 《UNIX网络编程卷1:套接字联网API》第2章 传输层:TCP、UDP和SCTP
  • [入门]NUC13配置Ubuntu20.04详细步骤
  • 三分钟掌握视频分辨率修改 | 在 Rust 中优雅地使用 FFmpeg
  • v-on=“$listeners“ 这个写法已经废弃了,如进行代替
  • “大鼻子情圣”德帕迪约因性侵被判缓刑,还有新的官司等着他
  • 在笔墨金石间,看胡问遂与梅舒适的艺术对话
  • 字母哥动了离开的心思,他和雄鹿队的缘分早就到了头
  • 中美大幅下调超100%关税,印巴四日“战争”复盘|907编辑部
  • 超新星|罚丢点球的那道坎,刘诚宇靠自己迈了过去
  • 中国工程院院士、国医大师石学敏逝世