二叉树
- 12.在二叉树中查找值为x的结点,试编写算法打印值为x的结点的所有祖先结点,假设值为x的结点不多于一个。
- 13. 设一颗二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根结点的指针,p和q分别为指向该二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT,p,q,r)找到p和q的最近的公共祖先结点r.
- 14.编写后序遍历二叉树的非递归算法
- 15. 假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点数最多的那一层的结点个数)
- 15.设有一颗满二叉树(所有结点均不同),已知其先序序列为pre,设计一个算法求出其后序序列post。
- 16.设计一个算法将二叉树的叶结点按从左到右的顺序连城一个单链表,表头指针为head,二叉树 按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表
- 17.设计判断两颗二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或者T1的左子树和T2的右子树是相似的,且T1的右子树和T2的右子树是相似的。
- 18.写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法
- 19.《2014统考真题》二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和,给定一颗二叉树T,采用二叉链表存储,结点结构为 left|weight|right 其中叶结点的weight域保存该结点的非负权指,
- 20.《2017统考真题》请设计一个算法,将给定的表达树(二叉树)转换为等价的中缀表达式
12.在二叉树中查找值为x的结点,试编写算法打印值为x的结点的所有祖先结点,假设值为x的结点不多于一个。
- 算法的基本设计思想
采用非递归的后序遍历,利用辅助栈,当弹出栈顶元素判断值是否为x,然后此时栈中元素即为x结点的祖先结点 - 算法实现
// 在二叉树中查找值为x的结点,试编写算法打印值为x的结点的所有祖先结点
#include <iostream>
using namespace std;
#define Max 10
typedef struct treenode
{
char data;
treenode *lchild, *rchild;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
ch = getchar();
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->rchild = NULL;
t->lchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
typedef struct stack
{
treenode *t;
int tag;//tag=0表示左孩子被访问,tag=1表示右孩子被访问
} stack;
void rexfather(tree &t, char x)
{
if(t==NULL) cout<<"未找到"<<endl;
// 初始化栈
stack s[100];
int top=0;
while (t!=NULL||top>0)//树空栈空就退出
{
while(t!=NULL&&t->data!=x) //沿着左分支向下压入栈中
{
s[++top].t=t;
s[top].tag =0;
t=t->lchild;
}
if(t!=NULL&&t->data==x)//找到x
{
cout<<"所查结点的所有祖先结点的值为:"<<endl;
for(int i=1;i<=top;i++)//栈中元素都是祖先结点,
{
printf("%c",s[i].t->data);
}
break;
}
while(top!=0&&s[top].tag==1)//栈顶元素的左右孩子都被访问了退栈
top--;
if(top!=0)
{
s[top].tag=1;
t=s[top].t->rchild;
}
}
}
int main()
{
tree t;
buildtree(t);
rexfather(t,'G');
return 0;
}//ABD##E##CF##G##
- 小结
13. 设一颗二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根结点的指针,p和q分别为指向该二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT,p,q,r)找到p和q的最近的公共祖先结点r.
-
算法的基本设计思想
用两个栈,假设p在q左边先把p的祖先压栈s1,然后复杂这个栈到s2,然后找到p,将s1与s2匹配第一个相同的就是刚刚祖先r。 -
算法实现
// 设一颗二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根结点的指针,p和q分别为指向该二叉树中任意两个结点的
// 指针,试编写算法ANCESTOR(ROOT,p,q,r)找到p和q的最近的公共祖先结点r.
#include <iostream>
using namespace std;
typedef struct treenode
{
char data;
treenode *lchild, *rchild;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
ch = getchar();
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->lchild = NULL;
t->rchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
typedef struct stack
{
treenode *t;
int tag; // tag=0 左孩子被访问过,tag=1右孩子被访问过
} stack;
tree Ancestor(tree &t, treenode *p, treenode *q)
{
stack s1[10], s2[10];
int top1 = 0, top2 ;
while (t != NULL || top1 > 0) // 树和栈都空跳出
{
while (t != NULL) // 沿着左分支压入栈中
{
s1[++top1].t = t;
s1[top1].tag = 0;
t = t->lchild;
}
while (top1 != 0 && s1[top1].tag == 1) // 假定p是在q的左测
{
if (s1[top1].t == p) // 找到p后,栈中元素都是p 的祖先
{
for (int i = 1; i <= top1; i++)
s2[i] = s1[i]; // 将栈s1复制到栈s2
top2=top1;
}
if (s1[top1].t == q) // 找到q结点
{
for (int i = top1; i > 0; i--) // 将栈中元素的结点到s2中去匹配
for (int j = top2; j > 0; j--)
if (s2[j].t == s1[i].t)
return s1[i].t; //找到了
}
top1--; // 退栈
} // while
if (top1 != 0) //压右边
{
s1[top1].tag = 1;
t = s1[top1].t->rchild; // 工作指针t指向栈顶的右孩子 沿右分支向下遍历
}
} // while
return NULL; // 前面都没返回那就是没找到,p和q无公共结点;
}
int main()
{
tree t;
buildtree(t);
treenode *p = t->lchild->lchild, *q = t->lchild->rchild;
cout << Ancestor(t, p, q)->data << endl;
return 0;
}
// ABD##E##CF##G##
- 小结
用了tag位,tag=0 左孩子被访问过,tag=1右孩子被访问过;
和双栈。
14.编写后序遍历二叉树的非递归算法
-
算法的基本设计思想
-
算法实现
- 小结## 1.编写后序遍历二叉树的非递归算法
15. 假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点数最多的那一层的结点个数)
- 算法的基本设计思想
求宽度,最多那一层我们想到用层次遍历,求出每一层的宽度,然后比较大小; - 算法实现
// 假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点数最多的那一层的结点个数)
// 算法思路,求宽度,最多那一层我们想到用层次遍历,求出每一层的宽度,然后比较大小;
#include <iostream>
using namespace std;
#define Max 20
typedef struct treenode
{
char data;
treenode *rchild, *lchild;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
ch = getchar();
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->rchild = NULL;
t->lchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
// 队列结构
typedef struct queue
{
treenode *data[Max];
int level[Max]; // data中下标结点的层次
int f, r;
} queue;
int Btwidth(tree &t)
{
queue q; // 队列
tree p; // 保存出队的结点指向
int k; // 保存出队的结构层次
q.f = q.r = -1; // 头尾指针的初始化
q.data[++q.r] = t; // 根节点入队
q.level[q.r] = 1; // 刚入队的根结点的层次记为1;
while (q.f < q.r) // 层次遍历的循环条件,尾指针大于头指针
{
// 头指针出队;
p = q.data[++q.f]; // p指针保存刚刚出队的结点
k = q.level[q.f]; // 保存出队结点的层次;
if (p->lchild) // 有左孩子左孩子入队
{
q.data[++q.r] = p->lchild; // 左孩子入队
q.level[q.r] = k + 1; // 进入队列的结点的层次为出队的结点的层次+1;
}
if (p->rchild)
{
q.data[++q.r] = p->rchild;
q.level[q.r] = k + 1;
}
}
// 遍历的时候最大值更新,遍历下标,计算每层的个数
int maxx = 0, i = 0, n;
k = 1;
while (i <= q.r) // 遍历的下标小于队尾部
{
n = 0; // 一开始每层默认0个
while (i <= q.r && q.level[i] == k)
{
n++;
i++;
}
k = q.level[i];
if (n > maxx)
maxx = n;
}
return maxx;
}
int main()
{
tree t;
buildtree(t);
cout << Btwidth(t) << endl;
return 0;
}
// ABD##E##CF##G##
- 小结## 1.编写后序遍历二叉树的非递归算法
15.设有一颗满二叉树(所有结点均不同),已知其先序序列为pre,设计一个算法求出其后序序列post。
- 算法的基本设计思想
用递归,pre的第一个结点就是post的最后一个结点,然后每次去找到half=将剩下的结点划分为两部分去递归 - 算法实现
// 设有一颗满二叉树(所有结点均不同),已知其先序序列为pre,设计一个算法求出其后序序列post。
#include <iostream>
using namespace std;
void prepost(char pre[], int l1, int r1, char post[], int l2, int r2) // 数组就是给地址
{
int half;
if (l1 <= r1)//左边大于右边就退出递归, 递归条件是左边小于等于右边才能去左右切割递归;
{
post[r2] = pre[l1];
half = (r1 - l1) / 2;
prepost(pre, l1 + 1, l1 + half, post, l2, l2 + half - 1);
prepost(pre, l1 + half + 1, r1, post, l2 + half, r2 - 1);
}
}
int main()
{
char pre[8] = "ABDECFG", post[8];
prepost(pre, 0, 6, post, 0, 6);
for (int i = 0; i < 7; i++)
{
cout << post[i] << " ";
}
return 0;
}
- 小结
做这种题目先想到递归,然后大树化小数找中间的分界点,和结点之间的逻辑关系 -
16.设计一个算法将二叉树的叶结点按从左到右的顺序连城一个单链表,表头指针为head,二叉树 按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表
- 算法的基本设计思想
用中序递归遍历 (左根右) - 算法实现
//设计一个算法将二叉树的叶结点按从左到右的顺序连城一个单链表,表头指针为head,
//二叉树 按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表
//由题目很容易想到用中序递归遍历。
#include <iostream>
using namespace std;
typedef struct treenode {
char data;
treenode *lchild,*rchild;
}treenode,*tree;
void buildtree(tree &t)
{
char ch;
ch=getchar();
if(ch=='#') t=NULL;
else{
t=(tree)malloc(sizeof(treenode ));
t->data=ch;
t->lchild=NULL;
t->rchild=NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
void disp(tree &t)
{
if(t)
{
cout<<t->data<<" ";
disp(t->lchild);
disp(t->rchild);
}
}
tree head=(tree)malloc(sizeof(treenode)),pre=NULL;//初始化一个head结点,和一个前驱结点用来保存下一个结点的前驱结点初始为NULL;
tree Inorder(tree &t)//最后返回后head
{
if(t)//递归条件
{
Inorder(t->lchild);//找最左下的结点
if(t->rchild==NULL&&t->rchild==NULL)//判断这个最左下的结点是不是叶子结点
{
//判断这个叶子结点是不是第一个叶子结点,前驱是NULL就是第一个
if(pre==NULL)
{
head=t;
pre=t;
}
else
{
pre->rchild=t;
pre=t;
}
}
Inorder(t->rchild);//再去右子树找
t->rchild=NULL; //注意这里需要将最后一个结点的右孩子指向空
}
return head;
}
int main(){
tree t;
buildtree(t);
disp(t);
Inorder(t);
while(head)
{
cout<<head->data<<" ";
head=head->rchild;
}
return 0;
}//ABD##E##CF##G##
- 小结
注意最后结点需要指向空 -
17.设计判断两颗二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或者T1的左子树和T2的右子树是相似的,且T1的右子树和T2的右子树是相似的。
- 算法的基本设计思想
相似: 空和空 || 非空和非空
不相似 : 空 和非空
递归判断左边和右边,用int返回类型的递归函数 - 算法实现
/* 设计判断两颗二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;
或者T1的左子树和T2的右子树是相似的,且T1的右子树和T2的右子树是相似的。 */
#include <iostream>
using namespace std;
typedef struct treenode
{
char data;
treenode *rchild, *lchild;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
cin>>ch;
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->rchild = NULL;
t->lchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
int similar(tree &t1, tree &t2)
{
int left,right;
if (t1 == NULL && t2 == NULL)
return 1;
else if (t1==NULL||t2==NULL)
return 0;
else
{
left = similar(t1->lchild, t2->lchild);
right = similar(t1->rchild, t2->rchild);
}
return (left && right);
}
int main()
{
tree t1, t2;
cout << "创建第一个二叉树:" << endl;
buildtree(t1);
cout << "创建第二个二叉树:" << endl;
buildtree(t2);
if (similar(t1, t2))
cout << "相似" << endl;
else
cout << "不相似" << endl;
return 0;
}
后序的前驱:q
1)p有右孩子,q->p
2)else if p有左孩子 , q->p
3)else if p的左孩子是空 q = NULL
4)else p的最上祖先的左孩子存在 q指向它 没有就是NULL
- 算法实现
/* 写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法 */
#include <iostream>
using namespace std;
typedef struct treenode
{
char data;
treenode *rchild, *lchild;
int ltag, rtag;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
cin >> ch;
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->ltag = 0;
t->rtag = 0;
t->lchild = NULL;
t->rchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
void zx(tree &t, treenode *&pre)
{
if (t)
{
zx(t->lchild, pre);
if (t->lchild == NULL)
{
t->lchild = pre;
t->ltag = 1;
}
if (pre != NULL && pre->rchild == NULL)
{
pre->rchild = t;
pre->rtag = 1;
}
pre = t;
zx(t->rchild, pre);
}
}
tree fpostpre(tree &t, treenode *p)
{
treenode *q;
if (p->rtag == 0)
q = p->rchild; // 有右孩子
else if (p->ltag == 0)
q = p->lchild; // 有左孩子
else if (p->lchild == NULL)
q = NULL; // 中序第一个结点
else
{
while (p->ltag == 1 && p->lchild != NULL)
p = p->lchild; // 沿着线索找到最上祖先结点
if (p->lchild)
q = p->lchild; // 如果这个祖先结点有左孩子,q=它
else
q = NULL;
}
return q;
}
int main()
{
tree t;
buildtree(t);
treenode *pre = NULL;
zx(t, pre);
cout << fpostpre(t, t->rchild)->data << endl;
return 0;
} // ABD##E##CF##G##
- 小结
分析好后序的前驱结点的存在可能 -
19.《2014统考真题》二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和,给定一颗二叉树T,采用二叉链表存储,结点结构为 left|weight|right 其中叶结点的weight域保存该结点的非负权指,
设root为指向T的根节点的指针,请设计求T的WPL算法
- 算法的基本设计思想
递归 - 算法实现
/* 《2014统考真题》二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和,
给定一颗二叉树T,采用二叉链表存储,结点结构为 left|weight|right 其中叶结点的weight域保存该结点的非负权指,
设root为指向T的根节点的指针,请设计求T的WPL算法*/
#include <iostream>
using namespace std;
typedef struct treenode {
char data;
treenode *rchild,*lchild;
}treenode ,*tree;
void buildtree(tree &t)
{
char ch;
cin >>ch;
if(ch=='#') t=NULL;
else{
t=(tree)malloc(sizeof(treenode ));
t->data=ch;
t->lchild=NULL;
t->rchild=NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
int wpl(tree &t,int deep )
{
static int ans=0;
if(t->lchild==NULL&&t->rchild==NULL)//是叶子结点
{
return ans+=(deep*((t->data)-'0'));
}
if(t->lchild)
wpl(t->lchild,deep+1);
if(t->rchild)
wpl(t->rchild,deep+1);
return ans;
}
int main(){
tree t;
buildtree(t);
cout<<wpl(t,0)<<endl;
return 0;
}
/*
1
2 3
4 5 6 7
124##5##36##7##
ans=(4+5+6+7)*2=44
*/
(通过括号反映操作符的计算次序)并输出
- 算法的基本设计思想
从树的根节点左孩子用括号括起来,右孩子用括号括起来,叶子结点不用括,中序递归 - 算法实现
/* 《2017统考真题》请设计一个算法,将给定的表达树(二叉树)转换为等价的中缀表达式
(通过括号反映操作符的计算次序)并输出 */
#include <iostream>
using namespace std;
typedef struct treenode
{
char data;
treenode *rchild, *lchild;
} treenode, *tree;
void buildtree(tree &t)
{
char ch;
cin >> ch;
if (ch == '#')
t = NULL;
else
{
t = (tree)malloc(sizeof(treenode));
t->data = ch;
t->lchild = NULL;
t->rchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
void toexp(tree &t, int deep)
{
if (t == NULL)
return; // 树空直接访回
else if (t->lchild == NULL && t->rchild == NULL) // 叶子结点输出data
{
cout << t->data;
}
else // 有左右孩子进行递归
{
if (deep > 1)
cout << '(';
if (t->lchild)//左
toexp(t->lchild, deep + 1);
cout << t->data;//print计算符
if (t->rchild)//右
toexp(t->rchild, deep + 1);
if (deep > 1)
cout << ')';
}
}
int main()
{
tree t;
buildtree(t);
toexp(t, 1);
return 0;
}
/*
*
+ *
a b c -
*+a##b##*c##-#d##
- 小结
递归