数据结构 4.2树和二叉树(遍历二叉树及各种应用)
二叉树遍历算法的应用

二叉树遍历算法的应用二叉树是一种常用的数据结构,它由节点和节点之间的链接组成。
每个节点最多有两个子节点,分别称为左子节点和右子节点。
二叉树遍历算法是指按照一定的顺序访问二叉树中的所有节点,经典的二叉树遍历算法有前序遍历、中序遍历和后序遍历。
这些遍历算法在计算机科学中有广泛的应用。
一、前序遍历前序遍历算法的访问顺序是先访问根节点,然后依次访问左子树和右子树。
在实际应用中,前序遍历算法十分常见,具有以下几个应用:1.树的复制:如果需要复制一棵二叉树,可以使用前序遍历算法遍历原树,然后按照递归或迭代的方式创建新节点,并复制原节点的值。
2.表达式求值:对于一个二叉树表示的数学表达式,前序遍历算法可以用来计算表达式的值。
遍历到运算符节点时,先计算左子表达式的值,然后计算右子表达式的值,最后根据运算符进行计算。
3.文件系统遍历:文件系统可以被视为一个树状结构,前序遍历算法可以按照前序的顺序遍历文件系统中的所有文件和文件夹。
二、中序遍历中序遍历算法的访问顺序是先访问左子树,然后访问根节点,最后访问右子树。
中序遍历算法也有多个应用:1.二叉树的中序遍历得到的节点值是按照从小到大的顺序排列的。
因此,可以使用中序遍历算法验证一个二叉树是否为二叉树。
2.二叉树中序遍历的结果可以用来实现按照升序排列的有序集合的功能。
例如,在数据库中存储的数据可以通过中序遍历的结果进行排序。
3.中序遍历算法可以将一个二叉树转换为一个有序的双向链表。
在遍历过程中,维护一个前驱节点和一个后继节点,并进行链接操作。
三、后序遍历后序遍历算法的访问顺序是先访问左子树,然后访问右子树,最后访问根节点。
后序遍历算法也有多个应用:1.后序遍历算法可以用来计算二叉树的深度。
在遍历过程中,可以维护一个全局变量来记录最大深度。
2.后序遍历算法可以用来判断一个二叉树是否为平衡二叉树。
在遍历过程中,可以比较左右子树的高度差,判断是否满足平衡二叉树的定义。
3.后序遍历算法可以用来释放二叉树的内存。
二叉树的遍历及常用算法

