二叉树性质及遍历

一、二叉树的定义

  树的每个结点至多只有二棵子树(不存在度大于2的结点),树的子树有左右之分,次序不能颠倒。

二、二叉树的性质

(1) 在非空二叉树中,第i层的结点总数不超过 , i>=1;
(2) 深度为h的二叉树最多有 个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树的深度为
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;
如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。
(6)给定N个节点,能构成h(N)种不同的二叉树。
h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。

三、二叉树的遍历

二叉树的遍历顺序分为三种:先序遍历、中序遍历和后序遍历。而遍历方式又分为递归遍历和非递归遍历。

1.先序遍历

  前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。

a.递归实现:

1 void preOrder1(BinTree *root)     //递归前序遍历 
2 {
3     if(root!=NULL)
4     {
5         cout<<root->data<<" ";
6         preOrder1(root->lchild);
7         preOrder1(root->rchild);
8     }
9 }

b.非递归实现:

 根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:

     对于任一结点P:

     1)访问结点P,并将结点P入栈;

     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

     3)直到P为NULL并且栈为空,则遍历结束。

 1 void preOrder2(BinTree *root)     //非递归前序遍历 
 2 {
 3     stack<BinTree*> s;
 4     BinTree *p=root;
 5     while(p!=NULL||!s.empty())
 6     {
 7         while(p!=NULL)
 8         {
 9             cout<<p->data<<" ";
10             s.push(p);
11             p=p->lchild;
12         }
13         if(!s.empty())
14         {
15             p=s.top();
16             s.pop();
17             p=p->rchild;
18         }
19     }
20 }

2.中序遍历

中序遍历按照“左孩子-根结点-右孩子”的顺序进行访问。

a.递归实现

1 void inOrder1(BinTree *root)      //递归中序遍历
2 {
3     if(root!=NULL)
4     {
5         inOrder1(root->lchild);
6         cout<<root->data<<" ";
7         inOrder1(root->rchild);
8     }
9 }

b.非递归实现

根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:

   对于任一结点P,

  1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;

  2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;

  3)直到P为NULL并且栈为空则遍历结束

 1 void inOrder2(BinTree *root)      //非递归中序遍历
 2 {
 3     stack<BinTree*> s;
 4     BinTree *p=root;
 5     while(p!=NULL||!s.empty())
 6     {
 7         while(p!=NULL)
 8         {
 9             s.push(p);
10             p=p->lchild;
11         }
12         if(!s.empty())
13         {
14             p=s.top();
15             cout<<p->data<<" ";
16             s.pop();
17             p=p->rchild;
18         }
19     }    
20 }

3.后序遍历

后序遍历按照“左孩子-右孩子-根结点”的顺序进行访问。

a.递归实现

1 void postOrder1(BinTree *root)    //递归后序遍历
2 {
3     if(root!=NULL)
4     {
5         postOrder1(root->lchild);
6         postOrder1(root->rchild);
7         cout<<root->data<<" ";
8     }    
9 }

