二叉树和堆(理论)

2023-05-16

1.树其实就是不包含回路连通无向图

2.一棵树中的任意两个结点有且仅有唯一的一条路径连通。

3.一棵树如果有n个结点,那么它一定恰好有n-1条边。

二叉树

二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。

【满二叉树与完全二叉树】

如果二叉树中每个内部结点都有两个儿子,这样的二叉树叫做满二叉树。如下所示:

如果一棵二叉树除了最右边位置上一个或者几个叶结点缺少外其它是丰满的,那么这样的二叉树就是完全二叉树。如下所示:

【完全二叉树如何储存】

完全二叉树中父亲和儿子之间有着神奇的规律,我们只需用一个一维数组就可以存储完全二叉树。首先将完全二叉树进行从上到下,从左到右编号。我们发现如果完全二叉树的一个父结点编号为k,那么它左儿子的编号就是2*k,右儿子的编号就是2*k+1。如果已知儿子(左儿子或右儿子)的编号是x,那么它父结点的编号就是x/2。

堆是什么?堆是一种特殊的完全二叉树。

所有父结点都比子结点要小的完全二叉树我们称为最小堆。反之,如果所有父结点都比子结点要大,这样的完全二叉树称为最大堆。

如下图所示,左边为最小堆,右边为最大堆。

【堆的创建】

把n个元素建立一个堆,首先我们可以将这n个结点以自顶向下、从左到右的方式从1到n编码,即以层序遍历的顺序编码,这样就可以把这n个结点转换成为一棵完全二叉树。紧接着从最后一个非叶结点(结点编号为n/2)开始到根结点(结点编号为1),逐个扫描所有的结点,根据需要将当前结点向下调整,直到以当前结点为根结点的子树符合堆的特性。

下面以建立最小堆为例:

#include <stdio.h>
#define maxn 105
int h[maxn]; //用来存放堆
int n; //堆的大小
//交换堆中的两个元素的值
void swap(int x,int y)
{
    int t=h[x];
    h[x]=h[y];
    h[y]=t;
}
//向下调整成最小堆
void siftdown(int pos)
{
    int t,flag=0; //flag用来标记是否需要继续向下调整
    while(!flag)
    {
        int t=pos; //用t记录父结点和左右儿子中值较小的结点编号
        if(pos*2<=n&&h[t]>h[pos*2]) t=pos*2;
        if(pos*2+1<=n&&h[t]>h[pos*2+1]) t=pos*2+1;
        //如果最小的结点不是父结点
        if(t!=pos)
        {
            swap(t,pos);
            pos=t;
        }
        else flag=1;
    }
}
//建堆
void create()
{
    //从最后一个非叶结点到第1个结点依次进行向下调整
    for(int i=n/2;i>=1;i--)
        siftdown(i);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&h[i]);
    create();
    for(int i=1;i<=n;i++) printf(i==n?"%d\n":"%d ",h[i]);
    return 0;
}

【堆的插入与删除】 

若最小(大)堆要进行删除最小(大)数并返回最小(大)数的操作,我们只需要删掉堆顶元素即最小(大)数,将最后一个数放在堆顶,通过比较与左右儿子的大小向下调整即可恢复为最小(大)堆。

下面以最小堆为例。

//向下调整成最小堆
void siftdown(int pos)
{
    int t,flag=0; //flag用来标记是否需要继续向下调整
    while(!flag)
    {
        int t=pos; //用t记录父结点和左右儿子中值较小的结点编号
        if(pos*2<=n&&h[t]>h[pos*2]) t=pos*2;
        if(pos*2+1<=n&&h[t]>h[pos*2+1]) t=pos*2+1;
        //如果最小的结点不是父结点
        if(t!=pos)
        {
            swap(t,pos);
            pos=t;
        }
        else flag=1;
    }
}
//返回并删除最大的元素
int deletemax()
{
    int t=h[1]; 
    h[1]=h[n]; //将堆的最后一个点赋值到堆顶
    n--; //堆的元素减少1
    siftdown(1); //向下调整
    return t; //返回最大值
}

若最小(大)堆要进行删除最小(大)数并插入一个新的数的操作,我们只需要删掉堆顶元素即最小(大)数,将新的数放在堆顶,通过比较与左右儿子的大小向下调整即可恢复为最小(大)堆。 

 若最小(大)堆只需进行插入一个新的数,我们只需要把新的数放在末尾,通过与父亲的比较向上调整即可恢复最小(大)堆。

下面以最小堆为例。 

