前、中、后序列遍历的应用


整点彩虹屁: 💨

一.先序

先序的特点是,从上到下,从根开始处理每个子树。
可用来从上到下构建二叉树,或者从上到下对二叉树进行整体操作。

1.先序构建
拓展二叉树的先序序列,可以通过输入的方式来构建二叉树。
💨二叉树构建详解

2.交换所有左右子树

/*Q9 交换所有结点的左右子树*/
void Q9(BiTree& T) {
	if (T) {
		BiNode* p = T->lchild;
		T->lchild = T->rchild;
		T->rchild = p;
		Q9(T->lchild);
		Q9(T->rchild);
	}
}

3.叶操作

//先序
int sum = 0;
void PreQ19(Q19Tree T,int level) {     //将level作为参数传入
	if (T) {
		if (T->lchild == 0 && T->rchild == 0)    //叶子结点,直接计算权值
			sum += T->weight * level;
		else {
			PreQ19(T->lchild, level + 1);
			PreQ19(T->rchild, level + 1);
		}
	}
}

二.中序

先序+中序构建
我们可以通过先序+中序、后序+中序、层序+中序来唯一确定二叉树。为何什么都需要中序,因为在已知根的情况下,中序可以划分左右子树!

💨二叉树构建详解

中缀表达式

将一个表达式树,转化成中缀表达式输出,需要在适当的位置加上括号。

*
+
*
a
c
_
b
d

转化成中缀:(a+b)*(c*(-d))
我们只需要按照中序遍历,进行输出即可输出相应的表达式,但是还需要加上括号,对于每一棵子树(除了二叉树本身),都是 "(左子树 根 右子树)",即在左子树递归前输出"(",右子树递归后输出")"。因为与层次有关,我们需要追踪递归所在层,因此将层次作为参数deep传入。

typedef struct node {
	char data[10]; 		//不太明白为什么给data[10],给char data;即可
	node* left, * right;
}BTree;
//除了第一层外,都是(左子树,根,右子树),加一个depth做参数
void BtreeToExp(BTree* T, int deep) {	//deep初始值为1
	if (T == NULL)
		return;
	else if (T->left == NULL && T->right == NULL)
		cout << T->data[0];
	else {
		if (deep > 1)      //非第一层,输出")"
			cout << "("; 
		BtreeToExp(T->left, deep + 1);
		cout << T->data[0];         //中序输出
		BtreeToExp(T->right, deep + 1);
		if (deep > 1)      //非第一层,输出")"
			cout << ")";
	}
}

三.后序

1.递归遍历
\qquad 后序非递归遍历也可以实现后序递归遍历的功能,出栈顺序即LRN,这里用递归是因为递归遍历代码精简,易于理解,并且不需要开辟O(n)的空间做栈,

(1)销毁二叉树

因为后序遍历总是LRN,每次取左右子树,再取下根,从下向上,像摘葡萄一样得,逐层瓦解,释放空间。

void PostDelete(BiTree& T) {
	if (T) {
		PostDelete(T->lchild);
		PostDelete(T->rchild);
		delete(T);        //从下向上删除
		T = NULL;
	}
}

删除以值为x的结点为根的二叉树,并释放空间
先序+后序:用先序从上到下找根,再用后序从下到上删除。

void Q11(BiTree &T, char x) {    //自上而下找值
	if (T) {
		if (T->data == x)
			PostDelete(T);
		else {
			Q11(T->lchild, x);
			Q11(T->rchild, x);
		}
	}
}

2.非递归遍历
非递归的特点:
出栈顺序为LRN。
每一时刻,栈中都是栈顶到根的路径(全是祖先)。

(2)求高度

💨求二叉树高度详解

(3)找祖先

1.找到某一结点的祖先

/*Q12 打印值位x的结点的所有的祖先*/
//直接后序非递归,输出栈即可
void Q12(BiTree T,char x) {
	BiTree S[100];
	int top = -1;
	BiNode* p = T, * r = NULL;
	while (p || top != -1) {
		if (p) {
			S[++top] = p;
			p = p->lchild;
		}
		else {
			p = S[top];
			if (p->rchild && p->rchild != r) 
				p = p->rchild;
			else {
				p = S[top--];
				if (p->data == x)      //若找到该结点,直接将栈内数据全输出
					while (top != -1) {
						p = S[top--];
						cout << p->data;
					}
				r = p;
				p = NULL;
			}
		}
	}
}

2.找到任意结点p、q的公共祖先
\qquad 我们可以先找到p的所有祖先,保存下来,然后找到q的所有祖先,进行比较,从后向前第一个相同的祖先即是公共祖先。

//初想法:暴力破解,直接后序分别压栈,将两个栈比对,最近相同的即是
void Q13(BiTree T, BiNode* p, BiNode* q, BiNode*& r) {      //不妨假设p在q的左边
	BiTree S[100];
	int top = -1;
	BiTree tp[100];   //辅助的数组
	BiNode* t = T, * tr = NULL;
	while (t || top!=-1) {
		if (t) {
			S[++top] = t;
			t = t->lchild;
		}
		else {
			t = S[top];
			if (t->rchild && t->rchild != tr)
				t = t->rchild;
			else {
				t = S[top--];
				if (t == p)   //找到p了,将p的栈内容存入tp
					for (int i = top; i >= 0; i--) 
						tp[i] = S[i];

				if (t == q) {  //找到q,将栈数据与tp对比,找到第一个相同的
					for (int i = top; i >= 0; i--)
						for (int j = sizeof(tp) / sizeof(tp[0]) - 1; j >= 0; j--) //判断数组元素个数:sizeof(a)/sizeof(a[0])
							if (S[i] == tp[j]) {
								r = tp[j];
								return;
							}
				}
				tr = t;
				t = NULL;
			}
		}
	}
}

三种通用

叶操作

1.将叶结点从左到右,用rchild串起来,头指针为head
此处用中序实现:

BiNode* head , * p = NULL;    //p用来向后遍历叶子
void leaf(BiTree T) {
	if (T) {
		leaf(T->lchild);
		if (T->lchild == NULL && T->rchild == NULL) { //找到叶子
			if (p == NULL) {	//第一个叶子
				head = T;
				p = T;
			}
			else {
				p->rchild = T;
				p = p->rchild;
			}
		}
		leaf(T->rchild);
	}
}

2.求二叉树的带权路径长度
带权路径长度= ∑ \sum 叶结点权值 × \times × 根到叶结点路径长度(叶层数-1)

//先序
int sum = 0;
void PreQ19(Q19Tree T,int level) {     //将level作为参数传入
	if (T) {
		if (T->lchild == 0 && T->rchild == 0)    //叶子结点,直接计算权值
			sum += T->weight * level;
		else {
			PreQ19(T->lchild, level + 1);
			PreQ19(T->rchild, level + 1);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值