【数据结构】树和二叉树3——遍历二叉树和线索二叉树


遍历二叉树

  • 遍历定义:顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次(又称周游)。
  • 遍历目的:得到树中所有结点的一 个线性排列。
  • 遍历用途:它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心。
  • 遍历方法:依次遍历二叉树中的三个组成部分,便是遍历了整个二叉树。
    在这里插入图片描述

1、遍历二叉树算法描述

假设:遍历左子树L;访问根结点D;遍历右子树R。
则遍历整个二叉树方案共有:
DLR、LDR、LRD、DRL、RDL、 RLD六种。
若规定先左后右,则只有前三种情况:
在这里插入图片描述
在这里插入图片描述

2、遍历二叉树操作定义

先序遍历

若二叉树为空,则空操作;否则
(1)访问根结点D;
(2)先序遍历左子树L;
(3)先序遍历右子树R。
在这里插入图片描述

中序遍历

若二叉树为空,则空操作;否则
(1)中序遍历左子树L;
(2)访问根结点D;
(3)中序遍历右子树R。
在这里插入图片描述

后序遍历

若二叉树为空,则空操作;否则
(1)后序遍历左子树L;
(2)后序遍历右子树R;
(3)访问根结点D。
在这里插入图片描述
例1:
在这里插入图片描述
例2:
在这里插入图片描述

3、递归遍历算法

先序遍历

Status PreOrderTraverse(BiTree T){
	if(T==NULL)//空二叉树
		return ok;
	else
		visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
		PreOrderTraverse(T->lchild);//递归遍历左子树
		PreOrderTraverse(T->rchild);//递归遍历右子树
}

时间复杂度:O(n)  //每个结点只访问一次
空间复杂度:O(n)  //栈占用的最大辅助空间

中序遍历

Status InOrderTraverse(BiTree T){
	if(T=NULL)//空二叉树
		return OK;
	else
		InOrderTraverse(T->lchild);//递归遍历左子树
		visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
		InOrderTraverse(T->rchild);//递归遍历右子树
}

时间复杂度:O(n)  //每个结点只访问一次
空间复杂度:O(n)  //栈占用的最大辅助空间

后序遍历

Status PostOrderTraverse(BiTree T){
	if(T==NULL)
		return OK;//空二叉树
	else
		PostOrderTraverse(T->lchild);//递归遍历左子树
		PostOrderTraverse(T->rchild);//递归遍历右子树
		visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
}

时间复杂度:O(n)  //每个结点只访问一次
空间复杂度:O(n)  //栈占用的最大辅助空间

4、中序遍历非递归算法

二叉树中序遍历的非递归算法的关键:在中序遍历过某结点的整个左子树后,如何找到该结点的根以及右子树。

算法基本思想:使用一个栈
(1) 建立一个栈;
(2) 根结点进栈,遍历左子树;
(3) 根结点出栈,输出根结点,遍历右子树。

Status InOrderTraverse(BiTree T){
	BiTree p;//p指向根结点
	InitStack(S);
	p=T;
	while(p||!StackEmpty(S)){
		if(p)
			push(S,p);
			p=p->lchild;
		else
			Pop(S,q);//q指向输出的结点
			printf("%c",q->data);
			p=q->rchild;
	}
	return OK;
}

5、层次遍历算法

对于一颗二叉树,从根结点开始,按从上到下、从左到右的顺序访问每一个结点。每一个结点仅仅访问一次。
在这里插入图片描述

算法基本思路:使用一个队列
(1) 建立一个队列;
(2) 将根结点进队;
(3) 队不空时循环:从队列中出列一个结点*p,访问它;若它有左孩子结点,将左孩子结点进队;若它有右孩子结点,将右孩子结点进队。

typedef struct{//使用队列类型定义
	BTNode data[MaxSize];//存放队中元素
	int front,rear;//队头和队尾指针
}SqQueue;//顺序循环队列类型
void LevelOrder(BTNode *b){
	BTNode *p;
	SqQueue *qu;
	InitQueue(qu);//初始化队列
	enQueue(qu,b);//根结点指针进入队列
	while(!QueueEmpty(qu)){//队不为空,则循环
		deQueue(qu,p);//出对结点p
		printf("%c",p->data);//访问结点p
		if(p->lchild!=NULL)//有左孩子时将其进队
			enQueue(qu,p->lchild);
		if(p->rchild!=NULL)//有右孩子时将其进队
			enQueue(qu,p->rchild);
	}
}

6、二叉树遍历算法的应用

二叉树的建立

在这里插入图片描述

Status CreateBiTree(BiTree &T){
	scanf(&ch);//cin>>ch;
	if(ch=="#")
		T=NULL;
	else
		if(!(T=(BiTNode*)malloc(ziseof(BiTNode))))//T=new BiTNode
			exit(OVERFLOW);
		T->data=ch;//生成根结点
		CreateBiTree(T->lchild);//构造左子树
		CreateBiTree(T->rchild);//构造右子树
	return OK;
}  //CreateBiTree

复制二叉树

【算法步骤】
如果是空树,递归结束;
否则,申请新结点空间,复制根结点
递归复制左子树
递归复制右子树

int Copy(BiTree T,BiTree &NewT){
	if(T==NULL)
		NewT=NULL;
		return 0;
	else
		NewT=new BiTNode;
		NewT->data=T->data;
		Copy(T->lchild,NewT->lchild);
		Copy(T->rchild,NewT->rchild);
}

计算二叉树的深度

【算法步骤】
如果是空树,则深度为0;
否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1。

int Depth(BiTree T){
	if(T==NULL)//如果是空树返回0
		return 0;
	else
		m=Depth(T->lchild);
		n=Depth(T->rchild);
		if(m>n)
			return (m+1);
		else
			return (n+1);
}

计算二叉树结点数

【算法步骤】
如果是空树,则结点个数为0;
否则,结点个数为左子树的结点个数+右子树的结点个数再+1。

int NodeCount(BiTree T){
	if(T==NULL)
		return 0;
	else
		return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}

求叶子结点数

【算法步骤】
如果是空树,则叶子结点个数为0;
否则,为左子树的叶子结点个数+右子树的叶子结点个数。

int LeafCount(BiTree T){
	if(T==NULL)
		return 0;
	if(T->lchild==NULL&&T->rchild==NULL)
		return 1;//如果是叶子结点返回1
	else
		return LeafCount(T->lchild)+LeafCount(T->rchild);
}

线索二叉树

定义:
利用二叉链表中的空指针域。如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继。——这种改变指向的指针称为“线索”。
加上了线索的二叉树称为线索二叉树(Threaded Binary Tree)
对二叉树按某种遍历次序使其变为线索二叉树的过程叫线索化

在这里插入图片描述
为区分Irchid和rchild指针到底是指向孩子的指针,还是指向前驱或者后继的指针,对二叉链表中每个结点增设两个标志域Itag和rtag,并约定:
Itag=0 Ichild指向该结点的左孩子
rtag=0 rchild指向该结点的右孩子
Itag=1 Ichild指向该结点的前驱
rtag=1 rchild指向该结点的后继
在这里插入图片描述

typedef struct BiThrNode{
	int data;
	int ltag,rtag;
	struct BiThrNode *lchild,rchild;
}BiThrNode,*BiThrTree;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为避免悬空态,增设了一个头结点:
Itag=O,lchild指向根结点,
rtag=1,rchild指向遍历序列中最后一个结点,
遍历序列中第一个结点的Ic域和最后一个结点的rc域都指向头结点,这样处理起来更方便。

在这里插入图片描述


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值