树的基本概念和遍历规则 数据结构和算法 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)
zsychanpin
- 博客园
- 首页
- 新随笔
- 联系
- 订阅
- 管理
树的基本概念和遍历规则
树的递归定义
树是n(n>0)个结点的有限集,这个集合满足下面条件:
⑴有且仅有一个结点没有前驱(父亲结点)。该结点称为树的根。
⑵除根外,其余的每一个结点都有且仅有一个前驱;
⑶除根外,每个结点都通过唯一的路径连到根上(否则有环)。
这条路径由根開始,而未端就在该结点上,且除根以外,路径上的每个结点都是前一个结点的后继(儿子结点);
由上述定义可知,树结构没有封闭的回路。
节点的分类
结点一般分成三类
⑴根结点:没有父亲的结点。
在树中有且仅有一个根结点。
如节点r
⑵分支结点:除根结点外,有孩子的结点称为分支结点。如a,b,c,x,t,d,i
⑶叶结点:没有孩子的结点称为树叶。如w,h,e,f,s,m,o,n,j,u
根结点到每个分支结点或叶结点的路径是唯一的。
从根r到结点i的唯一路径为rcti。
树的度
⑴结点的度:一个结点的子树数目称为该结点的度。如结点i的度为3。节点t的度为2,节点b的度为1。
显然,全部树叶的度为0。
⑵树的度:全部结点中最大的度称为该树的度(宽度)。
下列树的度为3。
假设採用数组存储子节点地址的话,则应依据树的度定义数组大小
树的深度
树是分层次的。结点所在的层次是从根算起的。
根结点在第一层,根的儿子在第二层,其余各层依次类推。
即某个节点在第k层,则该节点的后件均在第k+1层。
在树中。父结点在同一层的全部结点构成兄弟关系。
树中最大的层次称为树的深度。亦称高度。
图中树的深度为5。
森林
所谓森林。是指若干棵互不相交的树的集合。
如图去掉根结点。其原来的三棵子树Ta,Tb,Tc的集合{Ta。Tb。Tc}就为森林。这三棵子树的详细形态例如以下:
有序树和无序树
依照树中同层结点是否保持有序性,可将树分为有序树和无序树。
(1)假设树中同层结点从左而右排列,其次序不容互换。这种树称为有序树;
(2)假设同层结点的次序随意,这种树称为无序树。
树的表示方法
⑴自然界的树形表示法:
用结点和边表示树,比如上图採用的就是自然界的树形表示法。树形表示法一般用于分析问题。
长处:直观,形象;缺点:保存困难。
⑵括号表示法:
先将根结点放入一对圆括号里,然后把它的子树按由左而右的顺序放入括号里。而对子树也採用相同方法处理:同层子树与它的根结点用圆括号括起来。同层子树之间用逗号隔开,最后用闭括号括起来。比如图可写成例如以下形式
(A(B(E(K,L),F),C(G),D(H(M),I,J)))
长处:易于保存;缺点:不直观
树的遍历规则
所谓树的遍历,是指依照一定的规律不反复地訪问(或取出节点中的信息,或对节点做其它的处理)树中的每个节点,其遍历过程实质上是将树这样的非线性结构按一定规律转化为线性结构。
先根次序遍历
后根次序遍历
先根次序遍历树
先根次序遍历的遍历规则为:若树为空。则退出;否则先根訪问树的根点,然后先根遍历根的每棵子树。
比如。对右图所看到的树进行先根次序遍历,形成的次序为:
Rawxdhebfcstimonju
算法为:
program preorder(v:integer);
{訪问处理节点v;
for i in adj(v) do if i未訪问 then preorder(i);
}
后根次序遍历树
后根次序遍历的遍历规则为:若树为空,则退出;否则后根訪问每棵子树,然后訪问根节点。比如,对右图所看到的树进行后根次序遍历,形成的次序为:
whdexafbsmonijtucR
算法为:
program postorder(v:integer);
{for i in adj(v) do if i未訪问 then postorder(i);
訪问处理节点v;
}
【众安尊享e生】-国民百万医疗保险,投保详解及案例分析,每年最低112元——马云杀手锏
好文要顶 关注我 收藏该文
zsychanpin
关注 - 0
粉丝 - 7
+加关注
« 上一篇:结合源代码分析android的消息机制
» 下一篇:Scala环境搭建之eclipse
posted @ 2017-07-15 13:38 zsychanpin 阅读(5847) 评论(0) 编辑 收藏
https://www.cnblogs.com/zsychanpin/p/7182467.html
llguanli
- 博客园
- 首页
- 新随笔
- 联系
- 订阅
- 管理
二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)
二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。由于树的定义本身就是递归定义,因此採用递归的方法去实现树的三种遍历不仅easy理解并且代码非常简洁,而对于广度遍历来说,须要其他数据结构的支撑。比方堆了。所以。对于一段代码来说,可读性有时候要比代码本身的效率要重要的多。
四种基本的遍历思想为:
前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树---> 根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
层次遍历:仅仅需按层次遍历就可以
比如。求以下二叉树的各种遍历
前序遍历:1 2 4 5 7 8 3 6
中序遍历:4 2 7 5 8 1 3 6
后序遍历:4 7 8 5 2 6 3 1
层次遍历:1 2 3 4 5 6 7 8
一、前序遍历
1)依据上文提到的遍历思路:根结点 ---> 左子树 ---> 右子树,非常easy写出递归版本号:
[java] view plain copy
- public void preOrderTraverse1(TreeNode root) {
- if (root != null) {
- System.out.print(root.val+" ");
- preOrderTraverse1(root.left);
- preOrderTraverse1(root.right);
- }
- }
2)如今讨论非递归的版本号:
依据前序遍历的顺序,优先訪问根结点。然后在訪问左子树和右子树。所以。对于随意结点node。第一部分即直接訪问之,之后在推断左子树是否为空,不为空时即反复上面的步骤,直到其为空。若为空。则须要訪问右子树。注意。在訪问过左孩子之后。须要反过来訪问其右孩子。所以,须要栈这样的数据结构的支持。对于随意一个结点node,详细过程例如以下:
a)訪问之,并把结点node入栈。当前结点置为左孩子;
b)推断结点node是否为空,若为空。则取出栈顶结点并出栈,将右孩子置为当前结点;否则反复a)步直到当前结点为空或者栈为空(能够发现栈中的结点就是为了訪问右孩子才存储的)
代码例如以下:
[java] view plain copy
- public void preOrderTraverse2(TreeNode root) {
- LinkedList<TreeNode> stack = new LinkedList<>();
- TreeNode pNode = root;
- while (pNode != null || !stack.isEmpty()) {
- if (pNode != null) {
- System.out.print(pNode.val+" ");
- stack.push(pNode);
- pNode = pNode.left;
- } else { //pNode == null && !stack.isEmpty()
- TreeNode node = stack.pop();
- pNode = node.right;
- }
- }
- }
二、中序遍历
1)依据上文提到的遍历思路:左子树 ---> 根结点 ---> 右子树,非常easy写出递归版本号:
[java] view plain copy
- public void inOrderTraverse1(TreeNode root) {
- if (root != null) {
- inOrderTraverse1(root.left);
- System.out.print(root.val+" ");
- inOrderTraverse1(root.right);
- }
- }
2)非递归实现,有了上面前序的解释,中序也就比較简单了。同样的道理。仅仅只是訪问的顺序移到出栈时。代码例如以下:
[java] view plain copy
- public void inOrderTraverse2(TreeNode root) {
- LinkedList<TreeNode> stack = new LinkedList<>();
- TreeNode pNode = root;
- while (pNode != null || !stack.isEmpty()) {
- if (pNode != null) {
- stack.push(pNode);
- pNode = pNode.left;
- } else { //pNode == null && !stack.isEmpty()
- TreeNode node = stack.pop();
- System.out.print(node.val+" ");
- pNode = node.right;
- }
- }
- }
三、后序遍历
1)依据上文提到的遍历思路:左子树 ---> 右子树 ---> 根结点。非常easy写出递归版本号:
[java] view plain copy
- public void postOrderTraverse1(TreeNode root) {
- if (root != null) {
- postOrderTraverse1(root.left);
- postOrderTraverse1(root.right);
- System.out.print(root.val+" ");
- }
- }
2)
后序遍历的非递归实现是三种遍历方式中最难的一种。由于在后序遍历中,要保证左孩子和右孩子都已被訪问而且左孩子在右孩子前訪问才干訪问根结点,这就为流程的控制带来了难题。以下介绍两种思路。
第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索。直到搜索到没有左孩子的结点,此时该结点出如今栈顶,可是此时不能将其出栈并訪问,因此其右孩子还为被訪问。
所以接下来依照同样的规则对其右子树进行同样的处理,当訪问完其右孩子时。该结点又出如今栈顶,此时能够将其出栈并訪问。这样就保证了正确的訪问顺序。能够看出,在这个过程中,每一个结点都两次出如今栈顶,仅仅有在第二次出如今栈顶时,才干訪问它。因此须要多设置一个变量标识该结点是否是第一次出如今栈顶。
void postOrder2(BinTree *root) //非递归后序遍历 {stack<BTNode*> s;BinTree *p=root;BTNode *temp;while(p!=NULL||!s.empty()){while(p!=NULL) //沿左子树一直往下搜索。直至出现没有左子树的结点 {BTNode *btn=(BTNode *)malloc(sizeof(BTNode));btn->btnode=p;btn->isFirst=true;s.push(btn);p=p->lchild;}if(!s.empty()){temp=s.top();s.pop();if(temp->isFirst==true) //表示是第一次出如今栈顶 {temp->isFirst=false;s.push(temp);p=temp->btnode->rchild; }else //第二次出如今栈顶 {cout<<temp->btnode->data<<" ";p=NULL;}}} }
另外一种思路:要保证根结点在左孩子和右孩子訪问之后才干訪问,因此对于任一结点P。先将其入栈。假设P不存在左孩子和右孩子。则能够直接訪问它;或者P存在左孩子或者右孩子。可是其左孩子和右孩子都已被訪问过了。则相同能够直接訪问该结点。若非上述两种情况。则将P的右孩子和左孩子依次入栈。这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被訪问。左孩子和右孩子都在根结点前面被訪问。
void postOrder3(BinTree *root) //非递归后序遍历 {stack<BinTree*> s;BinTree *cur; //当前结点 BinTree *pre=NULL; //前一次訪问的结点 s.push(root);while(!s.empty()){cur=s.top();if((cur->lchild==NULL&&cur->rchild==NULL)||(pre!=NULL&&(pre==cur->lchild||pre==cur->rchild))){cout<<cur->data<<" "; //假设当前结点没有孩子结点或者孩子节点都已被訪问过 s.pop();pre=cur; }else{if(cur->rchild!=NULL)s.push(cur->rchild);if(cur->lchild!=NULL) s.push(cur->lchild);}} }
四、层次遍历
层次遍历的代码比較简单。仅仅须要一个队列就可以。先在队列中增加根结点。之后对于随意一个结点来说。在其出队列的时候,訪问之。同一时候假设左孩子和右孩子有不为空的。入队列。代码例如以下:
[java] view plain copy
- public void levelTraverse(TreeNode root) {
- if (root == null) {
- return;
- }
- LinkedList<TreeNode> queue = new LinkedList<>();
- queue.offer(root);
- while (!queue.isEmpty()) {
- TreeNode node = queue.poll();
- System.out.print(node.val+" ");
- if (node.left != null) {
- queue.offer(node.left);
- }
- if (node.right != null) {
- queue.offer(node.right);
- }
- }
- }
五、深度优先遍历
事实上深度遍历就是上面的前序、中序和后序。可是为了保证与广度优先遍历相照顾,也写在这。代码也比較好理解,事实上就是前序遍历,代码例如以下:
[java] view plain copy
- public void depthOrderTraverse(TreeNode root) {
- if (root == null) {
- return;
- }
- LinkedList<TreeNode> stack = new LinkedList<>();
- stack.push(root);
- while (!stack.isEmpty()) {
- TreeNode node = stack.pop();
- System.out.print(node.val+" ");
- if (node.right != null) {
- stack.push(node.right);
- }
- if (node.left != null) {
- stack.push(node.left);
- }
- }
- }
好文要顶 关注我 收藏该文
llguanli
关注 - 0
粉丝 - 2
+加关注
« 上一篇:Swift的基础,操作符,字符串和集合类型
» 下一篇:HDU 1260
posted @ 2017-08-15 10:17 llguanli 阅读(35838) 评论(0) 编辑 收藏
https://www.cnblogs.com/llguanli/p/7363657.html
树的基本概念和遍历规则 数据结构和算法 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)相关推荐
- java 树 广度优先遍历_Java二进制搜索树遍历操作的详细描述[前,中,后,层次,广度优先遍历]...
本文介绍了Java二进制搜索树遍历操作. 与您分享以供参考,如下: 前言: 在Java Binary Search Tree Basics的上一节中,我们了解了该树及其相关知识,并对Binary Se ...
- java数据结构学习笔记-二叉树前、中、后序遍历
public class BinaryTreeDemo {public static void main(String args[]){Employee emp1= new Employee(1,&q ...
- java中二叉树_Java工程师面试1000题224-递归非递归实现二叉树前、中、后序遍历...
224.使用递归和非递归实现二叉树的前.中.后序遍历 使用递归来实现二叉树的前.中.后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现. class Node { public int valu ...
- C++实现二叉树 前、中、后序遍历(递归与非递归)非递归实现过程最简洁版本
本文并非我所写,是复制的该链接中的内容: 最近学习二叉树,想编程实现递归和非递归的实现方式: 递归的方式就不说了,因为大家的递归程序都一样:但是对于非递归的实现方式, 根据这几天的查阅资料已看到差不多 ...
- 【LeetCode | 二叉树前、中、后序遍历{迭代法}实现】
1.前序遍历 // 解题思路:利用栈的原理实现以迭代方法来前序遍历(根左右)二叉树 class Solution { public:vector<int> preorderTraversa ...
- 【LeetCode | 二叉树前、中、后序遍历{递归法}实现】
1.前序遍历 #include <iostream> #include <vector> #include <queue> #include <algorit ...
- 二叉树前、中、后序线索化及遍历
public class ThreadedBinaryTree {public static void main(String[] args){Heronodes node1=new Heronode ...
- 二叉树的前、中、后、层次非递归遍历(js)
有如下二叉树 遍历: // 前序遍历, head-left-rightfunction HLR (tree) {const stack = [], res = []if (tree) stack.p ...
- 数据结构与算法-- 二叉树中和为某一值的路径
二叉树中和为某一值的路径 题目:输入一颗二叉树和一个整数,打印出二叉树中节点值的和为给定值的所有路径.从树的根节点开始往下一只到叶子节点所经过的节点形成一条路径. 我们用二叉树节点的定义沿用之前文章中 ...
最新文章
- mongodb数据库扩展名_MongoDB学习笔记:MongoDB 数据库的命名、设计规范
- java数据传递给安卓_Android数据传递的五种方法汇总
- Linux环境配置1
- SAP Spartacus RouteGuard路由守卫之CmsPageGuard
- 数字图像处理-1.图像获取
- python编程题计算矩阵对角线_Python练习题 028:求3*3矩阵对角线数字之和
- ansible概念以及基础(一)
- 力扣-1557. 可以到达所有点的最少点数目
- Illustrator 教程,如何在 Illustrator 中添加一行文字?
- 微信开发者工具下载使用
- 一步一步实现STM32-FOTA系列教程之BIN文件解包C语言实现
- sqlServer2014用sql server身份认证登录
- MAC使用技巧之苹果电脑新手最容易犯的20个错误
- Flutter 使用 ESC/POS蓝牙或以太网库控制热敏打印机
- 凤凰卫视:专业、互动、持续的云服务助力凤凰新媒体转型
- 安卓视频播放器(TV)
- ---- 招聘之操作系统原理 ----
- Oneday01 | ~scanf、冒泡排序、sort排序
- linux下文件对比工具详解(diff、diff3、sdiff、vimdiff和comm)
- 英文操作系统中,cmd显示中文乱码问题