二叉树的链式结构和顺序结构的增删查改

树的概念

树是一种非线性的数据结构,它是一个n个节点组成的具有层次关系的集合,一棵树由一个根节点和若干个其余节点构成,除了根节点外,其他的节点都由一个前驱和多个后继,而根节点可以有多个后继,但没有前驱,树由递归定义,并且子树不能有交集,否则就不是树。在这里插入图片描述

相关知识

节点的度:一个节点含有子树的个数叫做节点的度,如上图的B节点度为2
叶子节点:节点度为0的节点叫做叶子节点,如上图的G
分支节点:度不为0的节点,如上图的B
双亲节点:若一个节点拥有子节点,那这个节点就称为他子节点的双亲节点,如上图的B为D,E的双亲节点
孩子节点:一个节点含有多个子节点,那么这些子节点就是孩子节点,如上图的DE就是B的孩子节点
兄弟节点:有相同双亲节点的节点,如上图的D和E
树的度:一棵树里最大节点的度称为树的度,上图树的度为2
节点的层次:从根开始,根为第一层,以此类推,上图G在第4层
树的高度:最深的那个节点的层数,上图树的高度为4
堂兄弟节点:双亲节点在同一层称为堂兄弟节点,上图D和F,E和F为堂兄弟节点
节点的祖先:树的根,上图A为节点的祖先
子孙:除了根节点外的其余节点都是根节点的子孙
森林:由数量大于大于1的树构成的为森林

树的表示法

1.双亲表示法
用顺序表进行保存,顺序表每个位置存储的是节点的名称和双亲节点的位置,根节点没有双亲节点,其双亲的节点下标为-1.
在这里插入图片描述
2.左孩子右兄弟表示法
不管一个节点有多少个孩子,这个节点的指针都只指向在第一个孩子,兄弟指针指向右边的兄弟节点
如下图
在这里插入图片描述

二叉树概念及其表示法

概念

二叉树由一个根节点和两根分别称为左子树和右子树的二叉树构成或者没有节点构成,二叉树不存在度数大于2的节点,且二叉树的子树有左右之分,顺序不能颠倒,所以二叉树是一个有序树。
满二叉树:一个二叉树每一层的节点数都达到最大值,也就是说一个k层的满二叉树的节点数为2^k-1
完全二叉树:满二叉树是一种特殊的完全二叉树,完全二叉树的度不能为1,若一个完全二叉树有k层,前k-1层是满二叉树,最后一层可以不满,但从左到右必须是连续的,节点数最少有2 ^ (k-1),最多有2 ^ n - 1个

二叉树的存储结构

顺序存储

顺序存储就是用数组来存储,一般数组适合完全二叉树,因为不是完全二叉树就会导致空间的浪费,而二叉树的顺序存储在物理结构上是一个数组,在逻辑结构上是一棵树。
完全二叉树
在这里插入图片描述
不完全二叉树会有一些空的格子

链式存储

用链表来表示一棵二叉树,包括数据和左右指针,链式结构分为二叉链(两个孩子)或者三叉链(两个孩子和一个父指针),二叉树用的是二叉链,红黑树的学习要用到三叉链。

顺序存储中父亲和孩子的下标存在一个关系,设a为父亲的下标,b为左孩子的下标,c为右孩子的下标,则b=2a+1,c=2a+2,同理知道孩子的下标可以找父亲的节点,不管是左孩子还是右孩子,父亲的节点都可以由parent=(child-1)/2, 因为有一个取整的问题。

堆是一棵完全二叉树,分为大根堆(任何一个父亲都大于等于孩子)和小根堆(任何一个父亲都小于等于孩子),主要应用于堆排序,topk问题和优先级队列

