线索二叉树生成及其遍历

合集下载

数据结构实验五(二叉树的建立及遍历)题目和源程序

数据结构实验五(二叉树的建立及遍历)题目和源程序

实验5:二叉树的建立及遍历(第十三周星期三7、8节)一、实验目的1.学会实现二叉树结点结构和对二叉树的基本操作。

2.掌握对二叉树每种操作的具体实现,学会利用递归方法编写对二叉树这种递归数据结构进行处理的算法。

二、实验要求1.认真阅读和掌握和本实验相关的教材内容。

2.编写完整程序完成下面的实验内容并上机运行。

3.整理并上交实验报告。

三、实验内容1.编写程序任意输入二叉树的结点个数和结点值,构造一棵二叉树,采用三种递归遍历算法(前序、中序、后序)对这棵二叉树进行遍历并计算出二叉树的高度。

2 .编写程序生成下面所示的二叉树,并采用中序遍历的非递归算法对此二叉树进行遍历。

四、思考与提高1.如何计算二叉链表存储的二叉树中度数为1的结点数?2.已知有—棵以二叉链表存储的二叉树,root指向根结点,p指向二叉树中任一结点,如何求从根结点到p所指结点之间的路径?/*----------------------------------------* 05-1_递归遍历二叉树.cpp -- 递归遍历二叉树的相关操作* 对递归遍历二叉树的每个基本操作都用单独的函数来实现* 水上飘2009年写----------------------------------------*/// ds05.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <iostream>typedef char ElemType;using namespace std;typedef struct BiTNode {ElemType data;//左右孩子指针BiTNode *lchild, *rchild;}BiTNode, *BiTree;//动态输入字符按先序创建二叉树void CreateBiTree(BiTree &T) {char ch;ch = cin.get();if(ch == ' ') {T = NULL;}else {if(ch == '\n') {cout << "输入未结束前不要输入回车,""要结束分支请输入空格!" << endl;}else {//生成根结点T = (BiTNode * )malloc(sizeof(BiTNode));if(!T)cout << "内存分配失败!" << endl;T->data = ch;//构造左子树CreateBiTree(T->lchild);//构造右子树CreateBiTree(T->rchild);}}}//输出e的值ElemType PrintElement(ElemType e) { cout << e << " ";return e;}//先序遍历void PreOrderTraverse(BiTree T) { if (T != NULL) {//打印结点的值PrintElement(T->data);//遍历左孩子PreOrderTraverse(T->lchild);//遍历右孩子PreOrderTraverse(T->rchild);}}//中序遍历void InOrderTraverse(BiTree T) {if (T != NULL) {//遍历左孩子InOrderTraverse(T->lchild);//打印结点的值PrintElement(T->data);//遍历右孩子InOrderTraverse(T->rchild);}}//后序遍历void PostOrderTraverse(BiTree T) { if (T != NULL) {//遍历左孩子PostOrderTraverse(T->lchild);//遍历右孩子PostOrderTraverse(T->rchild);//打印结点的值PrintElement(T->data);}}//按任一种遍历次序输出二叉树中的所有结点void TraverseBiTree(BiTree T, int mark) {if(mark == 1) {//先序遍历PreOrderTraverse(T);cout << endl;}else if(mark == 2) {//中序遍历InOrderTraverse(T);cout << endl;}else if(mark == 3) {//后序遍历PostOrderTraverse(T);cout << endl;}else cout << "选择遍历结束!" << endl;}//输入值并执行选择遍历函数void ChoiceMark(BiTree T) {int mark = 1;cout << "请输入,先序遍历为1,中序为2,后序为3,跳过此操作为0:";cin >> mark;if(mark > 0 && mark < 4) {TraverseBiTree(T, mark);ChoiceMark(T);}else cout << "此操作已跳过!" << endl;}//求二叉树的深度int BiTreeDepth(BiTNode *T) {if (T == NULL) {//对于空树,返回0并结束递归return 0;}else {//计算左子树的深度int dep1 = BiTreeDepth(T->lchild);//计算右子树的深度int dep2 = BiTreeDepth(T->rchild);//返回树的深度if(dep1 > dep2)return dep1 + 1;elsereturn dep2 + 1;}}int _tmain(int argc, _TCHAR* argv[]){BiTNode *bt;bt = NULL; //将树根指针置空cout << "输入规则:" << endl<< "要生成新结点,输入一个字符,""不要生成新结点的左孩子,输入一个空格,""左右孩子都不要,输入两个空格,""要结束,输入多个空格(越多越好),再回车!"<< endl << "按先序输入:";CreateBiTree(bt);cout << "树的深度为:" << BiTreeDepth(bt) << endl;ChoiceMark(bt);return 0;}/*----------------------------------------* 05-2_构造二叉树.cpp -- 构造二叉树的相关操作* 对构造二叉树的每个基本操作都用单独的函数来实现* 水上飘2009年写----------------------------------------*/// ds05-2.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <iostream>#define STACK_INIT_SIZE 100 //栈的存储空间初始分配量#define STACKINCREMENT 10 //存储空间分配增量typedef char ElemType; //元素类型using namespace std;typedef struct BiTNode {ElemType data; //结点值BiTNode *lchild, *rchild; //左右孩子指针}BiTNode, *BiTree;typedef struct {BiTree *base; //在栈构造之前和销毁之后,base的值为空BiTree *top; //栈顶指针int stacksize; //当前已分配的存储空间,以元素为单位}SqStack;//构造一个空栈void InitStack(SqStack &s) {s.base = (BiTree *)malloc(STACK_INIT_SIZE * sizeof(BiTree));if(!s.base)cout << "存储分配失败!" << endl;s.top = s.base;s.stacksize = STACK_INIT_SIZE;}//插入元素e为新的栈顶元素void Push(SqStack &s, BiTree e) {//栈满,追加存储空间if ((s.top - s.base) >= s.stacksize) {s.base = (BiTree *)malloc((STACK_INIT_SIZE+STACKINCREMENT) * sizeof(BiTree));if(!s.base)cout << "存储分配失败!" << endl;s.top = s.base + s.stacksize;s.stacksize += STACK_INIT_SIZE;}*s.top++ = e;}//若栈不空,则删除s的栈顶元素,并返回其值BiTree Pop(SqStack &s) {if(s.top == s.base)cout << "栈为空,无法删除栈顶元素!" << endl;s.top--;return *s.top;}//按先序输入字符创建二叉树void CreateBiTree(BiTree &T) {char ch;//接受输入的字符ch = cin.get();if(ch == ' ') {//分支结束T = NULL;} //if' 'endelse if(ch == '\n') {cout << "输入未结束前不要输入回车,""要结束分支请输入空格!(接着输入)" << endl;} //if'\n'endelse {//生成根结点T = (BiTNode * )malloc(sizeof(BiTree));if(!T)cout << "内存分配失败!" << endl;T->data = ch;//构造左子树CreateBiTree(T->lchild);//构造右子树CreateBiTree(T->rchild);} //Create end}//输出e的值,并返回ElemType PrintElement(ElemType e) {cout << e << " ";return e;}//中序遍历二叉树的非递归函数void InOrderTraverse(BiTree p, SqStack &S) {cout << "中序遍历结果:";while(S.top != S.base || p != NULL) {if(p != NULL) {Push(S,p);p = p->lchild;} //if NULL endelse {BiTree bi = Pop(S);if(!PrintElement(bi->data))cout << "输出其值未成功!" << endl;p = bi->rchild;} //else end} //while endcout << endl;}int _tmain(int argc, _TCHAR* argv[]){BiTNode *bt;SqStack S;InitStack(S);bt = NULL; //将树根指针置空cout << "老师要求的二叉树序列(‘空’表示空格):""12空空346空空空5空空,再回车!"<< endl << "请按先序输入一个二叉树序列(可另输入,但要为先序),""无左右孩子则分别输入空格。