⼆叉树的遍历及常⽤算法⼆叉树的遍历及常⽤算法遍历的定义:按照某种次序访问⼆叉树上的所有结点,且每个节点仅被访问⼀次;遍历的重要性:当我们需要对⼀颗⼆叉树进⾏,插⼊,删除,查找等操作时,通常都需要先遍历⼆叉树,所有说:遍历是⼆叉树的基本操作;遍历思路:⼆叉树的数据结构是递归定义(每个节点都可能包含相同结构的⼦节点),所以遍历也可以使⽤递归,即结点不为空则继续递归调⽤每个节点都有三个域,数据与,左孩⼦指针和右孩⼦之指针,每次遍历只需要读取数据,递归左⼦树,递归右⼦树,这三个操作三种遍历次序:根据访问三个域的不同顺序,可以有多种不同的遍历次序,⽽通常对于⼦树的访问都按照从左往右的顺序;设:L为遍历左⼦树,D为访问根结点,R为遍历右⼦树,且L必须位于R的前⾯可以得出以下三种不同的遍历次序:先序遍历操作次序为DLR,⾸先访问根结点,其次遍历根的左⼦树,最后遍历根右⼦树,对每棵⼦树同样按这三步(先根、后左、再右)进⾏中序遍历操作次序为LDR,⾸先遍历根的左⼦树,其次访问根结点,最后遍历根右⼦树,对每棵⼦树同样按这三步(先左、后根、再右)进⾏后序遍历操作次序为LRD,⾸先遍历根的左⼦树,其次遍历根的右⼦树,最后访问根结点,对每棵⼦树同样按这三步(先左、后右、最后根)进⾏层次遍历层次遍历即按照从上到下从左到右的顺序依次遍历所有节点,实现层次遍历通常需要借助⼀个队列,将接下来要遍历的结点依次加⼊队列中;遍历的应⽤“遍历”是⼆叉树各种操作的基础,可以在遍历过程中对结点进⾏各种操作,如:对于⼀棵已知⼆叉树求⼆叉树中结点的个数求⼆叉树中叶⼦结点的个数;求⼆叉树中度为1的结点个数求⼆叉树中度为2的结点个数5求⼆叉树中⾮终端结点个数交换结点左右孩⼦判定结点所在层次等等...C语⾔实现:#include <stdio.h>//⼆叉链表数据结构定义typedef struct TNode {char data;struct TNode *lchild;struct TNode *rchild;} *BinTree, BinNode;//初始化//传⼊⼀个指针令指针指向NULLvoid initiate(BinTree *tree) {*tree = NULL;}//创建树void create(BinTree *BT) {printf("输⼊当前结点值: (0则创建空节点)\n");char data;scanf(" %c", &data);//连续输⼊整形和字符时.字符变量会接受到换⾏,所以加空格if (data == 48) {*BT = NULL;return;} else {//创建根结点//注意开辟的空间⼤⼩是结构体的⼤⼩⽽不是结构体指针⼤⼩,写错了不会⽴马产⽣问题,但是后续在其中存储数据时极有可能出现内存访问异常(飙泪....) *BT = malloc(sizeof(struct TNode));//数据域赋值(*BT)->data = data;printf("输⼊节点 %c 的左孩⼦ \n", data);create(&((*BT)->lchild));//递归创建左⼦树printf("输⼊节点 %c 的右孩⼦ \n", data);create(&((*BT)->rchild));//递归创建右⼦树}}//求双亲结点(⽗结点)BinNode *Parent(BinTree tree, char x) {if (tree == NULL)return NULL;else if ((tree->lchild != NULL && tree->lchild->data == x) || (tree->rchild != NULL && tree->rchild->data == x))return tree;else{BinNode *node1 = Parent(tree->lchild, x);BinNode *node2 = Parent(tree->rchild, x);return node1 != NULL ? node1 : node2;}}//先序遍历void PreOrder(BinTree tree) {if (tree) {//输出数据printf("%c ", tree->data);//不为空则按顺序继续递归判断该节点的两个⼦节点PreOrder(tree->lchild);PreOrder(tree->rchild);}}//中序void InOrder(BinTree tree) {if (tree) {InOrder(tree->lchild);printf("%c ", tree->data);InOrder(tree->rchild);}}//后序void PostOrder(BinTree tree) {if (tree) {PostOrder(tree->lchild);PostOrder(tree->rchild);printf("%c ", tree->data);}}//销毁结点递归free所有节点void DestroyTree(BinTree *tree) {if (*tree != NULL) {printf("free %c \n", (*tree)->data);if ((*tree)->lchild) {DestroyTree(&((*tree)->lchild));}if ((*tree)->rchild) {DestroyTree(&((*tree)->rchild));}free(*tree);*tree = NULL;}}// 查找元素为X的结点使⽤的是层次遍历BinNode *FindNode(BinTree tree, char x) {if (tree == NULL) {return NULL;}//队列BinNode *nodes[1000] = {};//队列头尾位置int front = 0, real = 0;//将根节点插⼊到队列尾nodes[real] = tree;real += 1;//若队列不为空则继续while (front != real) {//取出队列头结点输出数据BinNode *current = nodes[front];if (current->data == x) {return current;}front++;//若当前节点还有⼦(左/右)节点则将结点加⼊队列if (current->lchild != NULL) {nodes[real] = current->lchild;real++;}if (current->rchild != NULL) {nodes[real] = current->rchild;real++;}}return NULL;}//层次遍历// 查找元素为X的结点使⽤的是层次遍历void LevelOrder(BinTree tree) {if (tree == NULL) {return;}//队列BinNode *nodes[1000] = {};//队列头尾位置int front = 0, real = 0;//将根节点插⼊到队列尾nodes[real] = tree;real += 1;//若队列不为空则继续while (front != real) {//取出队列头结点输出数据BinNode *current = nodes[front];printf("%2c", current->data);front++;//若当前节点还有⼦(左/右)节点则将结点加⼊队列if (current->lchild != NULL) {nodes[real] = current->lchild;real++;}if (current->rchild != NULL) {nodes[real] = current->rchild;real++;}}}//查找x的左孩⼦BinNode *Lchild(BinTree tree, char x) {BinTree node = FindNode(tree, x);if (node != NULL) {return node->lchild;}return NULL;}//查找x的右孩⼦BinNode *Rchild(BinTree tree, char x) {BinTree node = FindNode(tree, x);if (node != NULL) {return node->rchild;}return NULL;}//求叶⼦结点数量int leafCount(BinTree *tree) {if (*tree == NULL)return 0;//若左右⼦树都为空则该节点为叶⼦,且后续不⽤接续递归了else if (!(*tree)->lchild && !(*tree)->rchild)return 1;else//若当前结点存在⼦树,则递归左右⼦树, 结果相加return leafCount(&((*tree)->lchild)) + leafCount(&((*tree)->rchild));}//求⾮叶⼦结点数量int NotLeafCount(BinTree *tree) {if (*tree == NULL)return 0;//若该结点左右⼦树均为空,则是叶⼦,且不⽤继续递归else if (!(*tree)->lchild && !(*tree)->rchild)return 0;else//若当前结点存在左右⼦树,则是⾮叶⼦结点(数量+1),在递归获取左右⼦树中的⾮叶⼦结点,结果相加 return NotLeafCount(&((*tree)->lchild)) + NotLeafCount(&((*tree)->rchild)) + 1;}//求树的⾼度(深度)int DepthCount(BinTree *tree) {if (*tree == NULL)return 0;else{//当前节点不为空则深度+1 在加上⼦树的⾼度,int lc = DepthCount(&((*tree)->lchild)) + 1;int rc = DepthCount(&((*tree)->rchild)) + 1;return lc > rc?lc:rc;// 取两⼦树深度的最⼤值 }}//删除左⼦树void RemoveLeft(BinNode *node){if (!node)return;if (node->lchild)DestroyTree(&(node->lchild));node->lchild = NULL;}//删除右⼦树void RemoveRight(BinNode *node){if (!node)return;if (node->rchild)DestroyTree(&(node->rchild));node->rchild = NULL;}int main() {BinTree tree;create(&tree);BinNode *node = Parent(tree, 'G');printf("G的⽗结点为%c\n",node->data);BinNode *node2 = Lchild(tree, 'D');printf("D的左孩⼦结点为%c\n",node2->data);BinNode *node3 = Rchild(tree, 'D');printf("D的右孩⼦结点为%c\n",node3->data);printf("先序遍历为:");PreOrder(tree);printf("\n");printf("中序遍历为:");InOrder(tree);printf("\n");printf("后序遍历为:");PostOrder(tree);printf("\n");printf("层次遍历为:");LevelOrder(tree);printf("\n");int a = leafCount(&tree);printf("叶⼦结点数为%d\n",a);int b = NotLeafCount(&tree);printf("⾮叶⼦结点数为%d\n",b);int c = DepthCount(&tree);printf("深度为%d\n",c);//查找F节点BinNode *node4 = FindNode(tree,'C');RemoveLeft(node4);printf("删除C的左孩⼦后遍历:");LevelOrder(tree);printf("\n");RemoveRight(node4);printf("删除C的右孩⼦后遍历:");LevelOrder(tree);printf("\n");//销毁树printf("销毁树 \n");DestroyTree(&tree);printf("销毁后后遍历:");LevelOrder(tree);printf("\n");printf("Hello, World!\n");return 0;}测试:测试数据为下列⼆叉树:运⾏程序复制粘贴下列内容:ABDGHECKFIJ特别感谢:iammomo。
二叉树遍历操作的基本应用(复制、求深度、求叶子数、求节点数等)