b.非递归实现

  后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。

  要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

 1 void postOrder3(BinTree *root)     //非递归后序遍历
 2 {
 3     stack<BinTree*> s;
 4     BinTree *cur;                      //当前结点 
 5     BinTree *pre=NULL;                 //前一次访问的结点 
 6     s.push(root);
 7     while(!s.empty())
 8     {
 9         cur=s.top();
10         if((cur->lchild==NULL&&cur->rchild==NULL)||
11            (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
12         {
13             cout<<cur->data<<" ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
14               s.pop();
15             pre=cur; 
16         }
17         else
18         {
19             if(cur->rchild!=NULL)
20                 s.push(cur->rchild);
21             if(cur->lchild!=NULL)    
22                 s.push(cur->lchild);
23         }
24     }    
25 }

四、参考资料

http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html

 

转载于:https://www.cnblogs.com/wangjzh/p/4695700.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用最新Apache解析漏洞(CVE-2017-15715)绕过上传黑名单

目标环境&#xff1a; 比如&#xff0c;目标存在一个上传的逻辑&#xff1a; <?php if(isset($_FILES[file])) {$name basename($_POST[name]);$ext pathinfo($name,PATHINFO_EXTENSION);if(in_array($ext, [php, php3, php4, php5, phtml, pht])) {exit(bad file);}mo…

将数据压缩到数据结构中

这个故事是关于我们最近在Plumbr进行的容量优化任务。 一切始于将无害的要求添加到现有组合中。 如您所知&#xff0c;Plumbr监视解决方案作为连接到服务器的Java代理分发。 只需少量添加即可跟踪一段时间内所有已连接的代理&#xff0c;以便可以实时回答以下问题&#xff1a;…

CVE-2017-15715漏洞复现

复现环境 docke apache 2.4.0到2.4.29即可 php5.5 复现过程 先在物理机上创建目录 mkdir -p /var/www/html 然后创建个容器&#xff0c;并关联物理机的/var/www/html目录 docker run -d -v /var/www/html:/var/www/html -p 8080:80 --name apache php:5.5-apache 再把物理机的/…

Linux网络流量实时监控ifstat iftop命令详解

ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstateth0 eth1 KB/s in KB/s out KB/s in KB/s out0.07 0.20 0.00 0.000.07 0.15 0.58 0.00 默认ifstat不监控回环接口&#xff0c;…

一种移动端自适应屏幕的方法

前端移动端开发的时候肯定是会面对不同型号的手机的页面展示问题的&#xff0c;今天给大家推出另外一种自适应不同移动端的方法&#xff0c;使用vw&#xff0c;vh单位。 vw和vh单位的大小是多少&#xff1f; vw和vh是根据设备的宽度和高度来决定的&#xff0c;设备的宽就是10…

Metasploit--后渗透(一些基本操作方法)

查看主机是否运行在虚拟机上 run post/windows/gather/checkvm关闭杀毒软件 拿到目标主机的shell后第一件事就是关闭掉目标主机的杀毒软件&#xff0c;通过命令 run killav获取目标主机的详细信息 run scraper它将目标机器上的常见信息收集起来然后下载保存在本地 Meterpre…

端到端测试_端到端测试的滥用–测试技术2

端到端测试我的上一个博客是有关测试代码方法的一系列博客中的第一篇&#xff0c;概述了使用一种非常常见的模式从数据库检索地址的简单方案&#xff1a; …并描述了一种非常通用的测试技术&#xff1a; 不编写测试 &#xff0c; 而是手动进行所有操作。 今天的博客涵盖了另一…

Metasploit 之生成木马(msfvenom)

msfvenom参数 相关参数介绍 -p, --payload <payload> 指定需要使用的payload(攻击荷载) -l, --list [module_type] 列出指定模块的所有可用资源,模块类型包括: payloads, encoders, nops, all -n, --nopsled <length> 为payload预先指定一个NOP滑动长度 -f, --for…

自学前端的误区和痛点解决办法

网上有很多自学的方案路线&#xff0c;还有一些知道&#xff1b;但是我感觉他们很多人都是没有真实学习过的&#xff0c;很多人说的那些路线&#xff0c;真的是扯淡&#xff1b;肯定不是自己通过自学后的感悟&#xff0c;他们所谓的路线可能是结合培训班大纲和知乎的一些答案组…

指纹识别工具(CMSeek)

你喜欢上别人挺好的啊&#xff0c;不然我总觉得我们还有可能。。。 ---- 网易云热评 介绍&#xff1a;一款扫描CMS相关信息的软件&#xff0c;该软件可进行170多个CMS的基本检测&#xff0c;可检测Drupal版本信息&#xff0c;可扫描多个站点 下载地址&#xff08;软件作者&…

kafka监控工具kafkaOffsetMoniter的使用

简介 KafkaOffsetMonitor是由Kafka开源社区提供的一款Web管理界面&#xff0c;用来实时监控Kafka的Consumer以及Partition中的Offset&#xff0c;可以在web界面直观的看到每个Partition的Message的增长速度&#xff0c;是否消费&#xff0c;是否阻塞等。 使用 如果不想编译&…

前端开发攻城狮必须知道的开发环境和插件

前端开发&#xff0c;做到后面&#xff0c;是可以走很多方向的&#xff1b;但是要保证后期的平滑过度&#xff0c;前期还是要把一些必须的知识搞扎实的&#xff1b;下面是我根据自己学习的感悟&#xff0c;写的一些东西&#xff1b;一个网站的流程,由前端工程师 使用 HTMLCSSJa…

hibernate示例_通过示例Hibernate–第1部分(删除孤儿)

hibernate示例所以我想做一系列的冬眠例子&#xff0c;展示冬眠的各种特征。 在第一部分中&#xff0c;我想展示有关删除孤儿功能及其在故事情节中的使用方式。 因此&#xff0c;让我们开始:) 先决条件 &#xff1a; 为了尝试以下示例&#xff0c;您将需要以下提到的JAR文件&…

docker更换国内镜像源

国内下载docker镜像大部分都比较慢&#xff0c;下面给大家介绍2个镜像源。 一、阿里云的docker镜像源 注册一个阿里云用户,访问 https://cr.console.aliyun.com/#/accelerator 获取专属Docker加速器地址 使用的时候修改/etc/docker/daemon.json文件就可以了&#xff0c;修改保…

(转)搞定DC/DC电源转换方案设计,必看金律十一条

[导读] 搞嵌入式的工程师们往往把单片机、ARM、DSP、FPGA搞的得心应手&#xff0c;而一旦进行系统设计&#xff0c;到了给电源系统供电&#xff0c;虽然也能让其精心设计的程序运行起来&#xff0c;但对于新手来说&#xff0c;有时可能效率低下&#xff0c;往往还有供电电流不足…

ThinkPHP 5.0.x、5.1.x、5.2.x 全版本远程命令执行漏洞

ThinkPHP 5.0.x、5.1.x、5.2.x 全版本远程代码执行漏洞 漏洞概述: ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架。借鉴Struts框架的Action对象&#xff0c;同时也使用面向对象的开发结构和MVC模式。1月11日&#xff0c;ThinkPHP官方发布新版本5.0.24&#xff0c…

CSDN如何上传视频?

最近想在CSDN上面上传视频&#xff0c;发现找不到入口&#xff0c;包含APP和PC端的博客&#xff0c;无奈之下只能咨询客服&#xff0c; 这个是需要提前申请讲师资格&#xff0c;需要3年以上相关经验。 发布纯IT类视频&#xff0c;先申请讲师&#xff0c;讲师申请 按照提示信息…

【APICloud系列|17】百度开发者平台为了帮助开发者积极开展APP隐私合规风险排查工作, 准确高效的识别违规风险

前段时间开发了一款APP,最近有时间看看了一下自己的邮件。 发现一条比较有意思的。 申请入口 为什么会受到这个邮件呢,因为我安卓上架了华为应用商店、百度手机开放平台,小米应用商店,阿里应用分发平台、腾讯应用宝,360这个不谈,一个IOS的苹果应用商店。 这个只是针对那…

Java数字格式:DecimalFormat

在Java Numeric Formatting一文中 &#xff0c;我描述并演示了NumberFormat静态方法提供的一些有用实例&#xff0c;例如NumberFormat.getNumberInstance&#xff08;Locale&#xff09; &#xff0c; NumberFormat.getPercentInstance&#xff08;Locale&#xff09; &#xf…

微信公众号(订阅号)如何开通付费功能?

前几天看了一下启舰的一个视频中谈到他做自媒体的收入,我记得应该有一年30多万的收入,大概组成是微信公众号广告每个月2万*12个月。两本安卓书收取版权提成,根据出版量8%-10%不等,他一年的出版量在10000左右吧,每本书每个月大概有2000元的样子*12个月,还有一些B站及其他的…