线索二叉树

线索二叉树

6.4 线索化二叉树从前面的讨论可知,遍历二叉树就是将非线性结构的二叉树线性化,即按一定规则将二叉树中的结点排列成一个线性序列依次访问。

如图6.20(a)所示的二叉树,经中序遍历得到线性序列:BADEC,经前序遍历得到线性序列:ABCDE,经后序遍历得到线性序列:BEDCA。

在这些线性序列中,二叉树中的每个结点(除第一个和最后一个外)有且仅有唯一的一个前趋和唯一的一个后继,很容易找到各个结点的直接前驱和直接后继。

但当以二叉链表作为二叉树的存储结构时,只能找到结点的左、右孩子,而不能直接找到前驱和后继,只有在遍历的动态过程中得到这些信息。

如果将这些信息在第一次遍历时保存起来,在需要再次对二叉树进行“遍历”时就可以将二叉树视为线性结构进行访问,从而简化遍历操作。

那么,如何存储遍历中得到的结点前驱和后继的信息呢?一个简单的办法是在每个结点上增加两个指针域fwd和bkwd,分别指向存储遍历中得到的结点前驱和后继。

fwd L child data R child bkwd这是采用多重链表来表示二叉树。

这种方法虽简单易行,但这种结构的存储密度将大大降低,浪费存储空间。

另一种方法,是利用原有链域L child 和R child的空链域。

在n个结点的二叉链表中有2n个孩子链域,其中仅有n-1个链域是用来指示结点的左右孩子,而另外n+1个链域是空链域。

现在把这些空链域利用起来,使其指向结点的前驱或后继;对那些原来就不为空的链域,则仍然指向左或右孩子。

如果把指向前驱和后继的指针称为线索(Thread),那么,如何区分指向左、右孩子的指针和指向前驱、后继的线索呢?在原结点结构上增加标志域定义为:0 Lchild为左指针,指向左孩子0 Rchild为右指针,指向右孩子ltag=rtag=1 Lchild为左线索,指向前驱 1 Rchild为右线索,指向后继以这种结点构成的二叉链表作为二叉树的存储结构,叫做线索链表,其C语言类型说明如下:Typedef struct ThreadTNode{enum{0,1} ltag, rtag;Elem Type data;Struct ThreadTNode *Lchild, *Rchild;}ThreadTNode, *ThreadTree;为了节省内存空间,我们用C语言的位段方法将结点中的左标志域和右标志域与数据域合并在一个存储单元中(即各用一位表示左标志和右标志,其余各位表示结点值)。

实验8 线索二叉树的创建和遍历

实验8 线索二叉树的创建和遍历

实验八线索二叉树的创建和遍历1.实验目的(1)掌握二叉树的线索链表存储结构。

(2)能够实现二叉树的线索链表的创建、遍历等基本操作。

2.实验内容编程实现二叉树的线索链表存储表示的基本操作,包括线索二叉树的创建与遍历。

3.实验要求(1)根据实验内容编写程序,上机调试并获得运行结果(2)撰写实验报告4.准备工作本次实验将会构造此二叉树,并会对此二叉树进行遍历5.关键步骤与算法(1)构造线索二叉树算法步骤;若某节点的左孩子节点指针域(lchild)为空,就可以利用它来指出该节点在某种遍历序列中的直接前驱的存储地址;若某节点的右孩子指针域(rchild)为空,就可以利用它来指出该节点在某种遍历序列中的直接后继的存储地址。

