数据结构7-二叉树-递归与非递归遍历操作及应用

二叉树的遍历及应用

二叉树的遍历就是尊从某种顺序,访问二叉树中的所有节点,使得每一个节点被访问有且仅有1次。二叉树是一种非线性结构,因此按照某种规则遍历二叉树实际就是:试图找到二叉树节点的一个线性序列。

一、递归遍历算法

根据访问节点的顺序,我们将遍历分为前序,中序,后序三种。
先定义二叉树节点:

#include <stdio.h>
#include <stdlib.h>
#include <stack>
using namespace std;
typedef struct node {
	char data;
	struct node* lchild;
	struct node* rchild;
}BTNode;

1.前序

前序遍历按照以下的步骤递归执行:

  • 访问根节点
  • 先序遍历左子树
  • 先序遍历右子树

通俗的理解为:遇到一个节点就访问一个数据,随后一路往左下走,左下没路了,再往右下走。

void PreOrder_recur(BTNode* bt) {
	//DLR
	if (bt != NULL) {
		printf("%c", bt->data);
		PreOrder_recur(bt->lchild);
		PreOrder_recur(bt->rchild);
	}
}

2.中序

中序遍历按照以下的步骤递归执行:

  • 中序遍历左子树
  • 访问根节点
  • 中序遍历右子树

通俗的理解为:一路往左下走,直到没路可走时,访问数据,随后在看看右下能不能走。

void InOrder_recur(BTNode* bt) {
	//LDR
	if (bt != NULL) {
		InOrder_recur(bt->lchild);
		printf("%c", bt->data);
		InOrder_recur(bt->rchild);
	}
}

3.后序

后序遍历按照以下的步骤递归执行:

  • 后序遍历左子树
  • 后序遍历右子树
  • 访问根节点
    通俗的理解为:一路往左下走,再一路往右下走,知道左右都走不下去了,再访问数据。
void PostOrder_recur(BTNode* bt) {
	//LRD
	if (bt != NULL) {
		PostOrder_recur(bt->lchild);
		PostOrder_recur(bt->rchild);
		printf("%c", bt->data);
	}
}

二、递归遍历算法的应用举例

1.创建二叉树

首先我们需要扩充二叉树,在空子树处添加虚节点 ∗ * 。如下图所示
在这里插入图片描述
要采用先序递归遍历算法构建这样一棵树,我们需要往函数传入一个数组str=['A','B','D','*','F','*','*','*','C','E','*','*','*']
其实*号是为了告诉程序这里应置空,下面没有元素了。

void Recur_CreateBinTree(BTNode* bt,char* str) {
	//采用先序序列的遍历方法
	if (*str != ';')
		return;//终止符
	if(*str!='*'){//不是虚节点
		bt = (BTNode*)malloc(sizeof(BTNode));
		bt->data = *str++;//生成节点
		Recur_CreateBinTree(bt->lchild, str);
		Recur_CreateBinTree(bt->rchild, str);
	}
	else {//如果是虚节点,置空
		bt = NULL;
	}
}

2.计算二叉树叶节点个数

首先要有终止条件:

  • 如果当前节点为空,说明已经触底,返回0
  • 如果当前节点的左右儿子都是空节点,说明自己是叶子节点,返回1

递归:
站在根节点处,那么这棵树叶节点的个数是左子树的叶节点数+右子树的叶节点数

int CountLeaves(BTNode* bt) {//返回叶子节点的个数
	if (bt == NULL)
		return 0;
	if (bt->lchild == NULL && bt->rchild == NULL) {
		return 1;
	}
	else {
		return CountLeaves(bt->lchild) + CountLeaves(bt->rchild);
	}
}

3.计算二叉树的深度

int BinTreeDepth(BTNode* bt) {
	if (bt == NULL) return 0;
	else {
		int left_depth = BinTreeDepth(bt->lchild);
		int right_depth = BinTreeDepth(bt->rchild);
		return (left_depth < right_depth) ? right_depth + 1 : left_depth + 1;
	}
}

三、非递归遍历算法

要抓住各个节点的前驱和后继。

1.前序遍历

基本思想:利用栈stack
操作步骤:

  • 1.将根节点压入stack
  • 2.如果stack不为空,就取出栈顶元素(记作p)
  • 3.如果p不是NULL,那么访问p的数据。接着将p的右儿子,左儿子(包括空指针)依次压入栈中
  • 4.回到第二步,循环
void pre_order(BTNode* bt) {
	stack<BTNode*>sta;
	sta.push(bt);
	while (!sta.empty()) {
		BTNode* p = sta.top();
		sta.pop();
		if (bt != NULL) {
			cout << p->data << endl;
			sta.push(p->rchild);
			sta.push(p->lchild);
		}
	}
}

2.中序遍历

基本思想:中序遍历总是优先访问左下的那个元素。所以在第一次遇到某个节点时,只要非空就会压入栈中。之后从栈中取出元素访问并会访问右边的元素。——先进左子树再访问,p的前驱是左儿子
操作步骤:

  • 1.创建指针p指向根节点
  • 2.只要p还不是NULL,就一直往左下走(后继为左儿子),并把路过的节点压入栈中。
  • 3.p已经是NULL,将栈顶元素(正是NULL的前驱)取出赋值给p,同时访问p的数据,并且进入p的右子树。
  • 4.只要p!=NULL或者stack没有空,就回到步骤2,循环。
void in_order(BTNode* bt) {
	stack<BTNode*>sta;
	BTNode* p = bt;
	while (p || !sta.empty()) {
		if (p) {
			sta.push(p);
			p = p->lchild;
		}
		else {
			p = sta.top();
			sta.pop();
			cout << p->data << endl;
			p = p->rchild;
		}
	}
}

3.后续遍历

基本思想:与中序遍历不同,后续遍历需要先进入右子树,最后才访问数据。即对于节点p,需要先进入右子树再去栈顶元素访问。p的前驱是右儿子。
操作步骤:

  • 1.创建指针p指向根节点
  • 2.只要p还不是NULL,就一直往左下走(后继为左儿子),并把路过的节点压入栈中。
  • 3.p已经是NULL,将栈顶元素赋值给p(不能pop出来),进行判断,如果p的右子树不存在或未被访问就进入p的右子树,否则访问p的数据。
  • 只要p!=NULL或者stack没有空,就回到步骤2,循环。
void post_order(BTNode* bt) {
	stack<BTNode*>sta;
	BTNode* p = bt, * pre = NULL;
	while (p || !sta.empty()) {
		if (p) {
			sta.push(p);
		}
		else {
			p = sta.top();
			if (p->rchild != NULL && p->rchild != pre) {
				//p的右儿子非空,且p的右儿子不能是刚刚访问过的
				p = p->rchild;
			}
			else {
				sta.pop();
				cout << p->data << endl;
				pre = p;
				p = NULL;//p取空,方便下一轮循环取栈顶元素
			}
		}
	}
}

四、根据访问顺序倒推二叉树

  • 只需要(前序,中序)(中序,后序)的访问顺序即可。
  • 前序遍历的第一个元素就是树根
  • 后续遍历的最后一个元素是树根
  • 中序遍历树根在中间
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值