【数据结构】二叉树及其相关操作
二叉树的定义
二叉树是一个由结点构成的有限集合,这个集合或者为空,或者由一个根节点及两棵互不相交的分别称作这个根节点的左子树和右子树的二叉树组成。
二叉树并非一般的树形结构的特殊形式,它们是两种不同的数据结构。
二叉树与一般树形结构的主要区别在于
- 二叉树中每个非空结点最多只有两个子女,而一般的树形结构中每个非空结点可以有0到多个子女
- 二叉树中结点的子树要区分左子树和右子树,即是在结点只有一棵子树的情况下也要明确指出是左子树还是右子树。
满二叉树
如果一棵二叉树中所有终端节点均位于同一层次,且其他非终端结点的度数均为2
完全二叉树
如果一棵二叉树扣除其最大层次那层后即成为一棵满二叉树,且层次最大的那层的所有结点均向左靠齐
二叉树常用的存储结构有两种
顺序存储结构和链式存储结构。
这里主要介绍的二叉树的相关操作全部基于链式存储
链式储存方式下二叉树结点数据结构定义如下
typedef struct node
{char data;struct node *lchild,*rchild;
}bintnode;
根据前序遍历结果创建一棵二叉树
bintnode *createbintree()
{char ch;bintnode *t;if((ch=getchar())=='#')return NULL;else{t=(bintnode*)malloc(sizeof(bintnode));t->data=ch;t->lchild=createbintree();t->rchild=createbintree();}return t;
}
则输入:abd#e##fg###c##
二叉树的遍历
递归实现
1、前序遍历(根、左、右)abdefgc
void preorder(bintnode *t)
{if(t){printf("%c",t->data);preorder(t->lchild);preorder(t->rchild);}
}
2、中序遍历(左、根、右)debgfac
void inorder(bintnode *t)
{if(t){inorder(t->lchild);printf("%c",t->data);inorder(t->rchild);}
}
3、后序遍历(左、右、根)edgfbca
void postorder(bintnode *t)
{if(t){postorder(t->lchild);postorder(t->rchild);printf("%c",t->data);}
}
非递归实现
在采用非递归的方式遍历二叉树时,需要使用一个栈来记录回溯点,以便将来进行回溯。
顺序栈的定义及部分操作
typedef struct stack
{bintnode *data[100];int tag[100];//为栈中每个元素设置标记,用于后序遍历int top;
}seqstack;void push(seqstack *s,bintnode *t)//进栈
{s->data[s->top]=t;s->top++;
}bintnode *pop(seqstack *s)//出栈
{if(s->top!=0){s->top--;return (s->data[s->top]);}else{return NULL;}
}
1、前序遍历(根、左、右)
对于一棵树t,如果t非空,访问完t的根结点后,就进入t的左子树,但此时必须将t保存下来,以便访问完其左子树后,进入其右子树进行访问,即应该在t处设置一个回溯点,并将该回溯点保存于栈中。
void preorder(bintnode *t)
{seqstack s;s.top=0;while(t||(s.top!=0)){if(t){printf("%c",t->data);push(&s,t);t=t->lchild;}else{t=pop(&s);t=t->rchild;}}
}
2、中序遍历(左、根、右)
对于一棵树t,如果t非空,首先应该进入t的左子树访问,此时由于t的根节点及右子树尚未被访问,因此必须将t保存起来放入栈中,以便访问完其左子树后,从栈中取出t,进行其根结点及右子树的访问。
void inorder(bintnode *t)
{seqstack s;s.top=0;while(t||(s.top!=0)){if(t){push(&s,t);t=t->lchild;}else{t=pop(&s);printf("%c",t->data);t=t->rchild;}}
}
3、后序遍历(左,右,根)
因此对于一棵树t,如果t非空,首先应进入t的左子树访问,此时由于t的右子树及根结点尚未访问,因此必须将t保存起来,放入栈中,以便访问完其左子树后,从栈中取出t,进行其右子树及根结点的访问。这里需要注意的是,当一个元素置于栈顶即将处理时,其左子树的访问一定已经完成,如果其右子树不为空,则下面对右子树进行访问,而此时栈顶元素是不能出栈的,因为它作为根结点,其本身的值还尚未被访问;只有等到右子树也访问完成后,该栈顶元素才能出栈,并输出它的值。
所以必须用到seqstack类型中的tag数组,数组有0和1,由于标识栈中每个元素的状态。当一个元素进栈时,其对应的tag值为0;当它第一次处于栈顶即将被处理时,其tag值为0,意味着应该访问其右子树,于是将其右子树作为当前处理的对象,此时该栈顶元素仍然保留在栈中,并将其对应的tag值改为1;当其右子树访问完成以后, 该元素又一次的位于栈顶,而此时其tag值为1,意味着其右子树已经访问完成,接下来应该访问它本身,并将其出栈。
void postorder(bintnode *t)
{seqstack s;s.top=0;while(t||(s.top!=0)){if(t){push(&s,t);t=t->lchild;}else{if(s.tag[s.top-1]==0)//还没有访问右子树{t=s.data[s.top-1];s.tag[s.top-1]=1;t=t->rchild;}else//已经访问完右子树,现在访问根结点{t=pop(&s);printf("%c",t->data);t=NULL;}}}
}
注意一下,在else里的if语句中,最后有t=NULL,可以这样理解,在最后一个结点出栈以后是保存在t中的,如果没有把t置空,那么就不会退出while循环,导致死循环。
二叉树的其他运算实现(大部分为递归的方法,之后补充非递归的方法。)
二叉树的查找 locate( t,x)
返回的是二叉树t中值为x的结点的位置
首先与根结点的值进行比较,若相等,则返回指向根结点的指针;否则,进入t的左子树查找,若查找仍未成功,则进入t的右子树查找;在查找过程中,如果找到值为x的结点,则返回指向该结点的指针,否则就意味着该棵二叉树中没有无值为x的结点。(注意:在主函数中使用该函数时,或许要用到getchar()吸收前面的空格)
bintnode *locate(bintnode *t,char x)
{bintnode *p;if(t==NULL)return NULL;else{if(t->data==x)return t;else{p=locate(t->lchild,x);if(p) return p;elsereturn locate(t->rchild,x);}}
}
统计二叉树中的结点个数 numofnode()
int numofnode(bintnode *t)
{if(!t) return 0;else return numofnode(t->lchild)+numofnode(t->rchild)+1;
}
判断二叉树是否等价 isequal(t1,t2)
二叉树等价当且仅当其根结点的值相等,且其左右子树对应等价。判断两棵二叉树的左子树是否等价及判断两棵二叉树的右子树是否等价与判断两棵二叉树是否等价过程完全相同。
等价返回1,不等价返回0
int isequal(bintnode *t1,bintnode *t2)
{int t=0;if(t1==NULL && t2==NULL) return 1;else{if(t1!=NULL && t2!=NULL){if(t1->data==t2->data){if(isequal(t1->lchild,t2->lchild)){return isequal(t1->rchild,t2->rchild);}}}}return 0;
}
求二叉树的深度 depth(t)
如果该树是空树,那么返回0,否则其高度就是其左子树和右子树高度的最大值再加1。
int depth(bintnode *t)
{int h,lh,rh;if(t==NULL) h=0;else{lh=depth(t->lchild);rh=depth(t->rchild);h=lh>rh?lh+1:rh+1;}return h;
}
相关文章:
函数节流与函数防抖
什么是函数节流与函数防抖 举个栗子,我们知道目前的一种说法是当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放(以前是,现在不知道)中基本是以每秒 24 张的速…

makefile 中 =, :=, ?=, +=的区别
在Makefile中我们经常看到 : ? 这几个赋值运算符,那么他们有什么区别呢?我们来做个简单的实验 新建一个Makefile,内容为: ifdef DEFINE_VRE VRE “Hello World!” else endif ifeq ($(OPT),define) VRE ? “Hello W…

ubuntu 编译源码包 dsc diff.gz orig.tar.gz
2019独角兽企业重金招聘Python工程师标准>>> 1) 在获取源码包之前,确保在软件源配置文件/etc/apt/sources.list中添加了deb-src项以tree实用程序(以树型结构获取目录树)为例,介绍Ubuntu中如何管理源码包&am…

【ACM】杭电OJ 2552
本来还查了atan 和 atan2 的用法,结果总是WA 看了解析之后才知道原来是要公式推导,最后得出所求的式子是一个等式,结果为1。 所以,以后出类似与数学公式的题,可能是要手算推到,在输出特定的结果。&#x…

蚂蚁金服天街:OceanBase 在大促 5 年来的技术演进
为了与金融从业者、科技从业者共同探讨金融 业务的深层次问题,蚂蚁金服联手 TGO 鲲鹏会,在 12 月 8 日举办了「走进蚂蚁金服:双十一背后的蚂蚁金服技术支持」活动。蚂蚁金服高级技术专家天街为大家分享了《蚂蚁双 11 大促 OceanBase 核心技术…

OTA升级flash分区
什么是在线OTA升级 - OTA是Over-the-Air的简写,空中下载技术的意思。 - OTA在线升级在日常消费电子产品中很常见,比如手机,机顶盒等,通过网络,下载升级数据包,更新操作系统等底层固件进行…

MD5与Base64的思考
MD5加密是对任意长的数据使用MD5哈稀算法散列为4个32位组,若格式化为ASCII字符则为16字符,若格式化16进制表示,则为32字符. (MD5的具体算法请参阅相关书籍和资料)MD5广泛用于数据校验和完整性检验.且不可逆.理论上为抗碰撞的在2004年8月17日,MD5遭遇重创,山东大学的王小云做了…
【ACM】杭电OJ 1076
数组要开的大一些,一开始数组只开到100005,就显示了错误的数据 AC代码: #include <iostream> #include <cstring> using namespace std; const int maxn 10000005; int a[maxn]; int main () {int i;memset(a,0,sizeof(a));fo…

IDEA ctrl+alt+L 格式化快捷键无效时解决
这几天发现自己Intellij IDEA ctrlaltL格式化代码无效 设置里面按照快捷键搜索 按了 ctrlaltL 也没反应 但是我设置的确实是默认的 ctrlaltL 最后终于找到了问题所在 原来是开网易云音乐的锅 网易云会有一个全局的快捷键ctrlaltL跟idea冲突 去网易云关了就好了 转载于:https:/…

gpio pin和pad的区别
PIN指芯片封装好后的管脚,即用户看到的管脚; PAD是硅片的管脚,是封装在芯片内部的,用户看不到。 PAD到PIN之间还有一段导线连接的。
【ACM】杭电OJ 1013
WA代码 输入很大的数的时候会输出“-1”,所以考虑用字符数组来储存输入的数据。 #include <iostream> #include <cstring> #include <cstdio> using namespace std; long long sum; long long fun (int n) {sum0;if(n<9) return n;while(n){s…

\\s+ split替换
出自: http://www.tuicool.com/articles/vy2ymm 详解 "\\s" 正则表达式中\s匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v] \f -> 匹配一个换页\n -> 匹配一个换行符\r -> 匹配一个回车符\t -> 匹配一个制表…

ubuntu18.04下双机驱动调试
环境搭建:https://blog.51cto.com/haidragon/2337256这里要先说下如果要下内核断点要先在编译前去掉写保护,但是下自己写的驱动可以不要。第二个最好编译完后压缩vm系统文件然后复制一份,这样就调试机与被调试机环境一模一样,同样…

如何独立开发一个网络请求框架
(原创出处为本博客:http://www.cnblogs.com/linguanh/) 目录: 前言 准备工作 开发模式 开发原则 线程 高并发 TCP/UDP 本类介绍 开发选择 功能列表 优点 拓展 完整代码 用法例子 前言: 已开源到GitHub,希望…
【ACM】杭电OJ 1284(待更)
#include<iostream> using namespace std; int main(){int n;while(cin>>n){int ans0; for(int i0;i<n/3;i){ //对3的个数进行枚举 int temp(n-3*i); //除了这i个3之外剩余的钱数 //temp/2,剩余部分换成2的总种类数,anstemp/21; //这…

c语言头文件中定义inline static相关函数的优劣
头文件中常见static inline函数,于是思考有可能遇到的问题,如头文件经常会被包含会不会产生很多副本?网上说法不一。于是自己验证。经过arm-none-eabi-gcc下测试后得出结论。 inline 关键字实际上仅是建议内联并不强制内联,gcc中O…

c语言inline详解
本文介绍了GCC和C99标准中inline使用上的不同之处。inline属性在使用的时候,要注意以下两点:inline关键字在GCC参考文档中仅有对其使用在函数定义(Definition)上的描述,而没有提到其是否能用于函数声明(Dec…

【ACM】杭电OJ 2090
题目中给出的四舍五入的条件可以忽略不计了,因为提交的程序没有考虑四舍五入,照样AC了 printf("%.1lf\n",sum); AC代码: 写的有点复杂了,其实不用定义结构体也可以。 #include<iostream> #include <cstdi…

属性配置文件详解(2)(十七)
过命令行设置属性值 相信使用过一段时间Spring Boot的用户,一定知道这条命令:java -jar xxx.jar --server.port8888,通过使用–server.port属性来设置xxx.jar应用的端口为8888。 在命令行运行时,连续的两个减号--就是对applicatio…

git track远程分支
在本地初始化仓库,提交代码时会出现,上游为空,当前分支为选择,等错误提示。其实就是本地仓库分支和远程仓库分支并未进行关联,即本地分支未追踪到远程分支。 1.本地和远程的状态 本地: 本地所有的文…

HTMLDOM中三种元素节点、属性节点、文本节点的测试案例
HTML dom中常用的三种节点分别是元素节点、属性节点、文本节点。 具体指的内容可参考下图: 以下为测试用例: <!DOCTYPE html> <html><head><title>元素节点、属性节点、文本节点的测试</title><meta name"Author" conte…

【ACM】DFS 全排列 回溯
深入体会一下DFS,回溯 在一些OJ上endl和“\n”还是有区别的!!! 题目链接:http://codevs.cn/problem/1294/ 方法一: #include <iostream> #include <cstdio> #include <cstring> usin…

(轉貼) 友達光電第五屆【A+種子暑期實習計畫】開始辦理報名 (News)
友達光電第五屆【A種子暑期實習計畫】開始辦理報名 友達光電以絕佳的團隊執行力,帶領台灣光電產業進入世界級的領域! 還在就學的你/妳,想成為世界級光電產業的A種子嗎? 把握最後的暑假加入友達的A種子實習團隊吧!! 【2008 A種子募集計畫】 實習期間&am…

binutils工具集用法
addr2line用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号。 在不少嵌入式开发环境中,编译器的名称往往不是gcc,而是想arm-rtems-gcc这样的,对于这种命名形式的编译器,读者通常可以找到arm-rtems-add…

【ACM】CODE[VS] 1215 (DFS)
题目描述 Description 在N*N的迷宫内,“#”为墙,“.”为路,“s”为起点,“e”为终点,一共4个方向可以走。从左上角((0,0)“s”)位置处走到右下角((n-1,n-1)“e”)位置处…

#大学#SQL基础学习笔记(02)
*数据分组select FAge,count(*) from TableName group by FAge (根据年龄进行分组)一般和聚合函数一起使用 *Having语句select FAge,count(*) from TableName group by FAge having count(*)>1 *聚合函数不能出现在where语句中 *having是对分组后的信息进行过滤,…

socket connect阻塞和非阻塞处理
建立socket后默认connect()函数为阻塞连接状态,在大多数实现中,connect的超时时间在75s至几分钟之间,想要缩短超时时间,可解决问题的两种方法:方法一、将socket句柄设置为非阻塞状态,方法二、采用信号处理函…

【ACM】CODE[VS] 2806(DFS)
感觉有点入了DFS的门槛,距离完全掌握还差得远呢 AC代码:运行时间为7ms #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn 26; int vis[maxn][maxn],col,line,flag,count; char map[…

scala学习手记34 - trait方法的延迟绑定
trait的方法的延迟绑定就是先混入的trait的方法会后调用。这一点从上一节的实例中也可以看出来。 下面再来看一个类似的例子: abstract class Writer {def write(message: String): String }trait UpperWriter extends Writer {abstract override def write(message…

(原創) Altera Technology Roadshow 2011 Taipei (SOC) (Quartus II) (Nios II) (Qsys)
Abstract這是我第一次參加Altera一年一度的Technology Roadshow。 Introduction 今年的Altera Technology Roadshow是在台北喜來登大飯店舉行。 位置在喜來登飯店的B2。 講師的講台。 當天的會場布置。 當天的演講內容,主要是台灣Altera兩家代理商Galaxy與Arrow的FA…