//向上调整成最小堆
void siftup(int pos) 
{
    int flag=0; //用来标记是否需要继续向上调整
    if(pos==1)  return; //如果是堆顶,就不需要调整了    
    //不在堆顶 并且 当前结点pos的值比父结点小的时候继续向上调整 
    while(pos!=1&&!flag)
    {
        //判断是否比父结点的小 
        if(h[pos]<h[pos/2]) swap(pos,pos/2); 
        else flag=1;
        pos=pos/2; //更新编号pos为它父结点的编号 
    }
}

【堆排序(Heap Sort)】

堆排序在寻找最小值(或最大值)的过程中使用了堆这种数据结构,提高了效率。堆是一个近似二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

那么我们要如何实现堆排序呢?下面以升序排序为例讲解。

算法一:

将需要排序的序列建堆,调整成最小堆。

每次删除并输出堆顶结点的值即最小值,再将最后一个数放置于堆顶结点的位置,最小堆的大小-1,向下调整成最小堆。重复上述步骤直到堆为空。

算法二:

将需要排序的序列建堆,调整成最大堆。

每次把堆顶结点的值即最大值与最后一个结点交换位置,最大堆的大小-1,向下调整成最大堆。重复上述步骤直到结束。

最后层序遍历输出堆排序后的堆,即为升序序列。

下面为算法二的代码,以升序堆排序为例: 

#include <stdio.h>
#define maxn 105
int h[maxn];
int n;
//交换函数
void swap(int x,int y)
{
    int t=h[x];
    h[x]=h[y];
    h[y]=t;
}
//向下比较调整成最大堆
void siftdown(int pos,int num) 
{
    int t,flag=0; //flag用来标记是否需要继续向下调整
    while(!flag)
    {
        int t=pos; //用t记录父结点和左右儿子中值较大的结点编号
        if(pos*2<=num&&h[t]<h[pos*2]) t=pos*2;
        if(pos*2+1<=num&&h[t]<h[pos*2+1]) t=pos*2+1;
        //如果最大的结点不是父结点
        if(t!=pos)
        {
            swap(t,pos);
            pos=t;
        }
        else flag=1;
    }
}
void create()
{
    //从最后一个非叶结点到第1个结点依次进行向下调整
    for(int i=n/2;i>=1;i--)
        siftdown(i,n);
}
//堆排序(升序)
void heapSort()
{
    create(); //建堆
    int num=n; 
    for(int i=n;i>1;i--)
    {
        swap(1,i); //交换最大值与最后一个数
        num--;
        siftdown(1,num); //前num个数调整成最大堆
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&h[i]);
    heapSort();
    for(int i=1;i<=n;i++) printf(i==n?"%d\n":"%d ",h[i]);
    return 0;
}

