二叉树遍历方法技巧

合集下载

二叉树遍历算法的应用

二叉树遍历算法的应用

二叉树遍历算法的应用二叉树是一种常用的数据结构,它由节点和节点之间的链接组成。

每个节点最多有两个子节点,分别称为左子节点和右子节点。

二叉树遍历算法是指按照一定的顺序访问二叉树中的所有节点,经典的二叉树遍历算法有前序遍历、中序遍历和后序遍历。

这些遍历算法在计算机科学中有广泛的应用。

一、前序遍历前序遍历算法的访问顺序是先访问根节点,然后依次访问左子树和右子树。

在实际应用中,前序遍历算法十分常见,具有以下几个应用: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、访问根节点2、遍历左子树3、遍历右子树中序:始终执行以下步骤,1、遍历左子树2、访问根节点3、遍历右子树后序:始终执行以下步骤,1、遍历左子树2、遍历右子树3、访问根节点“始终”:为什么要说“始终”执行呢?因为二叉树的每一个子树又可以看成是一个新的二叉树,遍历步骤、方式都保持一样,所以应该“始终”执行同样的操作,我们也应该始终把它看成一棵新的二叉树。

一些技巧:1、先序遍历第一个元素一定是根节点2、中序遍历中,任何一个元素的前一个元素一定在二叉树中它的左边,比如D在G前面,则D在G左边3、后序遍历最后一个元素一定是根节点4、先、中、后意思是说访问根节点的先后顺序,而且始终从左往右,从上往下先序遍历为:ABC中序遍历为:BAC后序遍历为:BCA先序遍历为:ABDECFG中序遍历为:DBEAFCG后序遍历为:AEBFGCA前序遍历:abcdef 中序遍历:cbdaef 后序遍历:cdbfea先序遍历为:ABDGCEF 中序遍历为:DGBAECF 后序遍历为:GDBEFCA前序遍历结果为 a b d e h i c f g 中序遍历结果为 d b h e i a f c g 后序遍历结果为 d h i e b f g c a前序遍历结果为 FCADBEGHP 中序遍历结果为 ACBDFEHGP 后序遍历结果为 ABDCHPGEF前序遍历为:—+ a * b — c d / e f 中序遍历为: a + b * c — d — e / f后序遍历为: a b c d — * + e f / —前序遍历结果为ABDHEICFG中序遍历结果为HDBEIACGF后序遍历结果为HDIEBGFCA由先序序列ABCDEFGH 和中序序列CBEDAGHF 恢复二叉树: 方法: 先序序列ABCDEFGH (注:A 是根) 中序序列CBEDAGHF由左子树先序序列:BCDE 和左子树中序序列:CBED 构造A 的左子树 同理,由右子树先序序列:FGH 和右子树中序序列GHF 构造A 的右子树:1. 已知某二叉树的其前序遍历序列为1 2 4 3 5 7 6,中序遍历序列为4 2 1 5 7 3 6,求后序遍历序列(4275631)。

二叉树遍历(前序、中序、后序、层次、广度优先、深度优先遍历)

二叉树遍历(前序、中序、后序、层次、广度优先、深度优先遍历)

⼆叉树遍历(前序、中序、后序、层次、⼴度优先、深度优先遍历)⽬录转载:⼆叉树概念⼆叉树是⼀种⾮常重要的数据结构,⾮常多其他数据结构都是基于⼆叉树的基础演变⽽来的。

对于⼆叉树,有深度遍历和⼴度遍历,深度遍历有前序、中序以及后序三种遍历⽅法,⼴度遍历即我们寻常所说的层次遍历。

由于树的定义本⾝就是递归定义,因此採⽤递归的⽅法去实现树的三种遍历不仅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。

第⼀部分即直接訪问之,之后在推断左⼦树是否为空,不为空时即反复上⾯的步骤,直到其为空。

若为空。

则须要訪问右⼦树。

注意。

在訪问过左孩⼦之后。

二叉树前中后序遍历做题技巧

二叉树前中后序遍历做题技巧

二叉树前中后序遍历做题技巧在计算机科学中,二叉树是一种重要的数据结构,而前序、中序和后序遍历则是二叉树遍历的三种主要方式。