结构
typedef int valuetype;
typedef struct heap
{valuetype* arr;int size;int capacity;
}heap;
初始化
void init(heap* hp)
{assert(hp);hp->arr = (valuetype*)malloc(sizeof(valuetype) * 4);hp->size = 0;hp->capacity = 4;
}
插入
void push(heap* hp, valuetype x)
{assert(hp);//检查空间是否足够if (hp->size == hp->capacity){hp->capacity *= 2;valuetype* tmp = (valuetype*)realloc(hp->arr,sizeof(valuetype) * hp->capacity);if (tmp == NULL){perror("realloc fail");return;}hp->arr = tmp;}//插入数据hp->arr[hp->size] = x;hp->size++;//向上调整算法,这里是大堆//for (int i = hp->size-1; i > 0; i = (i - 1) / 2)//{//	int j = (i - 1) / 2;//	if (hp->arr[i] > hp->arr[j])//若是小堆把这一行的大于号改成小于号//	{//		valuetype tmp = hp->arr[i];//		hp->arr[i] = hp->arr[j];//		hp->arr[j] = tmp;//	}//	else//	{//		break;//	}//}AdjustUp(hp->arr, 0, hp->size-1);//也可以写一个向上调整算法直接调用
}
销毁
void destroy(heap* hp)
{assert(hp);free(hp->arr);hp->arr = NULL;hp->capacity = hp->size = 0;
}
删除
void pop(heap* hp)//删除堆顶的数据
{//首尾数据交换assert(hp);assert(hp->size);valuetype tmp = hp->arr[hp->size - 1];hp->arr[hp->size - 1] = hp->arr[0];hp->arr[0] = tmp;//删除最后一个数据hp->size--;//向下调整AdjustDown(hp->arr, 0, hp->size);//这种方法能最大的提高效率,因为不需要重新建堆
}
向上调整算法
void AdjustUp(valuetype* arr, int begin, int end)
{for (int i = end; i > begin; i = (i - 1) / 2){int j = (i - 1) / 2;if (arr[i] > arr[j])//这里是大堆,若是小堆把这一行的大于号改成小于号{valuetype tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}else{break;}}
}
向下调整算法
void AdjustDown(valuetype* arr, int begin, int end)
{int parent = begin;int child = 2 * parent + 1;while (child < end){if (child + 1 < end && arr[child] < arr[child + 1])//这里是大堆{child += 1;}if (arr[child] >= arr[parent]){valuetype tmp = arr[child];arr[child] = arr[parent];arr[parent] = tmp;}else{break;}parent = child;child = 2 * parent + 1;}
}
堆顶元素
valuetype top(heap* hp)
{assert(hp);assert(hp->size);return hp->arr[0];
}
topk问题

eg.有一万个数,想找出最大的前十个,但排序太浪费资源了,我们可以用这个数组中前10个数建堆,然后再用剩余10000-10个元素与堆顶元素交换,不满足条件就替换
我们可以建大堆,然后pop k次获得所需元素,但topk问题有一个特殊情况,当N太大 的时候不能使用这个思路,因为要开的空间太大了,比如说N为十亿,换算出来的数组需要4g的空间,这种就不能存在内存上,所以我们需要一个新思路:前k个数建小堆,然后N-k个数依次与建成的小堆比较,如果比堆顶的数据大,就替换它进堆,

void topk(int* a, int k,int sz)
{//先用前k个数据建堆heap hp;init(&hp);for (int i = 0; i < k; i++){push(&hp, a[i]);}for (int i = k; i < sz; i++){if (hp.arr[0] < a[i]){hp.arr[0] = a[i];AdjustDown(hp.arr, 0, hp.size-1);}}//所获得的堆里就是最大的前十个数
}//上面的向下调整算法应该变号
堆排

法1:有空间复杂度的消耗

void heapsort(int* arr,int n)
{//建堆heap hp;init(&hp);for(int i=0;i<n;i++){push(&hp,arr[i]);}for(int i=0;i<n;i++){int tmp=top(&hp);pop(&hp);arr[i]=tmp;}destroy(&hp);
}

法2:对数组建堆,向上调整建堆或者向下调整建堆

void heapsort(int* arr, int n)//向上调整
{//向上调整建堆//for (int i = 0; i < n; i++)//{//	AdjustUp(arr, 0, i);//}//向下调整建堆for(int i=(n-1-1)/2;i>=0;i--){AdjustDown(arr,i,n-1);//时间复杂度优于向上调整建堆,可以通过差比数列算时间复杂度}//升序建大堆//交换第一个数据和最后一个数据,向下调整建堆for (int i = n-1; i > 0; i--){//swap(arr[0], arr[i - 1]);int tmp = arr[0];arr[0] = arr[i];arr[i] = tmp;AdjustDown(arr, 0, i);}
}

链式二叉树

遍历

1.前序:根 左子树 右子树的顺序遍历
2.中序:左子树 根 右子树的顺序遍历
3.后序:左子树 右子树 根的顺序遍历
4.层序:一层一层遍历
在这里插入图片描述
上图的前序遍历是:1 2 3 N N N 4 5 N N 6 N N
中序是:N 3 N 2 N 1 N 5 N 4 N 6 N
后序是: N N 3 N 2 N N 5 N N 6 4 1
N表示空树

链式二叉树的结构
typedef int valuetype;
typedef struct BinaryNode
{valuetype data;struct BinaryNode* left;struct BinaryNode* right;
}BTNode;
申请新节点
BTNode* BuyNode(valuetype x)
{BTNode* newnode=(BTNode*)malloc(sizeof(BTNode));if(newnode==NULL){perror("malloc fail");return;}newnode->left=NULL;newnode->right=NULL;newnode->data=x;return newnode;
}
前序遍历
void PrevOrder(BTNode* root)
{if(root==NULL){printf("N");return;}printf("%d",root->data);PrevOrder(root->left);PrevOrder(root->right);
}
中序遍历
void InOrder(BTNode* root)
{if(root==NULL){printf("N");return;}PrevOrder(root->left);printf("%d",root->data);PrevOrder(root->right);
}
后序遍历
void AfterOrder(BTNode* root)
{if(root==NULL){printf("N");return;}PrevOrder(root->left);PrevOrder(root->right);printf("%d",root->data);
}
层序遍历

用队列实现

void levelorder(BTNode* root)
{queue obj;init(&obj);//初始化队列if(root){push(&obj,root);}while(!isempty(&obj)){BTNode* tmp=pop(&obj);//这里的pop会返回队头数据printf("%d",tmp->data);if(tmp->left){push(&obj,tmp->left);}if(tmp->right){push(&obj,tmp->right);}}
}
销毁
void destroy(BTNode* root)
{if(root==NULL){return;}destroy(root->left);destroy(root->right);free(root);//后序
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3270054.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

探索 Blockly:自定义积木实例

3.实例 3.1.基础块 无输入 , 无输出 3.1.1.json var textOneJson {"type": "sql_test_text_one","message0": " one ","colour": 30,"tooltip": 无输入 , 无输出 };javascriptGenerator.forBlock[sql_test_te…

没打印机怎么打印东西?

在日常生活中&#xff0c;我们经常会遇到需要打印文件的情况&#xff0c;无论是学习资料、工作文档&#xff0c;还是个人兴趣的资料收集。然而&#xff0c;并不是每个人家里都有打印机&#xff0c;或者打印机出现了故障。在这种情况下&#xff0c;寻找一个高效、经济的打印途径…

通过 C# 写入数据到Excel表格

Excel 是一款广泛应用于数据处理、分析和报告制作的电子表格软件。在商业、学术和日常生活中&#xff0c;Excel 的使用极为普遍。本文将详细介绍如何使用免费.NET库将数据写入到 Excel 中&#xff0c;包括文本、数值、数组、和DataTable数据的输入。 文章目录 C# 在Excel单元格…

Spring AOP(概念、实战、原理)

文章目录 1. 什么是AOP2. AOP体系图3. 术语解释3.1 Aspect&#xff08;切面&#xff09;3.2 Join point&#xff08;连接点&#xff09;3.3 Advice&#xff08;通知&#xff09;3.4 Pointcut&#xff08;切入点&#xff09;3.5 Target&#xff08;目标&#xff09;3.6 Proxy&am…

文本解码原理--MindNLP

前言 根据前文预测下一个单词 一个文本序列的概率分布可以分解为每个词基于其上文的条件概率的乘积 Greedy search 在每个时间步&#x1d461;都简单地选择概率最高的词作为当前输出词: &#x1d464;&#x1d461;&#x1d44e;&#x1d45f;&#x1d454;&#x1d45a;&am…

【泛微E9】统一待办中心集成

1、什么是统一待办中心集成 概述&#xff1a;目前有很多第3方系统都有流程&#xff0c;操作人都会有待办事宜、已办事宜。但这些待办流程都分散在不同系统中&#xff0c;用户操作不方便&#xff0c;对相应流程也无法及时处理。客户希望能在泛微OA中对所有的第3方流程做统一展示…

LlamaIndex:向 LLM 添加个人数据

LlamaIndex 是您构建基于 LLM 的应用程序的友好数据助手。您可以使用自然语言轻松地获取、管理和检索私有数据和特定领域的数据。 LlamaIndex 是一个针对大型语言模型 (LLM) 应用程序的数据框架。GPT-4 等 LLM 在海量的公共数据集上进行预训练&#xff0c;开箱即用即可实现令人…

【.NET 6 实战--孢子记账--从单体到微服务】--开发环境设置

在这一小节&#xff0c;我们将设置开发环境。 一、安装SDK 咱们的项目使用的是 .NET6&#xff0c;开发前我们需要从官网上下载.NET6 SDK&#xff08;点击下载&#xff09;&#xff0c;这里要注意的是我们需要下载.NET6 SDK&#xff0c;而不是 .NET6 Runtiem 。SDK 包含 Runti…

PyCharm 常用 的插件

Material Theme UI Lite&#xff1a;‌提供多种不同的页面风格&#xff0c;‌为PyCharm界面增添个性化元素。‌Chinese (Simplified) Language Pack&#xff1a;‌为中文用户提供简体中文的界面、‌菜单、‌提示信息&#xff0c;‌提升使用体验。‌Tabnine&#xff1a;‌基于人…

C# 开发监控方法执行耗时

MethodTimer.Fody 是一个功能强大的库,可以用于测量 .NET 应用程序中的方法的执行时间。允许你在不修改代码的情况下,自动地测量和记录方法的执行时间。 这个工具是基于.NET的 weaving 技术,通过修改IL(Intermediate Language,中间语言)代码来插入计时逻辑,从而在方法调…

Mac应用快速启动器:Alfred 5 for Mac 激活版

Alfred 5 是一款专为 macOS 系统设计的效率提升工具。这款软件以其快速启动和高效操作功能著称&#xff0c;通过使用快捷键来呼出输入界面&#xff0c;用户可以快速完成各种任务。 最新版本 Alfred 5.5 引入了一些新功能。其中包括整合了 ChatGPT 和 DALL-E&#xff0c;这意味…

ROS2入门到精通—— 2-13 ROS2实战:实现机器人多目标点导航(附ROS C++代码以及脚本实现)

0 前言 实现机器人多目标点导航是非常常见的需求,本文将介绍ROS2和Shell脚本两个方法实现多目标点导航,读者可以根据需求对接仿真和实车。 1 yaml-cpp介绍 YAML是专门用来写配置文件的语言,实质上是一种通用的数据串行化格式 1.1 yaml-cpp安装 git clone https://github…

kail-linux如何使用NAT连接修改静态IP

1、Contos修改静态IP vi /etc/sysconfig/network-scripts/ifcfg-ens33&#xff0c; 标记红色处可能序号会变动 参考linux配置网络不通解决方案_kylinv10sp2 网关不通-CSDN博客https://tanrt06.blog.csdn.net/article/details/132430485?spm1001.2014.3001.5502 Kail时候NAT连…

Java SE 文件上传和文件下载的底层原理

1. Java SE 文件上传和文件下载的底层原理 文章目录 1. Java SE 文件上传和文件下载的底层原理2. 文件上传2.1 文件上传应用实例2.2 文件上传注意事项和细节 3. 文件下载3.1 文件下载应用实例3.2 文件下载注意事项和细节 4. 总结&#xff1a;5. 最后: 2. 文件上传 文件的上传和…

ElasticSearch(三)—文档字段参数设置以及元字段

一、 字段参数设置 analyzer&#xff1a; 指定分词器。elasticsearch 是一款支持全文检索的分布式存储系统&#xff0c;对于 text类型的字段&#xff0c;首先会使用分词器进行分词&#xff0c;然后将分词后的词根一个一个存储在倒排索引中&#xff0c;后续查询主要是针对词根…

模拟实现c++中的vector模版

目录 一vector简述&#xff1a; 二vector的一些接口函数&#xff1a; 1初始化&#xff1a; 2.vector增长&#xff1a; 3vector增删查改&#xff1a; 三vector模拟实现部分主要函数&#xff1a; 1.size,capacity,empty,clear接口&#xff1a; 2.reverse的实现&#xff1…

springboot招生宣传管理系统论文源码调试讲解

2 相关技术 2.1 VUE介绍 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目…

Github 2024-07-27开源项目日报 Top10

根据Github Trendings的统计,今日(2024-07-27统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量非开发语言项目2C++项目2C项目2TypeScript项目1JavaScript项目1Java项目1Python项目1C#项目1免费编程学习平台:freeCodeCamp.org 创建周期:33…

SpringBoot入门实战:SpringBoot整合Shiro

1.背景介绍 SpringBoot是一个用于快速开发Spring应用程序的框架。它的核心是对Spring框架的一层封装&#xff0c;使其更加简单易用。SpringBoot整合Shiro是一种将SpringBoot与Shiro整合的方法&#xff0c;以实现身份验证和授权功能。 Shiro是一个强大的Java安全框架&#xff0c…

电影院售票网站

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM框架 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 用户管理界面 正在上映管…