那些非空的指针域仍存放指向该节点左、右孩子节点的指针域。

这样,就得到了一棵线索二叉树。

算法如下;1.//按先序遍历序列输入树中各节点的值,构造线索二叉树2.BiThrTree* CreateBiThrTree()3.{4. BiThrTree *s;5. DataType ch;6. scanf("%c",&ch);7.8.if(ch == '#')9. exit(0);10.if(ch == '@')11. s = NULL;12.else if (ch=='\n')13. getchar();14.15.else16. {17. s = (BiThrTree *)malloc(sizeof(BiThrTree));18.if(!s)19. exit(ERROR);20. s->data = ch;21. s->lflag = 0;22. s->rflag = 0;23. s->lchild = CreateBiThrTree();24. s->rchild = CreateBiThrTree();25. }26.return s;27.}(2) 将二叉树中序线索化现将树递归到左子树然后再判断这个树的左右子树是否为空,如果为空则把它们的标志设为1,代表线索,首先设一个current代表前驱结点,然后判断它是否为空,如果为空则进行下一次递归,如果不为空,则判断左标志和右标志如果左标志为线索,则让前驱节点的右孩子指向当前节点,如果右标志为1,则让当前节点的左孩子指向前驱结点算法如下;1.void InThreading(BiThrTree *t)2.{3.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数4. {5. InThreading(t->lchild);//递归调用左子树进行线索化6.if(t->lchild == NULL)7. t->lflag = 1;8.if(t->rchild == NULL)9. t->rflag = 1;10.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上111.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL12. {13.if(current->rflag == 1)14. current->rchild = t;//前驱结点的右孩子指针指向当前节点t15.if(t->lflag == 1)16. t->lchild = current;//现结点t的左孩子指针指向前驱结点17. }18. current = t;//让前驱结点为t为下一次执行函数做准备19. InThreading(t->rchild);//递归调用右子树进行线索化20. }21.}6.源代码1.#include<malloc.h>2.#include<stdio.h>3.#include<stdlib.h>4.#include<io.h>5.#define ERROR -16.7.typedef char DataType;8.typedef struct BiThrNode//二叉树的二叉链表的存储结构9.{10. DataType data;11.struct BiThrNode *lchild,*rchild;12.int lflag,rflag;//左右标志,值为0表示指针,值为1表示线索13.}BiThrTree;14.BiThrTree *current = NULL;15.//检查if的==16.//按先序遍历序列输入树中各节点的值,构造线索二叉树17.BiThrTree* CreateBiThrTree()18.{19. BiThrTree *s;20. DataType ch;21. scanf("%c",&ch);22.23.if(ch == '#')24. exit(0);25.if(ch == '@')26. s = NULL;27.else if (ch=='\n')28. getchar();29.30.else31. {32. s = (BiThrTree *)malloc(sizeof(BiThrTree));33.if(!s)34. exit(ERROR);35. s->data = ch;36. s->lflag = 0;37. s->rflag = 0;38. s->lchild = CreateBiThrTree();39. s->rchild = CreateBiThrTree();40. }41.return s;42.}43.//将二叉树前序线索化(递归)44.void PreThreading(BiThrTree *t)45.{46.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数47. {48.if(t->lchild == NULL)49. t->lflag = 1;50.if(t->rchild == NULL)51. t->rflag = 1;52.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上153.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL54. {55.if(current->rflag == 1)56. current->rchild = t;//前驱结点的右孩子指针指向当前节点t57.if(t->lflag == 1)58. t->lchild = current;//现结点t的左孩子指针指向前驱结点59. }60. current = t;//让前驱结点为t为下一次执行函数做准备61.if(t->lflag == 0)62. PreThreading(t->lchild);//递归调用右子树进行线索化63.if(t->rflag == 0)64. PreThreading(t->rchild);65. }66.}67.//遍历前序线索二叉树68.void PreVisitThrtree(BiThrTree *t)69.{70.while(t != NULL)//大循环71. {72.while(t->lflag == 0)73. {74. printf("%c\t",t->data);75. t = t->lchild;76. }77. printf("%c\t",t->data);78. t = t->rchild;79. }80.}81.82.//检查if的==83.//将二叉树中序线索化(递归)84.void InThreading(BiThrTree *t)85.{86.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数87. {88. InThreading(t->lchild);//递归调用左子树进行线索化89.if(t->lchild == NULL)90. t->lflag = 1;91.if(t->rchild == NULL)92. t->rflag = 1;93.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上194.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL95. {96.if(current->rflag == 1)97. current->rchild = t;//前驱结点的右孩子指针指向当前节点t98.if(t->lflag == 1)99. t->lchild = current;//现结点t的左孩子指针指向前驱结点100. }101. current = t;//让前驱结点为t为下一次执行函数做准备102. InThreading(t->rchild);//递归调用右子树进行线索化103. }104.}105.//检查if的==106.//遍历中序线索二叉树107.void InVisitThrtree(BiThrTree *t)108.{109.while(t != NULL)//大循环110. {111.while(t->lflag == 0)//小循环1;此循环第一次进行时首先要去找到最左节点112. t = t->lchild;113.if(t == NULL)114. exit(ERROR);115. printf("%c\t",t->data);116.while(t->rflag == 1 && t->rchild != NULL)//小循环2117. {118. t = t->rchild;119. printf("%c\t",t->data);120. }121. t = t->rchild;122./*123.两种情况:124. 1.如果t的右子树为空则t直接指向t的中序后继结点.125. 2.如果t的右子树不为空,即t->rflag为0,那么退出这个小循环2回到大循环中,再到小循环1中去找t的右子树的最左下的结点.126. */127. }128.}129.130.//将二叉树后序线索化(递归)131.void PostThreading(BiThrTree *t)132.{133.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数134. {135. PreThreading(t->lchild);136. PreThreading(t->rchild);137.if(t->lchild == NULL)138. t->lflag = 1;139.if(t->rchild == NULL)140. t->rflag = 1;141.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上1142.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL143. {144.if(current->rflag == 1)145. current->rchild = t;//前驱结点的右孩子指针指向当前节点t146.if(t->lflag == 1)147. t->lchild = current;//现结点t的左孩子指针指向前驱结点148. }149. current = t;//让前驱结点为t为下一次执行函数做准备150. }151.}152.//遍历后序线索二叉树153.void PostVisitThrtree(BiThrTree *t)154.{155.if(t)156. {157.while(t->lchild != NULL && t->lflag == 0)158. {159. t = t->lchild;//先遍历到最左边的节点160. }161. }162.}163.//主函数164.void main()165.{166. BiThrTree *t,*s;167. printf("\t\t请按先序序列输入二叉树(如:ABC@@DE@G@@F@@@#)\n\t\t");168. t = CreateBiThrTree();169. InThreading(t);170. printf("\t\t按中序遍历输出线索二叉树:\n\t\t");171. InVisitThrtree(t);172. printf("\n");173.//getchar();174.//getchar();175. fflush(stdin);//这个操作必须要进行,或者是进行上面注释的那两步操作,要不然176. printf("\t\t请按先序序列输入二叉树(如:ABC@@DE@G@@F@@@#)\n\t\t");177. s = CreateBiThrTree();178. PreThreading(s);179. printf("\t\t按前序遍历输出线索二叉树:\n\t\t");180. PreVisitThrtree(s);181.}7.测试图8.实验总结然对于这一节,主要是讨论线索二叉树的建立以及遍历,对于二叉树的建立,主要有五个部分构成,分别是data,lchild,rchild,lflag,rflag,而它与二叉树不同的地方就是多了lflag和rflag的判断,当当前节点的左节点为空时一定会指向它的前驱结点,当前驱节点的右节点为空时,让前驱结点的右指针域指向当前节点,这就是线索化,而对于前序中序后序的线索化本质都是一样的。

