整点彩虹屁: 💨
一.先序
先序的特点是,从上到下,从根开始处理每个子树。
可用来从上到下构建二叉树,或者从上到下对二叉树进行整体操作。
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+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);
}
}
}