堆排序是不稳定的排序。最差时间复杂度为O(nlogn),平均时间复杂度为O(nlogn)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二叉树和堆(理论) 的相关文章

  • Ubuntu 20.04 WARNING笔记(长期更新,欢迎交流)

    记录使用Ubuntu 20 04过程的报错 以及解决 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • 循环嵌套例题

    循环嵌套例题 1 例题1 span class token comment 代码 span span class token keyword for span span class token punctuation span span c
  • java实现学生信息管理(对象数组实现)

    java实现学生信息管理 xff08 对象数组实现 xff09 1 例题 实体类 学生类 id 姓名 xff0c 年龄 xff0c 性别 xff0c 成绩 需要使用数组保存学生信息 Student allStu 需要完成的方法 1 根据学生
  • java基础语法思维导图

    java从入门到放弃 简单总结之前的
  • 学生管理系统2.0 (可对学生数组扩容)

    学生管理系统2 0 可对学生数组扩容 1 用户可初始化数组长度 xff0c 不够用时可以扩充数组容量 尝试完成以下功能 实体类 学生类 id 姓名 xff0c 年龄 xff0c 性别 xff0c 成绩 需要使用数组保存学生信息 Studen
  • LinkedList和Set

    LinkedList和Set 1 LinkedList 1 1 LinkedList概述 底层存储数据是一个双向链表结构 自行车链子 就是一个生活中链表结构 xff0c 环环相扣 xff0c 替换 xff0c 拆除非常方便 1 2 Link
  • shiro与springboot整合

    Shiro 与 SpringBoot 的整合 1 创建SpringBoot工程 xff0c 导入依赖 span class token generics function span class token punctuation lt sp
  • Vue

    Author Thor Version 9 0 1 文章目录 一 Vue简介1 1 简介1 2 MVVM 模式的实现者 双向数据绑定模式1 3 其它 MVVM 实现者1 4 为什么要使用 Vue js1 5 Vue js 的两大核心要素1
  • android AudioRecord 音频录制 噪音消除

    android AudioRecord 音频录制 噪音消除 因为公司APP做适配 xff0c 一些低端机的噪音比较严重 xff0c 所以再一些低端机上做了简单除噪音功能 xff0c 1 xff0c 由于APP使用场景的限制 xff0c 所以
  • springboot 常用注解

    springboot 常用注解 在spring boot中 xff0c 摒弃了spring以往项目中大量繁琐的配置 xff0c 通过自身默认配置 xff0c 极大的降低了项目搭建的复杂度 在spring boot中 xff0c 大量注解的使
  • X86架构基本汇编指令详解

    文章目录 汇编指令伪指令1 MODEL2 STACK3 ENDP4 END 汇编指令1 MOV xff1a 将源操作数复制到目的操作数2 MOVZX 和 MOVSX3 XCHG 交换两个操作数内容4 INC 和 DEC5 ADD 和 SUB
  • 详解 C++ 对象模型

    文章目录 何为 C 43 43 对象模型 xff1f 基本 C 43 43 对象模型C 43 43 对象模型中加入单继承1 无重写的单继承2 有重写的单继承 C 43 43 对象模型中加入多继承C 43 43 对象模型中加入虚继承1 简单虚
  • Ubuntu 扩大/home磁盘分区

    在删除或重命名home目录之前 xff0c 千万确保你可以使用root账户 xff01 xff01 xff01 sudo 无用 xff01 xff01 xff01 根目录一共212G xff0c 已经使用了80 了 xff0c 其中130G
  • 正则表达式学习的个人小结

    正则表达式是对字符串的一种操作 xff0c 运用到JS中可以帮助我们去寻找符合要求的字符串 表单验证 http xff1a regexper com xff08 这个网站可以把正则表达式输入进去然后用图形显示出来 xff0c 因为是国外的网
  • ROS学习 一、Debian10安装ROS Noetic,解决rosdep update失败问题(更新一个可修改位置)

    目录 前言ROS安装1 添加ROS的apt源和key xff08 中科大源 xff09 2 apt安装ros noetic核心组件3 配置ROS的bash环境4 安装其他常用ROS依赖项5 解决python3 rosdep安装中出现的ros
  • windows动态链接库的使用,隐式调用(静态链接)和显示调用(动态链接)

    一 xff0c 动态链接库项目创建 dll工程内部架构 用VS2019创建动态链接库dll工程 初始会有如下几个文件 xff1a pch h和pch cpp与动态链接库功能无关 是用来保存多个文件时共同引用的头文件 xff0c 函数 xff
  • 在wsl2中安装CUDA

    1 先根据我之前的教程把wsl1升级到wsl2 wsl1升级到wsl2 夕阳之后的黑夜的博客 CSDN博客 2 打开Ubuntu xff0c 输入 uname r 确定内核 xff0c 安装CUDA需要内核 4 19 121 或更高 3 在
  • import torch 报错没有找到torch_python.dll

    conda install c anaconda intel openmp 运行上述代码就成功了
  • 在wsl2(Ubuntu20.04)中安装cudnn

    可以看cudnn的官方安装文档 xff1a 安装指南 NVIDIA 深度学习 cuDNN 文档 1 根据之前安装的cuda版本来下载安装对应的cudnn版本 cuDNN Archive NVIDIA Developer 2 比如我的cuda