二叉树遍历操作的基本应用(复制、求深度、求叶子数、求节点数等)1. 引言1.1 概述二叉树是计算机科学领域中常用的数据结构之一,具有广泛的应用场景。
在二叉树的操作中,遍历是其中最基本和常见的操作之一。
通过遍历,我们可以按照特定规则依次访问二叉树中的所有节点。
本文将探讨二叉树遍历操作的基本应用,包括复制、求深度、求叶子数、求节点数等。
这些操作不仅在实际开发中有重要意义,而且对于理解和掌握二叉树数据结构及其相关算法也具有重要作用。
1.2 文章结构本文将分为五个部分进行论述。
首先,在引言部分(第1节)我们将概述文章的主题和目标。
紧接着,在第2节中,我们将介绍二叉树遍历的基本应用,包括复制、求深度、求叶子数和求节点数等。
在第3节中,我们将详细解析这些基本应用,并给出相应算法和实例分析。
接下来,在第4节中,我们将通过实际案例应用来验证并讨论这些基本应用的性能与适用范围。
最后,在第5节中总结全文内容,并对未来研究方向进行展望。
1.3 目的本文的目的是通过对二叉树遍历操作的基本应用进行详细剖析,帮助读者深入理解和掌握二叉树数据结构及其相关算法。
同时,我们希望通过实际案例应用与讨论,探讨如何优化算法性能、提高效率以及适应大规模二叉树遍历问题。
通过本文的阅读,读者将能够全面了解并应用二叉树遍历操作的基本方法,在实际开发中解决相关问题,并为进一步研究和探索提供思路与参考。
该部分主要介绍了文章的概述、结构和目的,引导读者了解全文并明确阅读目标。
2. 二叉树遍历的基本应用:二叉树是一种常见的数据结构,其遍历操作可以应用于多种实际问题中。
本节将介绍四个基本的二叉树遍历应用:复制二叉树、求二叉树的深度、求二叉树的叶子数和求二叉树的节点数。
2.1 复制二叉树:复制一个二叉树意味着创建一个与原始二叉树结构完全相同的新二叉树。
该应用场景在涉及对原始数据进行修改或者对数据进行独立操作时非常有用。
复制操作可以以递归方式实现,通过先复制左子树,再复制右子树,最后创建一个与当前节点值相等的新节点来完成。
二叉树,树,森林遍历之间的对应关系

