[数据结构]二叉树创建与遍历

实验报告:二叉树创建与遍历

一、问题描述

二叉树是一种实用范围很广的非线性结构,一棵非空二叉树有也只有一个根结点,每个结点最多有两个子树,我们称为左子树与右子树,当一个结点的左、右子树都是空的时,沃恩称此结点为叶子结点。

二叉树有一些很好的性质,这里不再赘述。考虑如何存储一棵树,本实验选择使用链式存储结构——二叉链表;如果事先知道需要存储的二叉树是满二叉树或者完全二叉树,则可以考虑使用顺序存储,否则将浪费大量的存储空间。

对于一棵既成的二叉树,有三种遍历方式——先序、中序与后序。可以证明,一棵形态固定的二叉树与先序遍历、后序遍历生成的序列是1-1对应的,基于这一点,我们可以从先序序列或后序序列出发得到唯一与之对应的二叉树。这是本实验创建二叉树的理论基础。

值得注意的是,一个中序遍历序列与二叉树并不是1-1对应的关系,也就是说一个中序序列对应的二叉树的形态并不固定,即使像本实验中这样添加了虚空结点也不是1-1对应的,所以中序列并不能够生成一棵二叉树。

本实验主要给出先序创建二叉树,以及中序遍历该树。

二、数据结构——二叉链表

二叉链表是二叉树的链式存储结构,其结构与二叉树的性质有天然的契合。

我们知道,二叉树的结点需要保存的信息有:当前结点的信息、左子树、右子树。基于如此的结构,我们在一个结点处分为数据域和指针域:数据域保存该结点自身的信息;指针域分为左、右指针域,分别指向该结点的左、右子树;有必要时可以加上一个链域指向该结点的父亲结点(双亲结点),当然这样就成了三叉链表。三叉链表与二叉链表没有太大的差别,只是三叉链表在具体操作涉及到寻找结点的祖先时会有优势,比如寻找最近公共祖先(lca),这与本实验无关,不再赘述。

值得注意的是,当结点的某个子树不存在时,指向该结点的指针域应该是NULL的。有个小技巧是,可以人为定义一个“空指针域”,应该指向NULL的时候都指向这个所谓的“空结点”,可以一定程度上避免指针访问越界的错误。

三、算法的设计和实现

这个算法的思想非常简单。本实验可以分为两部分——创建二叉树与遍历二叉树。

1、创建二叉树(以先序序列为例)

树的序列生成是一个递归的过程,所以以遍历序列创建树的时候也需要递归生成这棵树。

以先序序列为例,如果当前标号非空,那么我们得到的就是当前结点的数据,保存该数据后,下一个数据——无论是虚空结点还是真实的数据——是左子树的数据,所以我们将递归到左子树中;如果当前标号是空的,即虚空结点,表示此处不存在结点,返回NULL即可;当前结点的左子树递归返回后进入右子树递归;返回该结点的地址,结束。

可以看出这就是一个模拟生成先序序列的过程,着眼于一个结点来说,就是先访问当前子树的根节点,然后左子树,再右子树,最后返回。

后序序列也可以类似地生成,具体细节将在下面提到。

2、遍历二叉树(以中序遍历为例)

与树的创建相似,二叉树的遍历同样是一个递归的过程。

以中序序遍历为例,如果左子树非空,则递归进入左子树;输出当前结点的标号;如果右子树非空,则递归进入右子树;结束。如果不是需要输出序列,而是想得到这个序列,那么很自然地可以想到栈,将输出标号的操作改为将标号压栈即可,别的操作不改动。

遍历二叉树的意义重大,比如中序遍历一棵排序二叉树,得到的就是保存的有序的序列。

四、预期结果和实验中的问题

1、预期结果:

程序能够根据输入的先序序列(带有虚空结点),正确地生成对应的二叉树,并正确地输出中序遍历序列。下图是一个生成三序的例子。

2、实验中的问题及一些说明:

(1)如果没有虚空结点,能否通过三序得到唯一对应的二叉树?

答案是肯定的。下面将简单地说明如何利用先序序列和中序序列得到对应的二叉树。

A)分析:

对于一个先序序列来说,第一个结点一定是根结点,它的右边是左子树的先序序列,然后是右子树的先序序列。这里的问题在于,无法找到左、右子树序列的分界点,换句话说,仅靠一个先序序列我们无法得到对应的二叉树。

