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

二叉树基本学习

heap.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//堆,规定的是父亲与孩子的关系
// 物理:数组
//        也可以链式存储,但不是那么方便
// 逻辑:完全二叉树
// 
// 大堆:父亲大于等于孩子
// 小堆:父亲小于等于孩子
// 满二叉树为h,节点个数为N,若最后一层是满的,F(h)= 2^h - 1; N = logn;
// 若最后一层只有一个,则F(h)= 2^(h-1);——也有上面的式子可以得到
// 
// 若忽略掉细节,堆的向上和向下调整都可以认为h = log n
//     
//

//物理上用数组来实现,涉及扩容问题
typedef int HPDataType;

typedef struct Heap
{
    HPDataType* a;
    int size;
    int capacity;
}HP;

void Swap(HPDataType* p1, HPDataType* p2);


void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);

void HPInit(HP* php);
void HPDestroy(HP* php);
void HPPush(HP* php, HPDataType x);
void HPPop(HP* php);
HPDataType HPTop(HP* php);
bool HPEmpty(HP* php);


heap.c

#include "heap.h"
void HPInit(HP* php)
{
    assert(php);
    php->a = NULL;
    php->size = php->capacity = 0;
}

void HPDestroy(HP* php)
{
    assert(php);
    free(php->a);
    php->a = NULL;
    php->size = php->capacity = 0;
    free(php);
}

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>=0的话,最后会parent= child= 0;歪打正着能跑
        }
        else
        {
            break;
        }
    }
    //初始条件、中间过程、结束条件
}

void HPPush(HP* php, HPDataType x)
{
    assert(php);
    if (php->size == php->capacity)
    {
        int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
        HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        php->a = tmp;
        php->capacity = newcapacity;
    }
    php->a[php->size] = x;//前面的size-1个都是元素
    php->size++;
    AdjustUp(php->a, php->size - 1);
}

void AdjustDown(HPDataType* a, int n, int parent)
{
    //先假设左孩子小
    int child = parent * 2 + 1;
    while (child < n)//若>=n,说明孩子已经不存在,已经调整到叶子了
    {
        //否则会有数组越界风险
        if (a[child + 1] > a[child] && child + 1 < n)
        {
            ++child;
        }
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
    
}


//时间复杂度是O(log N)
void HPPop(HP* php)
{
    assert(php);
    assert(php->size > 0);
    //删除堆顶的数据,也就是根位置
    //不能直接删除,否则父子关系很可能会混乱
    //让最下面的子孙去换一下,然后删尾部的数据,最后进行向下调整算法
    Swap(&php->a[0], &php->a[php->size - 1]);
    php->size--;
    AdjustDown(php->a, php->size, 0);
}

HPDataType HPTop(HP* php)
{
    assert(php);
    assert(php->size > 0);
    return php->a[0];
}

bool HPEmpty(HP* php)
{
    assert(php);
    return php->size == 0;
}


test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "heap.h"
void TestHeap1()
{
    int a[] = {4,3,4,7,6,5,4,3};
    HP hp;
    HPInit(&hp);
    //要改变大堆、小堆,只需要改变向上、向下调整的判断逻辑改变
    for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
    {
        HPPush(&hp, a[i]);//这里相当于在堆上开辟的数组,再将排好的放进在栈上的数组
    }
    int i = 0;
    while (!HPEmpty(&hp))
    {
        //printf("%d  ", HPTop(&hp));
        a[i++] = HPTop(&hp);
        HPPop(&hp);
    }
    //找出最大的前k个
    /*int k = 0;
    scanf("%d", &k);
    while (k--)
    {
        printf("%d  ", HPTop(&hp));
        HPPop(&hp);
    }*/
    HPDestroy(&hp);
}

//向上建堆排序的时间复杂度是O(N*logN)
void HPSort(int* a, int n)
{
    //建堆
    //降序建小堆
    //升序建大堆
    /*for (int i = 1; i < n; i++)
    {
        AdjustUp(a, i);
    }
    */
    //向下建堆的时间复杂度是O(N)
    for (int i = (n-1-1)/2; i >= 0; i--)
    {
        //这里的i相当于是数组中的第i个元素,而我们在这里做的事情是对每个节点都一步步进行向下调整(这里的节点是每个叶节点的父节点)
        //向下调整建堆,是从倒数第一个非叶子节点往下调
        AdjustDown(a, n, i);//向下建堆的方法
        //节点数量多的层,调整次数少
    }
    int end = n - 1;
    while (end > 0)//这里是为了解决兄弟节点之间没有顺序的问题
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);                
        --end;
        //这里时间复杂度和向上建堆一样,是O(N* logN)
    }
}