二叉树,树,森林遍历之间的对应关系一、引言在计算机科学中,数据结构是非常重要的知识点之一。
而树这一数据结构,作为基础的数据结构之一,在软件开发中有着广泛的应用。
本文将重点探讨二叉树、树和森林遍历之间的对应关系,帮助读者更加全面地理解这些概念。
二、二叉树1. 二叉树的定义二叉树是一种特殊的树结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。
二叉树可以为空,也可以是一棵空树。
2. 二叉树的遍历在二叉树中,有三种常见的遍历方式,分别是前序遍历、中序遍历和后序遍历。
在前序遍历中,节点的访问顺序是根节点、左子树、右子树;在中序遍历中,节点的访问顺序是左子树、根节点、右子树;在后序遍历中,节点的访问顺序是左子树、右子树、根节点。
3. 二叉树的应用二叉树在计算机科学领域有着广泛的应用,例如用于构建文件系统、在数据库中存储有序数据、实现算法中的搜索和排序等。
掌握二叉树的遍历方式对于理解这些应用场景非常重要。
三、树1. 树的定义树是一种抽象数据类型,由n(n>0)个节点组成一个具有层次关系的集合。
树的特点是每个节点都有零个或多个子节点,而这些子节点又构成了一颗子树。
树中最顶层的节点称为根节点。
2. 树的遍历树的遍历方式有先根遍历、后根遍历和层次遍历。
在先根遍历中,节点的访问顺序是根节点、子树1、子树2...;在后根遍历中,节点的访问顺序是子树1、子树2...,根节点;在层次遍历中,节点的访问顺序是从上到下、从左到右依次访问每个节点。
3. 树的应用树广泛用于分层数据的表示和操作,例如在计算机网络中的路由算法、在操作系统中的文件系统、在程序设计中的树形结构等。
树的遍历方式对于处理这些应用来说至关重要。
四、森林1. 森林的定义森林是n(n>=0)棵互不相交的树的集合。
每棵树都是一颗独立的树,不存在交集。
2. 森林的遍历森林的遍历方式是树的遍历方式的超集,对森林进行遍历就是对每棵树进行遍历的集合。
3. 森林的应用森林在实际编程中经常用于解决多个独立树结构的问题,例如在数据库中对多个表进行操作、在图像处理中对多个图形进行处理等。
二叉树遍历(前序、中序、后序、层次、广度优先、深度优先遍历)