下面将分别对这三种遍历方式进行解析,并提供一些解题技巧。

1.理解遍历顺序前序遍历顺序是:根节点->左子树->右子树中序遍历顺序是:左子树->根节点->右子树后序遍历顺序是:左子树->右子树->根节点理解每种遍历顺序是解题的基础。

2.使用递归或迭代二叉树的遍历可以通过递归或迭代实现。

在递归中,每个节点的处理函数会调用其左右子节点的处理函数。

在迭代中,可以使用栈来模拟递归过程。

3.辨析指针指向在递归或迭代中,需要正确处理指针的指向。

在递归中,通常使用全局变量或函数参数传递指针。

在迭代中,需要使用栈或其他数据结构保存指针。

4.学会断点续传在处理大规模数据时,为了避免内存溢出,可以采用断点续传的方式。

即在遍历过程中,将中间结果保存在文件中,下次遍历时从文件中读取上一次的结果,继续遍历。

5.识别循环和终止条件在遍历二叉树时,要识别是否存在循环,并确定终止条件。

循环可以通过深度优先搜索(DFS)或广度优先搜索(BFS)避免。

终止条件通常为达到叶子节点或达到某个深度限制。

6.考虑边界情况在处理二叉树遍历问题时,要考虑边界情况。

例如,对于空二叉树,需要进行特殊处理。

又如,在处理二叉搜索树时,需要考虑节点值的最小和最大边界。

7.优化空间使用在遍历二叉树时,需要优化空间使用。

例如,可以使用in-place排序来避免额外的空间开销。

此外,可以使用懒加载技术来延迟加载子节点,从而减少内存占用。

8.验证答案正确性最后,验证答案的正确性是至关重要的。

可以通过检查输出是否符合预期、是否满足题目的限制条件等方法来验证答案的正确性。

如果可能的话,也可以使用自动化测试工具进行验证。

二叉树的遍历及其应用

二叉树的遍历及其应用

0引言
所谓遍历,是指沿着某条搜索路线,依次对树中每个结点均做一次 且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。 遍历 在二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。二叉 树作为一种重要的数据结构是工农业应用与开发的重要工具。遍历是二 叉树算法设计中经典且永恒的话题。经典的算法大多采用递归搜索。递 归算法具有简练、清晰等优点,但因其执行过程涉及到大量的堆栈使 用,难于应用到一些严格限制堆栈使用的系统,也无法应用到一些不支 持递归的语言环境[9]。
由先序序列和中序序列来还原二叉树的过程算法思想[7]: (1)若二叉树空,返回空; (2)若不空,取先序序列第一个元素,建立根节点; (3)在中序序列中查找根节点,以此来确定左右子树的先序序列和中 序序列; (4)递归调用自己,建左子树; (5)递归调用自己,建右子树。
4二叉树的遍历的应用
根据二叉树的遍历算法, 可得出如下规律: 规律1: 前序序列遍历第一个为根结点, 后序遍历的最后一个结点为 根结点。 规律2: 前序序列遍历最后一个为根结点右子树的最右叶子结点, 中 序遍历的最后一个结点为根结点右子树的最右叶子结点。 规律3: 中序序列遍历第一个结点为根结点左子树的最左叶子结点,
1遍历二叉树的概念
所谓遍历二叉树,就是遵从某种次序,访问二叉树中的所有结点, 使得每个结点仅被访问一次。这里提到的“访问”是指对结点施行某种 操作,操作可以是输出结点信息,修改结点的数据值等,但要求这种访
问不破坏它原来的数据结构。在本文中,我们规定访问是输出结点信息 data,且以二叉链表作为二叉树的存贮结构。由于二叉树是一种非线性 结构,每个结点可能有一个以上的直接后继,因此,必须规定遍历的规 则,并按此规则遍历二叉树,最后得到二叉树所有结点的一个线性序 列[1]。

二叉树遍历(前中后序遍历,三种方式)

二叉树遍历(前中后序遍历,三种方式)

⼆叉树遍历(前中后序遍历,三种⽅式)⽬录刷题中碰到⼆叉树的遍历,就查找了⼆叉树遍历的⼏种思路,在此做个总结。