随机推荐

  • 解决android opengl glReadPixels 慢的问题 三

    解决android opengl glReadPixels 慢的问题 三 使用2个pbo效率提上去了 xff0c 但是我手机分辨率是720p 或者1080p xff0c 我们手机相机使用一般是480x640 xff0c 这样通过gpu渲染到
  • 常用的损失函数

    pytorch的源码 xff1a torch nn PyTorch 1 11 0 documentation jittor的源码 xff1a jittor nn Jittor 1 3 2 6 文档 tsinghua edu cn paddl
  • Command ‘[‘where‘, ‘cl‘]‘ returned non-zero exit status 1.

    在环境变量中Path 那一项中添加两个路径 xff1a 在环境变量中新建一个LIB 变量 xff0c 并添加三个路径 xff08 记得加分号 xff09 xff1a 在环境变量中新建一个INCLUDE 变量 xff0c 并添加两个路径 xf
  • 尝试DCGAN+Self Attention

    先看一下DCGAN的G模型 xff1a 再看一下Self Attention的网络结构 xff1a 话不多说 xff0c 上代码 xff1a G D的model文件如下 xff1a import torch import torch nn
  • 2022基于GAN的去雾去雨论文

    目录 去雨CVPR2022 xff1a 使用双重对比学习的不成对深度图像去雨 去雨CVPR2021 xff1a 闭环 xff1a 通过解开图像转换生成和去除联合雨 去雨CVPR2021 xff1a 从雨水产生到雨水清除 去雾CVPR2020
  • Single image dehazing for a variety of hazescenarios using back projected pyramid network

    论文名 xff1a Single image dehazing for a variety of haze scenarios using back projected pyramid network 基于反投影金字塔网络的多雾场景单幅图像
  • Yolov7论文详解

    论文地址 xff1a https arxiv org pdf 2207 02696 pdf https arxiv org pdf 2207 02696 pdf 项目地址 xff1a WongKinYiu yolov7 Implementa
  • python logging 配置 输出 测试 根据时间生成日志文件,每天一个日志文件

    链接 https www cnblogs com xianyulouie p 11041777 html 链接 https www cnblogs com nancyzhu p 8551506 html 使用config惊醒logging配
  • 201809-3-元素选择器

    试题编号 xff1a 201809 3 试题名称 xff1a 元素选择器 时间限制 xff1a 1 0s 内存限制 xff1a 256 0MB 问题描述 xff1a import java util public class Main pu
  • VTK三维重建面绘制算法之MC表面重建

    面绘制 面绘制算法是基于表面实现的一种三维重建算法 该类算法实现的响应速度快 xff0c 对于一些实时的交互操作中不存在卡顿问题 xff0c 在日常使用中有助于提高处理效率 xff0c 但该算法在细节特征上的重建效果是不如体绘制方法 基于算
  • git clone 换源 / GitHub 国内镜像

    由于国内政策原因 xff0c 访问 GitHub 会被限制速度 xff0c 在 clone GitHub 上的代码时很容易出现连接超时的情况 xff0c 给无法翻墙的同学造成了很多困扰 xff0c 本博客旨在提供有效的国内镜像 xff0c
  • 安卓与串口通信-实践篇

    前言 在上一篇文章中我们讲解了关于串口的基础知识 xff0c 没有看过的同学推荐先看一下 xff0c 否则你可能会不太理解这篇文章所述的某些内容 这篇文章我们将讲解安卓端的串口通信实践 xff0c 即如何使用串口通信实现安卓设备与其他设备例
  • STM32复习之路——按键控制流水灯中断

    stm32复习之路1 STM32性能与结构 这里介绍的是STM32F103VET6 价格便宜 xff0c 实用性强 xff0c 其中的V为100引脚 xff0c E表示512K的FLASH xff0c T表示封装形式为LQFP xff0c
  • NXP LPC1768最小系统板Keil开发环境流程演示

    关键字 xff1a NXP LPC1768 最小系统 Keil MDK 开发环境 J Link 仿真器 概述 xff1a 以 MDK4 74版本配合 J Link 仿真器为例演示一下最小系统板的调试过程 首先运行 J Link Comman
  • ubuntu 下查看conda镜像源配置文件并修改

    查看源 xff1a conda config show sources root condarc为配置文件所在位置 xff0c 可以对其进行备份 cp condarc condarc bkp 然后修改 ls a vim condarc添加各
  • vscode+cmake配置普通c++项目

    目录 写在前面代码命令行编译与运行vscode配置编译与调试调试参考 写在前面 1 本文内容 vscode 43 cmake配置普通c 43 43 项目 2 平台 ubuntu vscode 3 转载请注明出处 xff1a https bl
  • Vscode 使用Remote-SSH 连接到虚拟机ubuntu18.04(以及遇到的错误和解决办法)

    vscode版本 xff1a 1 40 0 ubuntu xff1a 18 04 一 vscode安装remote ssh插件 二 设置要连接的主机IP地址和用户名 1 Crtl 43 P呼出命令栏 xff0c 输入 gt Remote S
  • 7、结构体之结构体数组

    结构体这块本来学着没有什么问题的 xff0c 但是 xff0c 有时候的学习不知道怎么的 xff0c 可能是课程进度有点快 xff0c 会让自己把前面的知识点与现学的联系起来 xff0c 从而使自己迷惑起来 好了 xff0c 先说问题 xf
  • 输入n个数字,并求出它们中间的最大值与最小值

    做题觉得简单 xff0c 拿着编译器一编程就各种小毛病出来了 xff0c 这样下去的进度就太慢了 既然是n个数 xff0c 那么肯定就要有输入 xff0c 定义一个数组a 5 来接收从键盘输入的数字 xff0c 怎么将接收的数值依次传入数组
  • 二叉树和堆(理论)

    树 1 树其实就是不包含回路的连通无向图 2 一棵树中的任意两个结点有且仅有唯一的一条路径连通 3 一棵树如果有n个结点 xff0c 那么它一定恰好有n 1条边 二叉树 二叉树是一种特殊的树 二叉树的特点是每个结点最多有两个儿子 xff0c