⼆叉树遍历(前序、中序、后序、层次、⼴度优先、深度优先遍历)⽬录转载:⼆叉树概念⼆叉树是⼀种⾮常重要的数据结构,⾮常多其他数据结构都是基于⼆叉树的基础演变⽽来的。
对于⼆叉树,有深度遍历和⼴度遍历,深度遍历有前序、中序以及后序三种遍历⽅法,⼴度遍历即我们寻常所说的层次遍历。
由于树的定义本⾝就是递归定义,因此採⽤递归的⽅法去实现树的三种遍历不仅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写出递归版本号:public void preOrderTraverse1(TreeNode root) {if (root != null) {System.out.print(root.val+" ");preOrderTraverse1(root.left);preOrderTraverse1(root.right);}}2)如今讨论⾮递归的版本号:依据前序遍历的顺序,优先訪问根结点。
然后在訪问左⼦树和右⼦树。
所以。
对于随意结点node。
第⼀部分即直接訪问之,之后在推断左⼦树是否为空,不为空时即反复上⾯的步骤,直到其为空。
若为空。
则须要訪问右⼦树。
注意。
在訪问过左孩⼦之后。
《数据结构及其应用》笔记含答案 第五章_树和二叉树

第5章树和二叉树一、填空题1、指向结点前驱和后继的指针称为线索。
二、判断题1、二叉树是树的特殊形式。
()2、完全二叉树中,若一个结点没有左孩子,则它必是叶子。
()3、对于有N个结点的二叉树,其高度为。
()4、满二叉树一定是完全二叉树,反之未必。
()5、完全二叉树可采用顺序存储结构实现存储,非完全二叉树则不能。
()6、若一个结点是某二叉树子树的中序遍历序列中的第一个结点,则它必是该子树的后序遍历序列中的第一个结点。
()7、不使用递归也可实现二叉树的先序、中序和后序遍历。
()8、先序遍历二叉树的序列中,任何结点的子树的所有结点不一定跟在该结点之后。
()9、赫夫曼树是带权路径长度最短的树,路径上权值较大的结点离根较近。
()110、在赫夫曼编码中,出现频率相同的字符编码长度也一定相同。
()三、单项选择题1、把一棵树转换为二叉树后,这棵二叉树的形态是(A)。
A.唯一的B.有多种C.有多种,但根结点都没有左孩子D.有多种,但根结点都没有右孩子解释:因为二叉树有左孩子、右孩子之分,故一棵树转换为二叉树后,这棵二叉树的形态是唯一的。
2、由3个结点可以构造出多少种不同的二叉树?(D)A.2 B.3 C.4 D.5解释:五种情况如下:3、一棵完全二叉树上有1001个结点,其中叶子结点的个数是(D)。
A.250 B. 500 C.254 D.501解释:设度为0结点(叶子结点)个数为A,度为1的结点个数为B,度为2的结点个数为C,有A=C+1,A+B+C=1001,可得2C+B=1000,由完全二叉树的性质可得B=0或1,又因为C为整数,所以B=0,C=500,A=501,即有501个叶子结点。
4、一个具有1025个结点的二叉树的高h为(C)。
A.11 B.10 C.11至1025之间 D.10至1024之间解释:若每层仅有一个结点,则树高h为1025;且其最小树高为⎣log21025⎦ + 1=11,即h在11至1025之间。
数据结构树的种类

