王道——数据结构——栈和队列(1)
王道——数据结构——树与二叉树(1)
王道——数据结构——树与二叉树(2)
王道——数据结构——树与二叉树(4)
本文为王道数据结构的第五章——树与二叉树的编程题。
运行软件:vscode
使用c++文件编写
本文所使用的树为《王道——数据结构——树与二叉树(1)》中建立的树
11、已知二叉树以二叉链表存储,编写算法完成:对于树中的每个元素值为x的结点,删去以它为根节点的子树,并释放相应的空间。
// 第十一题 使用层次遍历主树,发现结点值为x时,对x的子树进行后续遍历,访问各结点时删除结点
// 复杂版
void test11(){
BiTree T;
InitBiTree(T);
buildTree(T);
PreOrder(T);
int x;
printf("\n请输入你想删除的元素值:");
scanf("%d", &x);
BiTNode *p, *temp, *last, *par;
LinkQueue Q; // 使用队列进行层次遍历,只有结点不为x的值才能入队
InitLinkQueue(Q);
LinkStack S;
InitLinkStack(S); // 使用栈进行后续遍历
EnQueue(Q, T);
while(Q.front->next != NULL){
DeQueue(Q, p);
temp = p->lchild;
if(temp && temp->data.value == x){ // 删去以x为根节点的子树
while (temp || S != NULL){
if(temp){
Push(S, temp);
temp = temp->lchild;
}
else{
if(S->data->rchild != last && S->data->rchild != NULL)
temp = S->data->rchild;
else{
Pop(S, last);
free(last);
}
}
}
p->lchild = NULL;
}
else if(temp != NULL)
EnQueue(Q, p->lchild);
temp = p->rchild;
if(temp && temp->data.value == x){ // 删去以x为根节点的子树
while (temp || S != NULL){
if(temp){
Push(S, temp);
temp = temp->lchild;
}
else{
if(S->data->rchild != last && S->data->rchild != NULL)
temp = S->data->rchild;
else{
Pop(S, last);
free(last);
}
}
}
p->rchild = NULL;
}
else if(p->rchild != NULL)
EnQueue(Q, p->rchild);
}
PreOrder(T);
}
// 简洁版 通过函数调用实现
void Delechild(BiTree T){
if(T->lchild != NULL)
Delechild(T->lchild);
if(T->rchild != NULL)
Delechild(T->rchild);
free(T);
}
void test11_2(){
BiTree T;
InitBiTree(T);
buildTree(T);
PreOrder(T);
int x;
printf("\n请输入你想删除的元素值:");
scanf("%d", &x);
BiTNode *p;
LinkQueue Q; // 使用队列进行层次遍历,只有结点不为x的值才能入队
InitLinkQueue(Q);
EnQueue(Q, T);
while(Q.front->next != NULL){
DeQueue(Q, p);
if(p->lchild != NULL){
if(p->lchild->data.value == x){
Delechild(p->lchild);
p->lchild = NULL;
}
else
EnQueue(Q, p->lchild);
}
if(p->rchild != NULL){
if(p->rchild->data.value == x){
Delechild(p->rchild);
p->rchild = NULL;
}
else
EnQueue(Q, p->rchild);
}
}
PreOrder(T);
}
12、在二叉树中查找值为x的结点,试编写算法(用C语言)打印值为x的结点的所有祖先节点,假设值为x的结点不多于一个
// 第十二题(用C) 使用后续遍历,设立标志tag=0,当发现x时tag变为1,访问节点时判断tag是否为1,如果为1打印输出该结点
int test12(BiTree T, int x, int tag){
if(T->data.value == x)
tag = 1;
if(tag == 0 && T->lchild != NULL)
tag = test12(T->lchild, x, tag);
if(tag == 0 && T->rchild != NULL)
tag = test12(T->rchild, x, tag);
if(tag == 1 && T->data.value != x)
printf("%d ", T->data.value);
return tag;
// 运行代码
// BiTree T;
// InitBiTree(T);
// buildTree(T);
// int x, tag = 0;
// printf("请输入你想查找的数字:");
// scanf("%d", &x);
// x = test12(T, x, tag);
}
13、设一棵二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根节点的指针,p和q分别为指向二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT, p, q, r),找到p和q的最近公共结点r。
// 第十三题 使用后续遍历的方法建立两个栈分别保存p,q的祖先结点,假设p在q左边,则必先遍历到p再遍历到q
// 使用新的栈对两个栈进行逆置,分别对两个新的栈进行遍历找到最近公共结点
// 找a结点所有的祖先节点并保存在栈中
LinkStack Stackparent(BiTree T, int a){
LinkStack S;
InitLinkStack(S);
BiTNode *p = T, *last;
while (p || S!= NULL)
{
if(p){
Push(S, p);
p = p->lchild;
}
else{
if(S->data->rchild != NULL && S->data->rchild != last)
p = S->data->rchild;
else{
if(S->data->data.value == a)
break;
Pop(S, last);
}
}
}
return S;
}
void test13(){
BiTree T;
InitBiTree(T);
buildTree(T);
printf("请输入你想查找的第一个结点值:");
int a, b;
scanf("%d", &a);
printf("请输入你想查找的第二个结点值:");
scanf("%d", &b);
LinkStack S1, S2, SN1, SN2;
InitLinkStack(S1);
InitLinkStack(S2);
InitLinkStack(SN1);
InitLinkStack(SN2);
BiTNode *temp;
S1 = Stackparent(T, a); // 找a的所有祖先节点
S2 = Stackparent(T, b); // 找b的所有祖先结点
while(S1 != NULL){ // 将S1逆置
Pop(S1, temp);
Push(SN1, temp);
}
while(S2 != NULL){ // 将S2逆置
Pop(S2, temp);
Push(SN2, temp);
}
StackNode *p = SN1, *q = SN2;
while(p->next != NULL && q->next != NULL){ // 如果遍历的其中一个栈的最后一个元素还没有找到不同的元素,那么其中一个结点是另一个结点的孩子结点
if(p->next->data == q->next->data){ // 从一棵树找到的两个结点,根节点肯定是公共结点,这里直接从第二个结点开始计算
p = p->next;
q = q->next;
}
else
break;
}
printf("最近公共结点值为:%d",p->data->data.value);
}
14、假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度。
// 第十四题 使用队列进行层次遍历,用weigh计算每层结点个数,weighest保存最宽的层结点数
// 使用last指针指向每层的最后一个元素,当访问到last指针时,last指针指向下一层最后一个元素
void test14(){
BiTree T;
InitBiTree(T);
buildTree(T);
LinkQueue Q;
InitLinkQueue(Q);
BiTNode *p = T, *last = T;
int weight = 0, weightest = 0;
EnQueue(Q, p);
while(Q.front->next != NULL){
DeQueue(Q, p);
weight++;
if(p->lchild != NULL)
EnQueue(Q, p->lchild);
if(p->rchild != NULL)
EnQueue(Q, p->rchild);
if(p == last){
last = Q.rear->data;
if(weight > weightest)
weightest = weight;
weight = 0;
}
}
printf("树的宽度:%d", weightest);
}
15、设有一棵满二叉树(所有结点值均不同),已知其先序序列为pre,设计一个算法求其后序序列post。
// 第十五题 将先序序列的第一个元素放到后序序列的最后一个元素上,先序序列除了第一个元素剩下的所有元素为左右子树的总和——根左右
// 左子树为其二分之一,将左子树从后序序列第一个位置开始防止,然后放右子树
// l1为先序序列的开始位置,h1为先序序列的最后一个元素位置,l2为后序序列开始位置,h2为后序序列最后一个元素位置
void GetPostOrder(int pre[], int l1, int h1, int post[7], int l2, int h2){
if(l1<=h1){
post[h2] = pre[l1];
int half;
half = (h1-l1)/2;
GetPostOrder(pre, l1+1, l1+half, post, l2, l2+half-1);
GetPostOrder(pre, l1+half+1, h1, post, l2+half, h2-1);
}
}
void test15(){
int pre[7] = {1,2,4,5,3,6,7};
int post[7];
GetPostOrder(pre, 0, 6, post, 0, 6);
printf("后序序列:");
for (int i = 0; i<7; i++){
printf("%d ", post[i]);
}
}
16、设计一个算法将二叉树的叶节点按从左到右的顺序连成一个单链表,表头指针为head。二叉树按二叉链表方式存储,链接时用叶节点的右指针域来存放单链表指针。
// 第十六题 使用栈对二叉树进行后序遍历,找到叶子结点,将pre指针指向叶子结点,找到下一个叶子结点时,将pre的右孩子指向该结点,并将pre指向该结点
void test16(){
BiTree T;
InitBiTree(T);
buildTree(T);
LinkStack S;
InitLinkStack(S);
BiTNode *p = T, *pre = NULL, *last, *temp, *testnode;
while(p || S!=NULL){
if(p){
Push(S, p);
p =p->lchild;
}
else{
if(S->data->rchild != NULL && S->data->rchild != last)
p = S->data->rchild;
else{
Pop(S, temp);
if(temp->lchild == NULL && temp->rchild == NULL){
if(pre)
pre->rchild = temp;
pre = temp;
}
last = temp;
}
}
}
p = T;
while(p->lchild != NULL ) // 为了方便测试,树做左边必须有一个叶子节点
p = p->lchild;
while(p != NULL){
printf("%d ", p->data.value);
p = p->rchild;
}
}
17、试设计判断两棵树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或T1的左子树和T2的左子树相似,且T1的右子树和T2的右子树相似。
// 第十七题 相似:树的结构相同,形状相同,元素可以不同,使用标志位tag记录结构是否相同,1代表相同,0代表不同
void same(BiTree T1, BiTree T2, int &tag){
if(tag == 0)
return;
if(T1 == NULL && T2 != NULL){
tag = 0;
}
else if(T1 != NULL && T2 == NULL){
tag = 0;
}
else if(T1 != NULL){
same(T1->lchild, T2->lchild, tag);
same(T1->rchild, T2->rchild, tag);
}
return;
}
void test17(){
BiTree T1, T2;
printf("下面建立第一棵树\n");
InitBiTree(T1);
buildTree(T1);
printf("下面建立第二棵树\n");
InitBiTree(T2);
buildTree(T2);
int tag = 1;
same(T1, T2, tag);
if(tag == 1)
printf("两棵树相似");
else
printf("两棵树不相似");
}
18、写出在中序线索二叉树里查找指定结点在后序的前驱结点算法。
// 第十八题 找后序线索二叉树指定节点的前驱结点,如果该结点的有右孩子不为空,则前驱结点为右孩子
// 如果该结点的右孩子为空但左孩子不为空,则前驱结点为左孩子
// 如果该结点的左右孩子都为空,则前驱结点为该结点祖先节点中最近的且不被访问过的左孩子
void test18_1(ThreadTree T, ThreadNode *p){
if(p->rtag == 0)
printf("%d", p->rchild->data.value);
else if(p->ltag == 0)
printf("%d", p->lchild->data.value);
else{
while(p->lchild != NULL && p->ltag != 0)
p = p->lchild;
if(p->lchild != NULL)
printf("%d", p->lchild->data.value);
else
printf("该结点为后序遍历的第一个结点,没有前驱结点");
}
}
void test18(){
ThreadTree T;
InitThreadTree(T);
buildTree(T);
CreatTheadThree(T);
Inorder(T);
int x;
printf("\n请输入你想要查询的数:");
scanf("%d", &x);
ThreadNode *p = T, *first;
while(p->ltag == 0)
p = p->lchild;
while(p->data.value != x){
if(p->rtag != 0)
p = p->rchild;
else{
first = p->rchild;
while(first->ltag == 0)
first = first->lchild;
p = first;
}
}
printf("%d\n", p->data.value);
test18_1(T, p);
}
19、二叉树的带权路径长度(WPL)是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树T,采用二叉链表存储,结点结构为:(left, weight, right),其中叶结点的weight域保存该结点的非负权值。设root为指向T的根结点的指针,请设计求T的WPL的算法。要求:
(1)给出算法的基本设计思想;
(2)使用C或C++语言,给出二叉树结点的数据类型定义;
(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
// 第十九题 采用队列实现二叉树的层次遍历,使用last标记每层最后一个元素,high记录层数,每个结点的带权路径长度为high*weight
// 树的带权路径长度为所有结点带权路径长度之和
typedef struct WPLNode{
int weight;
struct WPLNode *left, *right;
}WPLNode, *WPLTree;
void test19(){
BiTree T;
InitBiTree(T);
buildTree(T);
LinkQueue Q;
InitLinkQueue(Q);
BiTNode *p, *last = T;
int high = 1, sum = 0;
EnQueue(Q, T);
while(Q.front->next != NULL){
DeQueue(Q, p);
sum += high*p->data.value;
if(p->lchild)
EnQueue(Q, p->lchild);
if(p->rchild)
EnQueue(Q, p->rchild);
if(p == last){
high++;
last = Q.rear->data;
}
}
printf("%d", sum);
}
20、二叉树–请设计一个算法,将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。
// 第二十题 使用递归实现对二叉树的中序遍历,在访问结点时,加上左右括号,根节点和叶子节点不加括号
void test20_1(BiTNode *p, int &deep){
if(deep != 1 ) // 根节点不加括号
if(p->lchild != NULL || p->rchild != NULL) // 叶子结点不加括号
printf("( ");
if(p->lchild != NULL){
deep++;
test20_1(p->lchild, deep);
deep--;
}
printf("%d ", p->data.value);
if(p->rchild != NULL){
deep++;
test20_1(p->rchild, deep);
deep--;
}
if(deep != 1)
if(p->lchild != NULL || p->rchild != NULL)
printf(") ");
}
void test20(){
BiTree T;
InitBiTree(T);
buildTree(T);
int deep = 1;
test20_1(T, deep);
}