这时我们再对中序序列的构成分析,我们可以在中序序列中找到根结点(根结点的标号已经在先序序列中得到了),它的左边是左子树的中序序列,右边是右子树的中序序列。

这时候我们发现,这里出现了一个子结构。因为一棵树(子树)的三序序列长度是相等的,所以我们可以得到左、右子树的先序序列,从而对于任一子树来说,我们得到了它的先序序列和中序序列,问题转换成了相似的子问题。

B)实现:

a)先序序列第一个结点找到根结点;

b)在中序序列中找到该结点(此处可以顺序查找,找到结束即可;当数据量比较大时推荐使用二分法查找),得到左、右子树的结点数,从而分别得到左、右子树对应的先序序列和中序序列;

c)分别递归进入左、右子树,建树。递归的边界是叶子结点,它的先序和中序都是长度为1的自己的标号。

(2)包含虚空结点的后序序列如何生成对应的二叉树?

有一个简单的办法,从后往前扫描后序序列,相当于是顺序为“根结点-右子树-左子树”生成的序列。这个与先序序列生成对应二叉树是一个镜面的过程,不再赘述。

(3)中序序列能够生成唯一对应的二叉树吗?

答案是否定的。中序序列与二叉树并不是1-1对应的,下图是一个例子:

这两个都是合法的二叉树,两者的中序序列都是CBA,即使是加上了虚空结点,得到的中序序列也都是$C$B$A$($表示空格,也就是虚空结点),所以中序序列与二叉树不是1-1对应的,当然不能通过中序序列生成一棵唯一的二叉树。

(2)能否不使用递归操作?

这个问题可以归结为研究递归操作的本质。

递归操作实际上是一个对栈的操作,以先序遍历二叉树为例:将当前结点压栈;如果左子树非空,将左子树压栈;如果右子树非空,将右子树压栈;如果左、右子树都是空的,即当前结点为叶子结点,弹栈;当结点的左右子树都完成了操作,弹栈。

于是我们得到了一个有意思的结果:所有的递归算法是可以有非递归调用的实现方式的(这里的非递归调用实现方式指的是递归调用函数,其本质应该还是递归)。当然,也不排除一些递归解决的问题同样可以用递推解决,比如汉诺塔问题,这里不赘述。

附:c++源代码:

 1 /*
 2 项目:创建二叉树,三序遍历
 3 作者:张译尹
 4 */
 5 #include <iostream>
 6 #include <cstdio>
 7
 8 using namespace std;
 9
10 template <class T> class BiTree
11 {
12 private:
13     T data; //根结点的标志
14     BiTree *lch, *rch; //左右儿子
15 public:
16     void InitBiTree() //初始化二叉链表
17     {
18         data = 0;
19         lch = rch = NULL;
20     }
21     BiTree* CreatBiTree() //先序(带虚空结点)递归建立二叉链表
22     {
23         BiTree <char> *rt = new BiTree;
24         rt -> InitBiTree();
25         char ch;
26         scanf("%c", &ch);
27         if(ch != ‘ ‘) //非空指针,递归建树
28         {
29             rt -> data = ch;
30             rt -> lch = CreatBiTree();
31             rt -> rch = CreatBiTree();
32         }
33         else
34         {
35             delete rt;
36             rt = NULL;
37         }
38         return rt;
39     }
40     void PreOrderTraverse() //递归先序遍历
41     {
42         printf("%c", data);
43         if(lch)
44             lch -> PreOrderTraverse();
45         if(rch)
46             rch -> PreOrderTraverse();
47     }
48     void InOrderTraverse() //递归中序遍历
49     {
50         if(lch)
51             lch -> InOrderTraverse();
52         printf("%c", data);
53         if(rch)
54             rch -> InOrderTraverse();
55     }
56     void PostOrderTraverse() //递归后序遍历
57     {
58         if(lch)
59             lch -> PostOrderTraverse();
60         if(rch)
61             rch -> PostOrderTraverse();
62         printf("%c", data);
63     }
64 };
65
66 int main()
67 {
68     BiTree <char> *Tree;
69     //Tree -> InitBiTree();
70
71     printf("请输入二叉树的先序列,结点用字母表示,空域用空格表示。\n");
72     Tree = Tree -> CreatBiTree();
73
74     printf("先序序列:\n");
75     Tree -> PreOrderTraverse();
76     printf("\n");
77
78     printf("中序序列:\n");
79     Tree -> InOrderTraverse();
80     printf("\n");
81
82     printf("后序序列:\n");
83     Tree -> PostOrderTraverse();
84     printf("\n");
85
86     return 0;
87 }