线索二叉树

线索二叉树

遍历 线索 二叉 树非
{
while(p->ltag==0) p=p->left; /*从根往下找到"最左"的结
点,即中序序列的开始结点*/
do
{
递归 算法
printf("%c",p->date);/*访问结点*/
p=succ(p);
}while(p!=NULL); }
返回
}

数据结构
在中序遍历线索树过程中,按下述两条 原则即可找到后继结点:
– 1) 如果某结点的右线索标志域为1,说明其 右指针域是线索,这个线索所指的即是该结 点的后继结点;
– 2) 如果某结点的右线索标志为0,则其右指 针域是指向右儿子结点的指针,由此结点的 右儿子结点起按左指针域指针逐结点向左查 找,一直找到左线索标志域为1的结点,即 是该结点的后继结点。
{

if(p!=NULL)

{ inthread(p->left,pre); /*左子树线索化*/
线
if(p->left==NULL)

/*若当前结点的左子树为空,则建立指 向其前趋结点的前趋线索*/

{

p->ltag=1;

p->left=pre; }
else
p->ltag=0树;
if (pre!=NULL && pre->right==NULL)
这种结点类型和相应结点的指针类型定义如 下:
typedef struct tnode {
ElemType data; int ltag,rtag; /*ltag和rtag只能取值为0或1*/ struct tnode *left,*right; }tbtree;

线索二叉树

线索二叉树

0 A0 0 B1
0 C0
1 D0
1 E1
1F1
1 G1
(b) root
0
1
ห้องสมุดไป่ตู้
0 A0
0 B1
0 C0
0 A0
0 B1
0 C0
1 D0
1 E1
1F1
1 D0
1 E1
1F1
1 G1
1 G1
(c)
(d)
线索二叉树 b—中序 c—前序 d—后序
一旦建立了某种方式的线索二叉树后,用户程序就可以 像操作双向链表一样操作该线索二叉树。
if(tree->current == tree->root) tree->nextComplete = 1;
}
int EndOfNext(ThreadBiTree *tree) //判断是否已到中序线索二叉树的最后一个结点 { return tree->nextComplete; }
例8-3 编写一个程序,首先建立如图8-10(a)所示的不带头结点的二叉树, 然后中序线索化该二叉树,最后用循环结构输出该中序线索化二叉树各结 点的序列信息。
这种算法设计要求分别设计三个函数: First():定位在第一个结点位置; Next():移动到下一个结点位置; End():是否已经到最后下一个结点位置; 当然,还需要一个根据二叉树构造线索二叉树的函数。
typedef struct { ThreadBiNode *root;
ThreadBiNode *current; int nextComplete; }ThreadBiTree;
规定:当某结点的左指针为空时,令该指针指向按某种方法遍历二叉树时 得到的该结点的前驱结点;当某结点的右指针为空时,令该指针指向按某种 方法遍历二叉树时得到的该结点的后继结点。仅仅这样做会使我们不能区分 左指针指向的结点到底是左孩子结点还是前驱结点,右指针指向的结点到底 是右孩子结点还是后继结点。因此我们再在结点中增加两个线索标志位来区 分这两种情况。

线索二叉树的创建及遍历

线索二叉树的创建及遍历

实验九线索二叉树的创建及遍历实验目的:掌握二叉树的线索链表存储结构,能够实现二叉树的线索链表的创建、遍历等基本操作实验要求:1、认真阅读和掌握教材上和本实验相关的内容及算法2、上机将线索二叉树的线索链表存储表示的创建和遍历算法实现。

3、进行简单的输入输出验证。

实验内容:编程实现二叉树的线索链表存储表示的基本操作,这些基本操作包括:线索二叉树的创建、线索二叉树的中序遍历算法的实现。

要求对程序中的一些关键语句要有注释,并能够进行简单的输入输出验证。

参考代码#include <stdlib.h>#include <stdio.h>#define OVERFLOW 0//线索二叉树的二叉链表存储定义typedef enum PointerTag {LINK=0,THREAD=1};struct BiThrNode{char data;struct BiThrNode * lchild, * rchild;PointerTag LTag, RTag;};typedef struct BiThrNode BiThrNode;typedef BiThrNode * BiThrTree;/*****************************************************************\** 按先序次序输入二叉树中的结点的值(一个字符)构造二叉链表表示的二叉树,* 字符'#'表示空树。

** 例如,一棵二叉树的三种遍历次序为:* 先序:-+a*b-cd/ef 中序:a+b*c-d-e/f 后序:abcd-*+ef/* 程序中应该输入:-+a##*b##-c##d##/e##f##** 又如,一棵二叉树的三种遍历次序为:* 先序:ABDFGCEH 中序:BFDGACHE 后序:FGDBHECA* 程序中应该输入:AB#DF##G##C#EH###*\******************************************************************/ void CreateBiTree(BiThrTree &T){char ch;ch = getchar();if (ch=='#') T=NULL;else{if (!(T = (BiThrNode *)malloc(sizeof(BiThrNode)))) exit(OVERFLOW);T->data = ch;T->LTag = LINK;T->RTag = LINK;CreateBiTree(T->lchild);CreateBiTree(T->rchild);}return;}//CreateBiTreevoid PrintBiTree(BiThrTree T){//按中序遍历次序输出二叉树T中的结点的值(一个字符),二叉树T用二叉链表存储。

莫里斯算法

莫里斯算法

莫里斯算法莫里斯算法(Morris Traversal)是一种二叉树遍历的算法,通过只使用一个指针和利用线索二叉树的思想,实现对二叉树的中序遍历,时间复杂度为O(n),空间复杂度为O(1)。

1. 线索二叉树线索二叉树是指将一颗二叉树中所有的空指针域指向该节点的直接前驱节点或直接后继节点的二叉树。

线索化后,原本空闲的指针域存储前驱或后继的指针,可以方便地实现对二叉树的遍历。

在递归遍历的过程中,需要记录每个节点是否已经被遍历。

线索二叉树分为前序线索二叉树、中序线索二叉树、后序线索二叉树。

其中,利用中序线索二叉树可以实现对二叉树的中序遍历。

2. Morris遍历算法Morris遍历算法是一种利用线索二叉树对二叉树进行中序遍历的算法,它的核心思想是在遍历节点的过程中,利用空闲的指针域存储前驱或后继的指针,从而实现对二叉树的遍历。

Morris遍历算法对于空间的要求比较严格,它只需要使用一个指针来实现对二叉树的遍历,因此空间复杂度为O(1),非常适合解决空间限制的问题。

在时间复杂度方面,Morris遍历算法需要在遍历每个节点和寻找其前驱节点时,都需要遍历部分节点,因此时间复杂度为O(n)。

具体而言,Morris遍历算法利用线索化指针判断节点是否被访问过,从而实现对二叉树的遍历。

Morris遍历算法的具体流程如下:1)初始化当前节点为根节点2)重复以下操作,直到当前节点为空a. 如果当前节点没有左孩子节点,则输出当前节点,将其右孩子节点作为下一次要访问的节点,更新当前节点为右孩子节点b. 如果当前节点存在左孩子节点,则寻找当前节点的左子树中的最右侧节点,该节点为当前节点的直接前驱节点i. 如果该节点的右孩子指向当前节点,则说明当前节点是第二次访问该节点,将其右孩子设为null,输出该节点,更新当前节点为右孩子节点ii. 如果该节点的右孩子指向null,则说明当前节点为第一次访问该节点,将其右孩子设为当前节点,更新当前节点为左孩子节点Morris遍历算法的特点是利用空闲的指针域存储前驱或后继的指针,实现对二叉树的遍历。