//进行堆排序的步骤:(因为向下建堆排序的时间复杂度更低,所以只用向下建堆排序)
//首先,取数组首元素地址,然后取得数组大小size,再将最后一个元素传给AdjustDown进行调整
//这里进行的目的是让完全二叉树成为堆,所以从最后一个节点的父节点开始,一直到根节点的每个节点进行AdjustDown
//这里就完成了建堆,但是只满足了父带节点与子代节点之间的大小关系,但是同代之间并不确定
//所以在从最后一个元素开始,首先将从最后一个元素开始的每个元素与根节点交换内容
//然后让新的根节点内容AdjustDown,直到处理到最新一个根节点,到此时也就完成了堆排序
void TestHeap2()
{
    int a[] = { 4,3,4,7,6,5,4,3 };
    //其实就是模拟插入的过程,只不过已经有了空间,不需要再开辟空间的过程
    HPSort(a, sizeof(a) / sizeof(int));
    for (int i = 0; i < sizeof(a) / sizeof(int); i++)
    {
        printf("%d ", a[i]);
    }
}
int main()
{
    //TestHeap1();
    TestHeap2();
    return 0;
}


//Top k 问题
// 方法一
//        建一个大堆,Pop k次
// 但是此方法有一个致命缺点:对空间要求较高,如排10亿个数据的堆,需要4G左右的内存
// 方法二:
//        用前k个数,建一个小堆,剩下的数据跟堆顶比较,若比堆顶的数据大,则替换堆顶的数据进堆
// 覆盖跟位置,然后向下调整,最后这个小堆的k个,就是最大的前k个
// 
//

//不是完全二叉树的采用的存储方法是链式存储方法
//有leftbrother data rightbrother组成,或者再加上一个parent指针
//搜索二叉树:左子树所有值 < 跟 < 右子树所有值
// 最多找树的高度次,有一点点像二分查找,但二分查找并不实用
// 一方面需要先进行排序,另一方面只能对数组进行操作
//  
// 
// 
// 前序、中序、后序遍历
// 前序:根、左子树、右子树
// 中序:左子树、根、右子树
// 后序:左子树、右子树、根
// ——这里有递归的思想,对于前序,任何一棵树的访问,都要符合前序,只有空才不能再拆,也就是循环的回归条件
// 
//

http://www.lqws.cn/news/441379.html

相关文章:

  • “开放原子园区行”太原站:openKylin以开源之力,赋能产业发展
  • Go 运维巡检系统(opsxj)开发与实践
  • 01.线性代数是如何将复杂的数据结构转化为可计算的数学问题,这个过程是如何进行的
  • 前端跨域解决方案(5):websocket
  • SQL注入安全研究
  • JMeter 高阶玩法:分布式压测的技术核心技术要点
  • 容器运行时保护:用Falco构建云原生安全防线
  • Java同步机制四大工具对比
  • 国产USRP X440 PRO:超大带宽、多通道相参同步的旗舰型软件无线电设备
  • 自主学习-《WebDancer:Towards Autonomous Information Seeking Agency》
  • leetcode:461. 汉明距离(python3解法,数学相关算法题)
  • 中国医科大借iMeta影响因子跃升至33.2(中科院1区)东风,凭多组学联合生信分析成果登刊
  • 视觉大语言模型未能充分利用视觉表征
  • Oracle 中唯一索引对行锁的影响
  • 工具 | vscode 发出声音,如何关闭
  • Uniapp 网络请求封装专题
  • 使用Charles抓包工具提升API调试与性能优化效率
  • 【LLM学习笔记3】搭建基于chatgpt的问答系统(下)
  • 从“看懂”到“行动”: VLM 与 VLA
  • MySQL读写分离技术详解:架构设计与实践指南
  • Hive优化详细讲解
  • vue项目插入腾讯地图
  • Umi + qiankun 微前端架构
  • Python爬虫(七):PySpider 一个强大的 Python 爬虫框架
  • SQL分片工具类
  • 动态规划:砝码称重(01背包-闫氏DP分析法)
  • 性能优化中的工程化实践:从 Vite 到 Webpack 的最佳配置方案
  • Day05_数据结构总结Z(手写)
  • 386. 字典序排数
  • 解码成都芯谷金融中心:文化科技产业园的产融创新生态密码