数据结构之二叉树——二叉树的遍历

二叉树的遍历——四种遍历方式:

一、先序遍历(PreOrderTraverse)

按照 根—左—右 的顺序进行的遍历

算法实现如下:

void Visit(BTNode * node){ // 访问某结点,这里仅输出结点中的字符
	printf(%c  ”, node->data);
}


void PreOrderTraverse(BTNode * root) // 先序遍历
{
	// 若root非空,则先访问root,再按照先序遍历方式访问其左子树
	// 和右子树
	if ( root->data ){  //判断非空
	     Visit(root)  ;   //访问该结点
	     PreOrderTraverse(root->lchild)  ;   //先序遍历的方式访问该结点的左子树
	     PreOrderTraverse(root->rchild)  ;   //先序遍历的方式访问该结点的右子树
	}
}

二、中序遍历

按照 左—根—右 的顺序进行的遍历

void PostOrderTraverse(BTNode * root){
	if(root){
		if(root->data){
			PostOrderTraverse(root->lchild);
			Visit(root);
			PostOrderTraverse(root->rchild);
		}
	}
}

三、后序遍历

按照 左—右—根 的顺序进行的遍历

void PostOrderTraverse(BTNode * root){
if(root){
	if(root->data){
		PostOrderTraverse(root->lchild);
		PostOrderTraverse(root->rchild);
		Visit(root);
	}
}
}

四、层次遍历

借用队列的形式

思路:
1.根节点入队,然后出队,出队的时候传递值,执行访问,让它的子节点都入队;
2.根节点的子节点逐一出队,每出一个就传递值进行访问,并且引其子节点入队;
3.最后所有结点入队并出队,完成遍历。

算法实现如下:

#define MAX 100

typedef struct SqQueue{
	BTNode QueueData[MAX];
	int front, rear;
}SqQueue;



void InitQueue(SqQueue *q){
	q->front = q->rear = 0;
}

int QueueFull(SqQueue *q){
	if(q->rear == MAX -1)return 1;
	else return 0;
}

void EinQueue(SqQueue *q, BTNode * Elmt){		//其中,q中记录了要进入的位置,elmt指向要加入队列的元素
	int QueueFull(SqQueue *);
	if(QueueFull(q)){printf("Team Full!"); exit(0);}
	(q->QueueData[q->rear]) = *Elmt;
	q->rear = (q->rear+1) % MAX;
}

int QueueEmpty(SqQueue *q){
	return (q->rear - q->front <= 0);
}

BTNode* DeQueue(SqQueue *q){
	void EinQueue(SqQueue*, BTNode *);
	if(QueueEmpty(q)){printf("Team Empty!\n"); exit(0);}
	BTNode *Elmt;
	Elmt = &(q->QueueData[q->front]);
	q->front = (q->front + 1) % MAX;
	return Elmt;
}

void LevelTraverse(BTNode *root){
	BTNode * pelmt; SqQueue queue, *qw;			//总体思路:1.先让根节点1入队;(单独完成,在while循环之外)
	qw = &queue;							//			2.根节点1出队,同时其左右孩子2、3入队;
	//先初始化队列							//			3.结点2出队,其左右孩子4、5入队
	void InitQueue(SqQueue *);				//			4.结点3出队,其左右孩子6、7入队;
	InitQueue(qw);							//			5.循环往复,直到队列为空;(while循环,条件是不为空)
	
	
	//重复步骤2过程:
	while(!QueueEmpty(qw)){ //队不为空,则循环
		pelmt = DeQueue(qw);	//pelmt中存放的是要加入队列的元素的地址
		Visit(pelmt); //结点p出队,并对其进行访问
		if(pelmt->lchild) EinQueue(qw, pelmt->lchild); //有左孩子时将其进队
		if(pelmt->rchild) EinQueue(qw, pelmt->rchild); //有右孩子时将其进队
	}
}

二叉树的重构:

在已知先序遍历和中序遍历的结果的情况下重构二叉树

对于二叉树的三种遍历,知二求一
(用ppos表示先序遍历的结果,ipos来表示中序遍历的结果)

BTNode* RestoreTree(char *ppos, char *ipos, int n){
	BTNode *PTemp= (BTNode *)malloc(sizeof(BTNode));
	char *findroot = ipos; //用来在中序遍历中寻找根节点,以用来区分左右子树
	char *pMove = ipos; //用来移动
	int lnum = 0, rnum = 0;

	PTemp->data = *ppos; //ptemp是头节点,这是在给根节点赋值
	PTemp->lchild = PTemp->rchild = NULL; //只是最底层的最左叶子节点,所以其r/lchild值为NULL;
	//边界检测:
	if(*ppos == NULL || *ipos == NULL) return PTemp;
	//递归重构
	while(PTemp->data != *pMove) //寻找根节点在中序遍历字符串的哪个具体位置
	{
		pMove++;
		lnum++;
	}
	rnum = n - lnum-1; //右孩子总数 = 总数-左孩子-根节点
	if(lnum > 0){
		PTemp->lchild = RestoreTree(ppos+1, ipos, lnum);} //先不断递归这一条语句,直到ppos所指向的内容就是根节点中的内容
	if(rnum > 0){
		PTemp->rchild = RestoreTree(ppos+lnum+1, ipos+lnum+1, rnum);//这时所有的左子树已经出来了,剩下右边的,右边的同理
	}
	return PTemp;
}
其他与树相关的函数:
  1. 求结点总数:
int TotalNodes(BTNode *root){
	if(!root) return 0;
	else return 1 + TotalNodes(root->rchild) + TotalNodes(root->lchild);
}
  1. 求非叶子结点数的函数:
int TotalNoLeafNodes(BTNode * root){
	if(!root) return 0; // 若root没有任何子树,则非叶子结点数也为0
	else if(!(root->lchild || root->rchild)) return 0; //当root为空指针的时候,非叶子结点数为0
	else//否则,root结点本身就是非叶子结点,还加上其子树的非叶子结点数
		return 1 + TotalNoLeafNodes(root->rchild) + TotalNoLeafNodes(root->lchild);
}

  1. 求叶子结点数的函数:
int TotalLeafNodes(BTNode * root){
	return TotalNodes(root) - TotalNoLeafNodes(root);
}

  1. 求树深的函数:
int TotalNoLeafNodes(BTNode * root){
	if(!root) return 0; // 若root没有任何子树,则非叶子结点数也为0
	else if(!(root->lchild || root->rchild)) return 0; //当root为空指针的时候,非叶子结点数为0
	else//否则,root结点本身就是非叶子结点,还加上其子树的非叶子结点数
	return 1 + TotalNoLeafNodes(root->rchild) + TotalNoLeafNodes(root->lchild);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值