二叉树的生成与遍历

二叉树的生成与遍历

#include <alloc.h>#include "btree.h"#include "sstack.h"typedef struct stack_tag{elemtype *elem;int top;int size;}SQSTACK;int InitSqstack(SQSTACK *S,int n);void DestroySqstack(SQSTACK *S);int IsSqstackEmpty(SQSTACK S);int IsSqstackFull(SQSTACK S);int Push(SQSTACK *S,elemtype e);int Pop(SQSTACK *S,elemtype *e);int InitSqstack(SQSTACK *S, int n){S->elem=(elemtype *)malloc(n*sizeof(elemtype)); if(S->elem==NULL)return 0;S->size=n;S->top=-1;return 1;}void DestroySqstack(SQSTACK *S){free(S->elem);S->elem=NULL;S->top=-1;S->size=0;}int IsSqstackEmpty(SQSTACK S){return S.top==-1;}int IsSqstackFull(SQSTACK S){return S.top==S.size-1;}int Push(SQSTACK *S,elemtype e){if(IsSqstackFull(*S))return 0;S->top++;S->elem[S->top]=e;return 1;}int Pop(SQSTACK *S,elemtype *e){if(IsSqstackEmpty(*S)) return 0;*e=S->elem[S->top];S->top--;return 1;}typedef struct thrbtreenode{char data;int ltag,rtag;struct thrbtreenode *lchild,*rchild;} THRBTREENODE, *THRBTREENODEPTR,*THRBTREE; typedef THRBTREENODEPTR elemtype;typedef struct{int x,y;}BTREENODEPOS;#define MAXCOUNT 32void InitBtreeNodePos();THRBTREE CreateBtree2(char *str);void DestroyBtree(THRBTREE root);void ShowBtree(THRBTREE root,int index);void PreOrderThr(THRBTREE p);THRBTREE InOrderThread(THRBTREE root);void InOrderThr(THRBTREE p);THRBTREE PreOrderThread(THRBTREE root);#include <stdio.h>#include <conio.h>#include <graphics.h>#include <alloc.h>#include <string.h>#include "btree.h"#include "sstack.h"THRBTREENODEPTR pre;BTREENODEPOS btnpos[MAXCOUNT];void InitBtreeNodePos(void){int i;for(i=0;i<16;i++){btnpos[16+i].x=20+i*40;btnpos[16+i].y=480-30;}for(i=15;i>=1;i--){btnpos[i].x=(btnpos[2*i].x+btnpos[2*i+1].x)/2;btnpos[i].y=btnpos[2*i].y-80;}}THRBTREE CreateBtree1(char *str){THRBTREE root=NULL;THRBTREENODEPTR p;int tag,i,len;int mark;/* 1--characters,2--(,3--,4--) */SQSTACK s;if(str[0]==0)return root;root=(THRBTREENODEPTR)malloc(sizeof(THRBTREENODE)); if(root==NULL)return root;root->data=str[0];root->lchild=root->rchild=NULL;root->ltag=root->rtag=0;len=strlen(str);InitSqstack(&s,len);p=root;mark=1;for(i=1;str[i]!=0;i++)switch(str[i]){case '(':if(mark==2){DestroyBtree(root);printf("illegal global list!");return NULL;}mark=2;Push(&s,p);tag=0;break;case ')':mark=4;if(!Pop(&s,&p)){DestroyBtree(root);printf("illegal global list!");return NULL;}break;case ',':if(mark==3){DestroyBtree(root);printf("illegal global list!");return NULL;}mark=3;tag=1;break;default:if(mark==1){DestroyBtree(root);printf("illegal global list!");return NULL;}mark=1;if(IsSqstackEmpty(s)){DestroyBtree(root);printf("illegal global list!");return NULL;}p=(THRBTREENODEPTR)malloc(sizeof(THRBTREENODE)); if(p==NULL){DestroyBtree(root);return NULL;}p->data=str[i];p->lchild=p->rchild=NULL;p->ltag=p->rtag=0;if(tag==0)s.elem[s.top]->lchild=p;elses.elem[s.top]->rchild=p;break;}return root;}void DestroyBtree(THRBTREE root){if(root==NULL)return;if(root->ltag==0)DestroyBtree(root->lchild);if(root->rtag==0)DestroyBtree(root->rchild);root->lchild=NULL;root->rchild=NULL;free(root);}void ShowBtree(THRBTREE root,int index){int i,j,len;char str[10];if(root==NULL)return;i=index*2;j=index*2+1;setcolor(YELLOW);if(i<MAXCOUNT&&root->ltag==0&&root->lchild!=NULL) line(btnpos[index].x,btnpos[index].y,btnpos[i].x,btnpos[i].y); if(j<MAXCOUNT&&root->rtag==0&&root->rchild!=NULL) line(btnpos[index].x,btnpos[index].y,btnpos[j].x,btnpos[j].y); setfillstyle(SOLID_FILL,BLACK);setcolor(WHITE);fillellipse(btnpos[index].x,btnpos[index].y,15,15);sprintf(str,"%c",root->data);len=strlen(str);outtextxy(btnpos[index].x-len*8/2,btnpos[index].y-4,str); setcolor(YELLOW);if(root->ltag==1&&root->lchild!=NULL){sprintf(str,"%c",root->lchild->data);len=strlen(str);outtextxy(btnpos[index].x-len*8/2-8,btnpos[index].y,str);}if(root->rtag==1&&root->rchild!=NULL){sprintf(str,"%c",root->rchild->data);len=strlen(str);outtextxy(btnpos[index].x-len*8/2+8,btnpos[index].y,str);}if(i<MAXCOUNT&&root->ltag==0) ShowBtree(root->lchild,i);if(j<MAXCOUNT&&root->rtag==0) ShowBtree(root->rchild,j);}void InOrder(THRBTREE root,char *str){char tmpstr[10];if(root==NULL)return;InOrder(root->lchild,str);sprintf(tmpstr,"%c ",root->data);strcat(str,tmpstr);InOrder(root->rchild,str);}void InOrderThr(THRBTREE p){}THRBTREE InOrderThread(THRBTREE root){THRBTREE head;head=(THRBTREENODEPTR)malloc(sizeof(THRBTREENODE)); if(head==NULL)return NULL;head->lchild=NULL;return head;}void TraverseInOrderThr(THRBTREE root,char *str){return;}void main(){char *gstr="A(B(C,),E(F(G,H),))"; char trastr[80];THRBTREE root,thrt;int gdriver=DETECT,gmode;int i;InitBtreeNodePos();root=CreateBtree1(gstr);registerbgidriver(EGA VGA_driver); initgraph(&gdriver,&gmode,"");trastr[0]=0;InOrder(root,trastr);setcolor(WHITE);outtextxy(10,10,trastr);thrt=InOrderThread(root);trastr[0]=0; TraverseInOrderThr(root,trastr); setcolor(YELLOW);outtextxy(10,20,trastr); ShowBtree(thrt->lchild,1);getch();closegraph();DestroyBtree(root);}。

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