数据结构树的种类树是一种基本的数据结构,用于表示具有层次结构的数据。
它由一组节点组成,其中的每个节点都可以有零个或多个子节点。
树可以有不同的种类,每种种类具有不同的特点和应用场景。
以下是一些常见的树的种类:1. 二叉树(Binary Tree):二叉树是一种每个节点最多只有两个子节点的树结构。
它可以是空树,或者由一个根节点、左子树和右子树组成。
二叉树具有简单的结构,常用于二分和排序。
2. 二叉树(Binary Search Tree):二叉树是一种具有以下特点的二叉树:左子树中的所有节点都比根节点小,右子树中的所有节点都比根节点大。
二叉树支持快速的查找、插入和删除操作,并在树中保持有序性。
3. 平衡二叉树(Balanced Binary Tree):平衡二叉树是一种二叉树,但它在插入和删除节点时会自动调整树的结构以保持树的平衡性。
平衡二叉树的常见实现包括 AVL 树和红黑树,它们可以提供在最坏情况下仍保持对数时间复杂度的查找、插入和删除操作。
4. B树(B-Tree):B树是一种自平衡的树结构,它具有以下特点:每个节点可以有多个子节点,每个节点中的键值有序排列,并且每个节点中的键值数量有一个上限和下限。
B树通常用于大规模数据的存储和数据库系统。
5. Trie树(Trie Tree):Trie树,也称为字典树或前缀树,是一种专门用于处理字符串集合的树结构。
Trie树的每个节点都代表一个字符串前缀,通过将字符逐级插入树中,可以高效地完成字符串的和查找操作。
6. 线段树(Segment Tree):线段树是一种用于处理区间查询问题的树结构。
它将要处理的区间划分为一系列离散的线段,并为每个线段创建一个节点。
线段树可以高效地回答关于区间的统计性质,如区间最小值、区间最大值、区间和等。
7. 堆(Heap):堆是一种完全二叉树,它具有以下特点:对于每个节点,它的值都大于等于(或小于等于)它的子节点的值。
堆被广泛应用于优先队列、排序算法(如堆排序)以及图算法中。
二叉树的遍历及其应用