时间: 2024-11-08 20:10:42

[数据结构]二叉树创建与遍历的相关文章

中序线索二叉树创建及其遍历

通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数.准确的说,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个.如下图所示. 因此,提出了一种方法,利用原来的空链域存放指针,指向树中其他结点.这种指针称为线索. 记ptr指向二叉链表中的一个结点,以下是建立线索的规则: (1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点.这个结点称为ptr的中序前驱: (2)如果ptr->rchild为空,则存放指向中序遍历

创建先序二叉树-创建层次遍历树

创建先序二叉树 #include<iostream> using namespace std; class BinTreeNode { public:     char ch;     BinTreeNode(int value){ch=value;}     BinTreeNode *left,*right; }; BinTreeNode* create_tree() {     char item;     BinTreeNode *t,*t_l,*t_r;     cin>>

二叉树创建及遍历

声明:这篇文章是抄袭http://blog.csdn.net/sjf0115/article/details/8645991 请大家自己查看原博客 #include<iostream> #include<stack> #include<queue> using namespace std; //二叉树结点数据结构 typedef struct BiTNode { char data; //数据 struct BiTNode * lchild;//左右孩子指针 struc

数据结构-二叉树的各种遍历(先中后层序!!)

最近在写数据结构中二叉树的遍历,这里总结一下: 先序递归遍历: void PreTravel(BiTree T) {//前序递归遍历 if(T) { printf("%c",T->data); PreTravel(T->lchild); PreTravel(T->rchild); } } 中序递归遍历: void MidTravel(BiTree T) {//中序递归遍历 if(T) { MidTravel(T->lchild); printf("%c

二叉树学习一:二叉树创建与遍历

二叉树的遍历有三种方式: 1)先序遍历:若二叉树为空,则空操作:不为空,则先访问根结点,先序遍历左子树,先序遍历右子树. 2)后序遍历:若二叉树为空,则空操作:不为空,则中序遍历左子树,访问根结点,中序遍历右子树. 3)后序遍历:若二叉树为空,则空操作:不为空,则后序遍历左子树,后序遍历右子树,访问根结点. 例: 1)先序遍历结果为:ABDECF 2)中序遍历结果为:DBEAFC 3)后序遍历结果为:DEBFCA 二叉树输出的思想是将树转换成线性结构输出,一般采用递归方式,非递归方式是使用栈实现

数据结构二叉树构造及遍历详解

前言 最近学到了二叉树,就学着将二叉树构造,并尝试三种遍历操作.本次主要使用递归,回头会整理非递归的方法. 定义二叉树 1 typedef struct BinaryTree 2 { 3 TelemType data; 4 struct BinaryTree *lchild; 5 struct BinaryTree *rchild; 6 }*Node,node; 其中要注意Node是结构体指针,这样定义以后使用会方便很多. 构造二叉树 1 Node CreatTree() 2 { 3 Node

数据结构 - 二叉树(重构 + 遍历)

写在前面 昨天有同学问到我一题关于重构二叉树的问题(link),做了一下,也做个记录吧! 所谓二叉树的重构,就是给你前序和中序,或者中序和后序,让你还原这棵二叉树. 注意:给出前序和后序是不能唯一确定一棵二叉树的,证明请看这儿. 一.给出前序和中序,重构二叉树 一个递归的过程: 当前结点的value:每一轮根据前序的第一个元素确定当前结点值. 左子树的中序遍历数组:以当前结点的value为分界点,将中序分为左部分和右部分,左部分即为左子树的中序遍历数组. 右子树的中序遍历数组:以当前结点的val

二叉树创建、遍历

#include<iostream> #include <stdlib.h> #include<stdio.h> using namespace std; struct BinNode{ char data; struct BinNode *lchild,*rchild; }; BinNode * T; void creatBinTree(BinNode * &T); void Preorder(BinNode * &T); void Inorder(B

SDUT 3341 数据结构实验之二叉树二:遍历二叉树

数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,,de,g,,f,,, (其中,表示空结点).请建立二叉树并按中序和后序的方式遍历该二叉树. Input 连续输入多组数据,每组数据输入一个长度小于50个字符的字符串. Output 每组输入数据对应输出2行:第1行输出中序遍历序列:第2行输出后序遍历序列