数据结构课程设计题目: 线索二叉树的生成及其遍历学院:理学院班级:数学13-2班学生姓名:孙晴、张炳赫、张美娜、董自鹏学生学号: 8、12、13、22 指导教师:张太发2014 年 12月 24日课程设计任务书目录摘要................................. 错误!未定义书签。

1 题目分析.......................... 错误!未定义书签。

1.1相关思想及概念介绍 (1)1.2线索二叉树的结构 (1)1.3需求分析 (2)2 概要设计 (2)2.1抽象数据类型的定义 (3)2.2主程序的流程 (3)2.3各程序模块之间的层次(调用)关系 (5)3 详细设计 (6)4 调试分析 (10)5 用户使用说明 (10)6 测试结果 (11)7 课程设计体会 (12)8 参考文献 (12)9 源程序 (13)摘要针对以二叉链表作为存储结构时,只能找到结点的左、右孩子的信息,而得不到结点的前驱与后继信息,为了使这种信息只有在遍历的动态过程中才能得到。

增设两个指针分别指示其前驱和后继,并且利用结点的空链域存放(线索链表)。

同时为了记下遍历过程中访问结点的先后关系,附设一个指针pre始终指向刚刚访问过的结点,若指针 p 指向当前访问的结点,则 pre指向它的前驱。