对应的LeetCode题⽬如下:,,,接下来以前序遍历来说明三种解法的思想,后⾯中序和后续直接给出代码。

⾸先定义⼆叉树的数据结构如下://Definition for a binary tree node.struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}};前序遍历,顺序是“根-左-右”。

使⽤递归实现:递归的思想很简单就是我们每次访问根节点后就递归访问其左节点,左节点访问结束后再递归的访问右节点。

代码如下:class Solution {public:vector<int> preorderTraversal(TreeNode* root) {if(root == NULL) return {};vector<int> res;helper(root,res);return res;}void helper(TreeNode *root, vector<int> &res){res.push_back(root->val);if(root->left) helper(root->left, res);if(root->right) helper(root->right, res);}};使⽤辅助栈迭代实现:算法为:先把根节点push到辅助栈中,然后循环检测栈是否为空,若不空,则取出栈顶元素,保存值到vector中,之后由于需要想访问左⼦节点,所以我们在将根节点的⼦节点⼊栈时要先经右节点⼊栈,再将左节点⼊栈,这样出栈时就会先判断左⼦节点。

代码如下:class Solution {public:vector<int> preorderTraversal(TreeNode* root) {if(root == NULL) return {};vector<int> res;stack<TreeNode*> st;st.push(root);while(!st.empty()){//将根节点出栈放⼊结果集中TreeNode *t = st.top();st.pop();res.push_back(t->val);//先⼊栈右节点,后左节点if(t->right) st.push(t->right);if(t->left) st.push(t->left);}return res;}};Morris Traversal⽅法具体的详细解释可以参考如下链接:这种解法可以实现O(N)的时间复杂度和O(1)的空间复杂度。

二叉树常用的三种遍历方法

二叉树常用的三种遍历方法

二叉树常用的三种遍历方法二叉树是一种常用的数据结构,它由一个根节点和两个子节点组成,其中左子节点小于根节点,右子节点大于根节点。

遍历二叉树是对所有节点进行访问的过程,常用的三种遍历方法是前序遍历、中序遍历和后序遍历。

下面将详细介绍这三种方法的实现步骤。

一、前序遍历前序遍历是指先访问根节点,然后按照左子树、右子树的顺序依次访问每个节点。

具体实现步骤如下:1. 如果当前节点为空,则返回。

2. 访问当前节点。

3. 递归进入左子树。

4. 递归进入右子树。

代码实现:void preorderTraversal(TreeNode* root) {if (root == NULL) return;cout << root->val << " ";preorderTraversal(root->left);preorderTraversal(root->right);}二、中序遍历中序遍历是指先访问左子树,然后访问根节点,最后访问右子树。

具体实现步骤如下:1. 如果当前节点为空,则返回。

2. 递归进入左子树。

3. 访问当前节点。

4. 递归进入右子树。

代码实现:void inorderTraversal(TreeNode* root) {if (root == NULL) return;inorderTraversal(root->left);cout << root->val << " ";inorderTraversal(root->right);}三、后序遍历后序遍历是指先访问左子树,然后访问右子树,最后访问根节点。

具体实现步骤如下:1. 如果当前节点为空,则返回。

2. 递归进入左子树。

3. 递归进入右子树。

4. 访问当前节点。

代码实现:void postorderTraversal(TreeNode* root) {if (root == NULL) return;postorderTraversal(root->left);postorderTraversal(root->right);cout << root->val << " ";}总结:以上就是二叉树常用的三种遍历方法的详细介绍和实现步骤。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

二叉树遍历方法
1.中序遍历的投影法
如果给定一棵二叉树的图形形态,是否能根据此图快速地得出其中序遍历的序列?回答是肯定的。

具体做法是:首先按照二叉树的标准绘制二叉树形态,即将所有左子树都严格绘于根结点的左边;将所有右子树都严格绘于根结点的右边。

然后假设现在有一个光源从该二叉树的顶部投射下来,那么所有结点在地平线上一定会有相应的投影,从左至右顺序读出投影结点的数据即为该二叉树的中序遍历序列。

如图11.10所示。

图示的中序遍历序列: D J G B H E A F I C
2.先序遍历的填空法
如果给定一棵二叉树的图形形态,可在图形基础上,采用填空法迅速写出该二叉树的先序遍历序列。

具体做法是:我们知道,对于每个结点都由三个要素组成,即根结点,左子树、右子树;又已知先序遍历顺序是先访问根结点、然后访问左子树、访问右子树。

那么,我们按层分别展开,逐层填空即可得到该二叉树的先序遍历序列。

图11.10 中序遍历投影法示意图
如图11.10 中的二叉树采用填空法的步骤如下:
(1)根结点左子树右子树
A( )( )
(2)A (根结点(左子树)(右子树))(根结点(左子树)(右子树))
A B C
(3)A(B(根结点(左)(右))(根结点(左)(右)))(C(……)(……))
A B D 无 G E H 无 C F 无
(4)A B D G J E H C F I 此即为该二叉树的先序遍历序列。

注:后序遍历的序列亦可以此方法类推,请读者自己尝试。

3.利用遍历序列构造二叉树
如果已知一棵二叉树的先序遍历序列和中序遍历序列,则可以用这两个遍历序列构造一棵唯一的二叉树形态。

我们知道任意一棵二叉树的先序遍历序列和中序遍历序列是唯一的,那么首先从给定的先序遍历序列入手,该先序序列的第一个元素一定是该二叉树的根;再分析这个根结点在中序遍历序列中的位置,中序遍历序列中根结点的左边即为左子树的全部元素,而根结点的右边即为右子树的全部元素;然后据此再将先序遍历序列除根结点以外的其余部分分为左、右子树两部分,并在这两部分中分别找出左、右子树的根结点。

依此类推,即可得到完整的二叉树。

例11.1 已知一棵二叉树的先序遍历和中序遍历序列分别为:
先序: A B C I D E F H G
中序: C I B E D A H F G
请构造这棵二叉树。

按前述分析,这棵二叉树的构造过程如图11.11所示
图11.11 二叉树的构造过程
树、森林与二叉树的转换(flash演示)
如前所述,树(或森林)的存储结构及其操作算法的实现,由于其“度”的不确定性而导致其存储结构不是较为复杂就是浪费空间,因而,定义在其存储结构上的算法也相应地较难兼顾全面。

如果我们设定一定的规则,用二叉树来表示树和森林的话,就可以方便地解决树、森林的存储结构及其相关算法问题。

1.树、森林转换为二叉树
我们知道,一棵树中每个结点的孩子是无序的,而二叉树中各结点的孩子必须有左右之分。

在此,为避免概念混淆,首先约定树中每个结点的孩子按从左至右的顺序升序编号,即将树中同一层上的兄弟分出大小。

那么将一棵树转换成二叉树的方法是:
(1)在树中同层兄弟间加一连线;
(2)对树中每个结点仅保留其与长兄(左边第一个孩子)的连线,擦去其与其它孩子的连线;
(3)以树(或子树)的根作为轴心,将所有的水平连线顺时针旋转45度,即可得到与该树完全等价的一棵二叉树。

图11.12 树转换为二叉树的过程示意图
可以证明,转换得到的二叉树形态是唯一的。

转换过程如图11.12所示。

森林转换为二叉树的过程与树的转换过程基本相似,区别如下:
(1)将森林中每棵树的根结点间加一连线;
(2)转换后的二叉树不唯一,二叉树的形态取决于森林中树的初始摆放位置;
(3)树转换成二叉树后根结点没有右子树,而森林转换成二叉树后根结点有右子树。

2.将二叉树还原为树或森林
由上述转换过程可知:将二叉树还原为树或森林是上述过程的逆过程。

只是在转换时应注意树和森林转换为二叉树后的形态上的重要区别在于根结点是否具有右子树。

还原的具体步骤如下:
(1)若某结点是其双亲结点的左孩子,则将该结点的右孩子、右孙子、右重孙子……都与该结点的双亲用连线连起来;
(2)删去所有右孩子、右孙子……与其现在双亲结点的连线;
(3)将所有结点以其现在新的连接线所连接的结点为轴心,逆时针旋转45度。

这样即可得到还原后的树或森林。

如图11.13所示
图11.13 二叉树还原为树的过程示意图。

相关文档
最新文档