0引言
所谓遍历,是指沿着某条搜索路线,依次对树中每个结点均做一次 且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。 遍历 在二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。二叉 树作为一种重要的数据结构是工农业应用与开发的重要工具。遍历是二 叉树算法设计中经典且永恒的话题。经典的算法大多采用递归搜索。递 归算法具有简练、清晰等优点,但因其执行过程涉及到大量的堆栈使 用,难于应用到一些严格限制堆栈使用的系统,也无法应用到一些不支 持递归的语言环境[9]。
由先序序列和中序序列来还原二叉树的过程算法思想[7]: (1)若二叉树空,返回空; (2)若不空,取先序序列第一个元素,建立根节点; (3)在中序序列中查找根节点,以此来确定左右子树的先序序列和中 序序列; (4)递归调用自己,建左子树; (5)递归调用自己,建右子树。
4二叉树的遍历的应用
根据二叉树的遍历算法, 可得出如下规律: 规律1: 前序序列遍历第一个为根结点, 后序遍历的最后一个结点为 根结点。 规律2: 前序序列遍历最后一个为根结点右子树的最右叶子结点, 中 序遍历的最后一个结点为根结点右子树的最右叶子结点。 规律3: 中序序列遍历第一个结点为根结点左子树的最左叶子结点,
1遍历二叉树的概念
所谓遍历二叉树,就是遵从某种次序,访问二叉树中的所有结点, 使得每个结点仅被访问一次。这里提到的“访问”是指对结点施行某种 操作,操作可以是输出结点信息,修改结点的数据值等,但要求这种访
问不破坏它原来的数据结构。在本文中,我们规定访问是输出结点信息 data,且以二叉链表作为二叉树的存贮结构。由于二叉树是一种非线性 结构,每个结点可能有一个以上的直接后继,因此,必须规定遍历的规 则,并按此规则遍历二叉树,最后得到二叉树所有结点的一个线性序 列[1]。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
{ if(T!=NULL)
{ inOrder (T->lchild);
printf(T->data);
inOrder (T->rchild);
}
}
后序遍历二叉树的递归算法: void PostOrder (BiTree T)
{ if(T!=NULL)
{ PostOrder (T->lchild); PostOrder (T->rchild); printf(T->data); } }
算法:用到队列
void layer(BiTree T){
InitQueue(Q);
if(T) EnQueue(Q, T);
while(!QueueEmpty(Q))
{ DeQueue(Q,p); Visit(p); if(p->lchild) EnQueue(Q, p->lchild); if(p->rchild) EnQueue(Q, p->rchild);} }//layer
A B C
B
A
C
若已知一棵二叉树的前序序列和中序序列,能否 唯一确定这棵二叉树呢?怎样确定? 例如:已知一棵二叉树的前序遍历序列和中序遍历 序列分别为ABCDEFGHI 和BCAEDGHFI,如何构 造该二叉树呢?
A
BC
D E F G H I
前序:A B C D E F G H I 中序:B C A E D G H F I
6.3 遍历二叉树 •遍历二叉树
•遍历的非递归算法
•遍历二叉树的应用
6.3.1 遍历二叉树
一、问题的提出 顺着某一条搜索路径巡访二叉树中的 结点,使得每个结点均被访问一次,而且 仅被访问一次。 “访问”的含义可以很广,如:输出结点 的信息等。
“遍历”是任何类型均有的操作,对线性 结构而言,只有一条搜索路径(因为每个结点 均只有一个后继),故不需要另加讨论。 而二叉树是非线性结构,每个结点有两 个后继,则存在如何遍历即按什么样的搜索 路径进行遍历的问题。
Void PreOder (BiTree T){
A
左是空返回 T 返回 T B
if(T){
printf (T->data); PreOrder(T->lchild);
B
C
左是空返回 右是空返回
PreOrder (T->rchild);
}/*先序遍历*/
主程序 Pre( T )
}
D
T
A
printf(A); pre(T L); pre(T R);
}//if else return FALSE;
}
5 二叉链表的生成 第一种方法:先序遍历递归构建
按如下顺序输入二叉树中各结点的值: (1) 输入根结点的值; (2) 若左子树不空,则输入左子树,否则 输入一个结束符; (3)若右子树不空,则输入右子树,否则输 入一个结束符;
以字符串的形式 “根 左子树 右子树” 定义一棵二叉树 例如: 空树
A 100
200
B
400
C 300
D E 500
中序遍历序列 BDAECF
F 600
显示E
显示C
显示F
500 1 500 0 300 0 300 0 300 0 300 1 600 1 600 1 600 1 600 1 600 0
void InOrder_iter( BiTree BT ) { //“任务书”分析方法 // 利用栈实现中序遍历二叉树,BT为指向二 叉树的根结点的头指针 InitStack(S); e.ptr=BT; e.task=Travel; if(BT) Push(S, e);// 布置初始任务 while(!StackEmpty(S)) { … //处理任务,见后 } //while }//InOrder_iter
6.3.2 遍历算法的应用
1 确定二叉树
若已知一棵二叉树的前序(或中序,或后序,或 层序)序列,能否唯一确定这棵二叉树呢?
例:已知前序序列为ABC,则可能的二叉树有5种。 A B C B C A
若已知一棵二叉树的前序序列和后序序列,能否 唯一确定这棵二叉树呢? 例:已知前序遍历序列为ABC,后序遍历序列为 CBA,则下列二叉树都满足条件。
遍历算法的执行轨迹
第1次到达 第2次到达
每个结点都 有3次到达的 机会
B D
第3次到达
A
C
E
F
G
五、“任务书”分析方法
“中序遍历二叉树”包括三项子任务: “遍历左子树” “访问根结点” “遍历右子树”
在写算法之前首先需定义栈的元素类型 typedef enum {Visit , Travel} TaskType; // Travel == 1:遍历, // Visit == 0:访问 typedef struct { BiTree ptr; // 指向根结点的指针 TaskType task; // 任务性质 } ElemType; 栈的元素类型
算法基本思想:
先序(或中序或后序)遍历二叉树,在遍历过 程中查找叶子结点,并计数。 由此,需在遍历算法中增添一个“计数”的 参数,并将算法中“访问结点” 的操作改为: 若是叶子,则计数器增1。
void CountLeaf (BiTree T, int* count){ if ( T ) { if ((!T->lchild)&& (!T->rchild)) *count++; // 对叶子结点计数 CountLeaf( T->lchild, count); CountLeaf( T->rchild, count); } // if } // CountLeaf
A B C E D
前序:B C 中序:B C 前序: D E F G H I 中序: E D G H F I
FG HI
A B C E G H D F I
前序:F G H I 中序:G H F I
A B C E D
前序: D E F G H I 中序: E D G H F I
FG HI
2 统计二叉树中叶子结点的个数
1 表示遍历
0 表示访问
p
200
A 100
B
400
C
D E 500
地址 “任务书” 300 分析方法 F 600 中序
显示D 显示A
进栈:按中序布置任务 出栈:根据任务性质处 理任务
显示B
200 200 1 400 100 0 100 100 1 300 1 300
0 1 400 0 0 100 0 100 0 1 300 1 300 1
非空二叉树
根结点(D) 左子树(L) 右子树(R)
对“二叉树”而言,可以有三条搜索路 径:
1.先上后下的按层次遍历; 2.先左(子树)后右(子树)的遍历;
3.先右(子树)后左(子树)的遍历。
二、先左后右的遍历算法
先(根)序的遍历算法
根
中(根)序的遍历算法
左 子树 右 子树
后(根)序的遍历算法
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点。
先序遍历:D L R 中序遍历:L D R 后序遍历:L R D
ABDC
D A D
L
R
BDAC DBCA
L R
D
L R
A
B D B T1 D C T3 D 以先序遍历D L R 为例演示遍历过程 L R C
T2
先序序列:
A B E AB C D E FGH K 中序序列:
三、层次遍历
A B D E C
F
层次遍历:A B
C
D
E
F
A B D E C F
用队列存放结点地址
A B C D E F G
遍历序列:A B C D E F G
GLeabharlann 算法描述1. 队列Q初始化; 2. 如果二叉树非空,将根指针入队; 3. 循环直到队列Q为空 3.1 q=队列Q的队头元素出队; 3.2 访问结点q的数据域; 3.3 若结点q存在左孩子,则将左孩子指针入队; 3.4 若结点q存在右孩子,则将右孩子指针入队;
printf(B); pre(T L); pre(T R); T C
左是空返回 T D 右是空返回 T printf(D); 返回 pre(T L); pre(T R); T 返回 T 返回
printf(C); pre(T L); pre(T R);
T
返回
中序遍历二叉树的递归算法:
void inOrder (BiTree T)
while(!StackEmpty(S)) {//中序 Pop(S,e); // 每次处理一项任务 if (e.task==Visit) visit(e.ptr); else // e.task== Travel 处理访问任务 {p=e.ptr; if(p->right){// 处理非空树的遍历任务 {e.task = 1;e.ptr=p->rchild; 进栈顺序 Push(S,e);// 遍历右子树} e.ptr=p; e.task=Visit; 遍历左子树 Push(S,e); // 访问根结点 访问根结点 if(p->left) 遍历右子树 {e.ptr=p->lchild;e.task=Travel; Push(S,e);}//遍历左子树 }//else } //while
// 若二叉树中存在和 x 相同的元素,则 p 指向该结点并返回 OK,
// 否则返回 FALSE
if (T) { if (T->data==x) return OK; else { if (Preorder(T->lchild, x) return OK; else return(Preorder(T->rchild, x)) ; }//else