由此得到中序遍历建立中序线索化链表的算法本文通过建立二叉树,实现二叉树的中序线索化并实现中序线索二叉树的遍历。

实现对已生成的二叉树进行中序线索化并利用中序线索实现对二叉树的遍历的效果。

关键词:二叉树,中序线索二叉树,中序线索二叉树的遍历1 题目分析1.1 相关思想及概念介绍(1)二叉树遍历二叉树的遍历是指按照某种顺序访问二叉树中的每个结点,使每个结点被访问一次且仅被访问一次。

遍历二叉树中经常要用到的一种操作。

因为在实际应用问题中,常常需要按一定顺序对二叉树中的每个结点逐个进行访问,查找具有某一特点的结点,然后对这些满足条件的结点进行处理。

通过一次完整的遍历,可使二叉树中结点信息由非线性排列变为某种意义上的线性序列。

也就是说,遍历操作使非线性结构线性化。

由二叉树的定义可知,一棵二叉树由根结点、根结点的左子树和根结点的右子树三部分组成。

因此,只要依次遍历这三部分,就可以遍历整个二叉树。

若以D、L、R分别表示访问根结点、遍历根结点的左子树、遍历根结点的右子树,则二叉树的遍历方式有6种:DLR、LDR、LRD、DRL、RDL、和RLD。

如果限定先左后右,则只有前三种方式,即DLR(先序遍历)、LDR(中序遍历)和LRD(后序遍历)。

(1)线索二叉树按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有结点排列为一个线性序列。

在该序列中,除第一个结点外,每个结点有且仅有一个直接前驱结点;除最后一个结点外,每个结点有且仅有一个直接后继结点。

但是,二叉树中每个结点在这个序列中的直接前驱结点和直接后继结点是什么,二叉树的存储结构中并没有反映出来,只能在对二叉树遍历的动态过程中得到这些信息,可以利用二叉树的二叉链表存储结构中的那些空指针域来指示。

这些指向直接前驱结点和指向直接后继结点的指针被称为线索,借了线索的二叉树成为线索二叉树。

线索二叉树将为二叉树的遍历提供许多方便。

1.2 线索二叉树的结构1、n个结点有n-1个前驱和n-1个后继;一共有2n个链域,其中:n+1个空链域,n-1个指针域;因此, 可以用空链域来存放结点的前驱和后继。

线索二叉树就是利用n+1个空链域来存放结点的前驱和后继结点的信息。

2、线索:有效利用二叉链表中空的存储空间,指定原有的孩子指针为空的域来存放指向前驱和后继的信息,这样的指针被称为“线索”。

加线索的过程称为线索化,由此得到的二叉树称作线索二叉树。

若结点有左子树,则左链域lchild指示其左孩子(ltag=0);否则,令左链域指示其前驱(ltag=1);若结点有右子树,则右链域rchild指示其右孩子(rtag=0);否则,令右链域指示其后继(rtag=1);增设一个头结点,令其lchild指向二叉树的根结点,ltag=0、rtag=1;并将该结点作为遍历访问的第一个结点的前驱和最后一个结点的后继;最后用头指针指示该头结点。

其rchild域的指针指向中序遍历时访问的最后一个结点;反之,令二叉树中序序列中的第一个结点的lchild域指针和最后一个结点rchild 域的指针均指向头结点,这好比为二叉树建立了一个双向线索链表,既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。

3、例子:如下图a示的二叉树,其中序线索二叉树为图b所示:1.3 需求分析以二叉链表作为存储结构时,只能找到结点的左、右孩子的信息,而得不到结点的前驱与后继信息,为了使这种信息只有在遍历的动态过程中才能得到。

增设两个指针分别指示其前驱和后继,并且利用结点的空链域存放(线索链表)。

同时为了记下遍历过程中访问结点的先后关系,附设一个指针pre始终指向刚刚访问过的结点,若指针 p 指向当前访问的结点,则 pre指向它的前驱。

由此得到中序遍历建立中序线索化链表的算法本文通过建立二叉树,实现二叉树的中序线索化并实现中序线索二叉树的遍历。

实现对已生成的二叉树进行中序线索化并利用中序线索实现对二叉树的遍历的效果。

主要任务:1.建立二叉树;2.将二叉树进行中序线索化;3.编写程序,运行并修改;4.利用中序线索遍历二叉树5.书写课程设计论文并将所编写的程序完善。

2 概要设计2.1抽象数据类型的定义二叉树的存储结构struct node{ ElemenType data; //数据域int ltag; //左标志域int rtag; //右标志域struct node *lchild; //左指针域struct node *rchild; //右指针域}BTree;2.2主程序的流程3 详细设计(一)算法的C语言实现#include "stdio.h"#include "stdlib.h"#define OK 1typedef char TElemType;typedef int Status;typedef enum PointerTag {Link,Thread};//link==0:pointer,Thread==1:threadtypedef struct BiThrNode{TElemType data;struct BiThrNode *lchild,*rchild;PointerTag LTag,RTag;}BiThrNode,*BiThrTree;BiThrTree pre; // 全局变量,始终指向刚刚访问过的结点void InThreading(BiThrTree p){if(p){InThreading(p->lchild);//左子树线索化if(!p->lchild){p->LTag=Thread;p->lchild=pre;}//前驱线索if(!pre->rchild){pre->RTag=Thread;pre->rchild=p;}//后续线索 pre=p; //保持pre指向p的前驱InThreading(p->rchild);//右子树线索化}//if}//InThreadingStatus InOrderThreading(BiThrTree &Thrt,BiThrTree T){//中序遍历二叉树,并将其中序线索化,Thrt指向头结点//BiThrTree Thrt;if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode)))) exit(-1); Thrt->LTag=Link; //建立头结点Thrt->RTag=Thread; //右指针回指Thrt->rchild=Thrt;if(!T) Thrt->rchild=Thrt; //若二叉树为空,则左指针回指else {Thrt->lchild=T;pre=Thrt;InThreading(T); //中序遍历进行中序线索化pre->rchild=Thrt;//最后一个结点线索化pre->RTag=Thread;Thrt->rchild=pre;}return OK;}//InOrderThreadingStatus InOrderTraverse_Thr(BiThrTree T){//T指向头结点,头结点的左链lchild指向根结点,非递归算法BiThrTree p;p=T->lchild;while(p!=T) //空树或遍历结束时,T==p{while(p->LTag==Link) p=p->lchild;printf("%c\n",p->data); //访问其左子树为空的结点while(p->RTag==Thread&&p->rchild!=T){p=p->rchild;printf("%c\n",p->data); //访问后续结点}//whilep=p->rchild;}//whilereturn OK;}//InOrderT_ThrStatus CreateBitree(BiThrTree &T){//按先序次序输入二叉树char ch,enter;scanf("%c%c",&ch,&enter);if(ch==' ') T=NULL;else{if(!(T=(BiThrTree)malloc(sizeof(BiThrNode)))) exit(1);T->data=ch;CreateBitree(T->lchild);CreateBitree(T->rchild);}//elsereturn OK;}//CreateBitreeint main(){BiThrTree T,Thrt;CreateBitree(T);//创建InOrderThreading(Thrt,T);//线索化InOrderTraverse_Thr(Thrt);//遍历访问return OK;}注意点:在输入字符创立二叉树时,要注意叶子结点的输入形式,即叶子结点的左右空指针也要输入,在我们这里输入空格。

(二)建立中序二叉树的递归算法,其中pre为全局变量。

BiThrNodeType *pre;BiThrTree InOrderThr(BiThrTree T){ /*中序遍历二叉树T,并将其中序线索化,pre为全局变量*/BiThrTree head;head=(BitThrNodeType *)malloc(sizeof(BiThrType));/*设申请头结点成功*/head->ltag=0;head->rtag=1;/*建立头结点*/head->rchild=head;/*右指针回指*/if(!T)head->lchild=head;/*若二叉树为空,则左指针回指*/ else{head->lchild=T;pre=head;InThreading(T);/*中序遍历进行中序线索化*/pre->rchild=head;pre->rtag=1;/*最后一个结点线索化*/head->rchild=pre;};return head;}void InThreading(BiThrTree p){/*通过中序遍历进行中序线索化*/if(p){InThreading(p->lchild);/*左子树线索化*/if(p->lchild==NULL)/*前驱线索*/{p->ltag=1;p->lchild=pre;}if(p->rchild==NULL)p->rtag=1;/*后驱线索*/if(pre!=NULL && pre->rtag==1) pre->rchild=p;pre=p;InThreading(p->rchild);/*右子树线索化*/}}进行中序线索化的算法:bithptr*pre; /* 全程变量*/voidINTHREAD(bithptr *p){if(p!=NULL){ INTHREAD(p->lchild); /* 左子树线索化*/if(p->lchild==NULL) {p->ltag=1;p->lchild=pre;}if(p->rchild==NULL) p->rtag=1;if(pre!=NULL && pre->rtag==1)pre->rchild=p;pre=p; /* 前驱指向当前结点*/INTHREAD(p->rchild); /* 右子树线索化*/}4 调试分析该程序在调试过程中遇到的问题是:对已学知识运用能力欠缺,尤其是在编程方面,由于C语言等计算机基础课程知识没有很好的掌握同时在学习数据结构时也没有真正弄懂导致编程时小错误不断,而且在遇到许多小的错误时靠自己很难再调整过来,但整体上是一次对所学知识的运用巩固,特别是对二叉树的建立以及对它进行线索方面,翻阅大量的书籍及搜集资料并求教于计算机系的同学,才找到一些解决问题的方法,在用中序遍历线索二叉树时总是搞混不过也因此让我对前序,中序,后序遍历更加理解。

相关文档
最新文档