【王道数据结构编程题】- 二叉树算法练习

目录

1.假设二叉树采用二叉链存储结构存储,设计一个算法,求先序遍历序列中第k(1<=k<=二叉树中节点个数)个节点的值。

2.已知二叉树以二叉链表存储,编写算法完成,对于树中每个元素值为x的节点,删去以它为根的子树,并且释放相应的空间。

3.在二叉树中查找值为x的节点,试编写算法打印值为x的节点的所有祖先,假设值为x的节点不多于1个。

4.编写算法找到p和q的最近公共祖先。

5.设计算法,求非空二叉树的宽度。

6.设一颗满二叉树,已知其先序序列,设计算法求其后序序列

7.设计算法将二叉树的叶节点按从左到右的顺序连成一个单链表。

8.设计算法判断两个二叉树是否相似。


1.假设二叉树采用二叉链存储结构存储,设计一个算法,求先序遍历序列中第k(1<=k<=二叉树中节点个数)个节点的值。

//假设二叉树采用二叉链表存储结构存储,设计一个算法
//求先序遍历序列中第k(1<=k<=二叉树中节点个数)个节点的值
#include<iostream>
using namespace std;
//结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//先序遍历 递归

//计数器
int i=1;
//字符
char ch;
char PreNode(tree t ,int k)
{
	if(t==NULL) return '#';
	if(i==k) return t->data;
	i++;
	ch = PreNode(t->lchild,k);
	if(ch!='#') return ch;
	ch = PreNode(t->rchild,k);
	return ch;
}

//主函数 测试
int main()
{
	tree t;
	buildtree(t);
	cout<<PreNode(t,7)<<endl;
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

2.已知二叉树以二叉链表存储,编写算法完成,对于树中每个元素值为x的节点,删去以它为根的子树,并且释放相应的空间。

//已知二叉树以二叉链表存储,编写算法完成 
//对于树中每个元素值为x的节点,删去以它为根的子树,并释放相应的空间
#include<iostream>
using namespace std;
//结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//递归释放节点
void Release(tree &t)
{
	if(!t) return;
	Release(t->lchild);
    Release(t->rchild);
	free(t);
}

//前序序列
void delete_x(tree &T,char x)
{
	if(T==NULL) return;
	if(T->data==x)
	{
		//递归释放节点
        Release(T);
		//手动去设置节点为NULL,物理内存的一个释放
		T=NULL;
	}
	if(T!=NULL)
	{
		delete_x(T->lchild,x);
		delete_x(T->rchild,x);
	}
}

//打印
void disp(tree t)
{
	if(t)
	{
		cout<<t->data<<" ";
		disp(t->lchild);
		disp(t->rchild);
	}
}

//主函数 测试
int main()
{
	tree t;
	buildtree(t);
	delete_x(t,'C');
	disp(t);
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

3.在二叉树中查找值为x的节点,试编写算法打印值为x的节点的所有祖先,假设值为x的节点不多于1个。

//在二叉树中查找值为x的节点,试着编写算法
//打印值为x的节点的所有祖先,假设值为x的节点不多于1个
#include<iostream>
using namespace std;
//树节点结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//节点栈的结构体
typedef struct{
   //树的节点
	treenode *t;
	//此节点的左右孩子访问变量 tag=0 左孩子已经访问过 =1 右孩子已经访问过
	int tag;
}Stack;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//非递归后序遍历
void grand(tree t,char x)
{
	//栈 栈的栈顶指针top的初始化指向 0
	Stack s[10];
	int top = 0;
	while(t!=NULL||top>0)
	{
		//从根节点开始向左延深压入栈中并且节点的值不为x
		while(t!=NULL&&t->data!=x)
		{
			s[++top].t=t;
			s[top].tag=0;
			t=t->lchild;
		}
		//如果找到了值为x的节点 将栈中的节点值输出
		if(t!=NULL&&t->data==x)
		{
			printf("所有的祖先节点为:\n");
			for(int i=1;i<=top;i++)
			{
				printf("%c\n",s[i].t->data);
			}
		}
		//判断当前栈顶的节点的tag值 为1 的话 依次退栈
		while(top!=0&&s[top].tag==1)
		{
			top--;
		}
		//当前栈顶不为0 将栈顶的tag值赋值为1 节点指向该节点的右孩子
		if(top!=0)
		{
			s[top].tag=1;
			t=s[top].t->rchild;
		}
	}
}

//主函数 测试
int main()
{
	tree t;
	buildtree(t);
	grand(t,'D');
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

4.编写算法找到p和q的最近公共祖先。

//找到p和q的最近公共祖先节点
#include<iostream>
using namespace std;
//树节点结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//节点栈的结构体
typedef struct{
   //树的节点
	treenode *t;
	//此节点的左右孩子访问变量 tag=0 左孩子已经访问过 =1 右孩子已经访问过
	int tag;
}Stack;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//非递归后序遍历
tree Ancestor(tree t,treenode *p,treenode *q)
{
	//两个辅助栈
	Stack S1[10],S2[10];
	//栈顶指针的初始化
	int top1=0,top2;
	//遍历二叉树指针
	treenode *tt=t;
	//大循环体条件:当前遍历的节点不能为空或者栈1顶指针>0(栈不为空)
	while(tt!=NULL||top1>0)
	{
		//向左延伸压入栈
		while(tt!=NULL)
		{
			S1[++top1].t=tt;//压入栈中
			S1[top1].tag=0;//左孩子访问过
			tt=tt->lchild;//继续指向左孩子
		}
		while(top1!=0&&S1[top1].tag==1)//栈1不为空并且栈顶节点的tag=1
		{
			//如果栈顶的节点就是p 就复制到栈2中 栈2的栈顶指针此时赋值 top1
			if(S1[top1].t==p)
			{
				for(int i=1;i<top1;i++)
				{
					S2[i]=S1[i];
				}
				top2=top1;
			}
			//如果栈顶的节点就是q 与栈1进行匹配
			if(S1[top1].t==q)
			{
				for(int i=top1;i>0;i--)
				{
					for(int j=top2;j>0;j--)
					{
						if(S2[j].t==S1[i].t)
							return S1[i].t;
					}
				}
			}
			top1--;//退栈
		}
		//栈不为空 将栈顶节点的tag=1 并且当前的一个节点指向该节点的右孩子
		if(top1!=0)
		{
			S1[top1].tag=1;
			tt=S1[top1].t->rchild;
		}
	}
	return NULL;
}

//主函数 测试
int main()
{
	tree t;
	buildtree(t);
	treenode *p=t->lchild->rchild,*q=t->rchild->lchild;
	cout<<Ancestor(t,p,q)->data<<endl;
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
		   p  q
*/
//ABD##E##CF##G##

5.设计算法,求非空二叉树的宽度。

//非空二叉树b的高度
#include<iostream>
using namespace std;
//树节点结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//节点队列的结构体
typedef struct{
  tree data[10];
  int level[10];
  int f,r;
}Qu;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//层次遍历
int width(tree b)
{
	//队列
	Qu Q;
	//出队节点的指向
	tree p;
	//保存出队的节点层次
	int k;
	//头尾指针初始化
	Q.f=Q.r=-1;
	//尾指针后移用来根节点入队
	Q.r++;
	//根节点入队
	Q.data[Q.r]=b;
	//根节点层次为1
	Q.level[Q.r]=1;
	//循环体 头指针小于尾指针可以执行
	while(Q.f<Q.r)
	{
	   //头指针后移出队
		Q.f++;
       //保存出队节点
		p=Q.data[Q.f];
	   //保存出队节点的层次
		k=Q.level[Q.f];
	   //左孩子进入队列
		if(p->lchild!=NULL)
		{
			//尾指针后移 用来入队
			Q.r++;
	        //左孩子进入队列
			Q.data[Q.r]=p->lchild;
	        //进入队列的节点的层次为出队节点的层次+1
			Q.level[Q.r]=k+1;
		}
		if(p->rchild!=NULL)//右孩子进队
		{
			//尾指针后移,用来入队
			Q.r++;
			//右孩子进入队列
			Q.data[Q.r]=p->rchild;
			//进入队列的节点的层次为出队节点的层次+1
			Q.level[Q.r]=k+1;
		}
	}
	//循环遍历的时候最大值的更新,遍历下标,计算每层的个数
	int maxx=0,i=0,n;
	//从第一层开始
	k=1;
	//循环条件 遍历的下标小于队列尾部
    while(i<=Q.r)
	{
		//一开始每层默认0个
		n=0;
		//当下标小于队列长度并且遍历的下标对应的层次为k的时候
		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<<width(t)<<endl;
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

6.设一颗满二叉树,已知其先序序列,设计算法求其后序序列

//已知先序序列,设计算法求其后序序列
#include<iostream>
using namespace std;

//递归交换过程
void PrePost(char pre[],int l1,int h1,char post[],int l2,int h2)
{
	int half;//左右子树临界
	if(l1<=h1)//递归条件
	{
		post[h2]=pre[l1];
		half=(h1-l1)/2;
		PrePost(pre,l1+1,l1+half,post,l2,l2+half-1);
	    PrePost(pre,l1+half+1,h1,post,l2+half,h2-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;
}

/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

7.设计算法将二叉树的叶节点按从左到右的顺序连成一个单链表。

//叶节点从左到右连接成单链表
#include<iostream>
using namespace std;

//树节点结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	ch = getchar();
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//全局 头节点 前驱节点 类型仍然是树节点的一个结构体 通过rchild连接
tree head = (treenode *)malloc(sizeof(treenode)),pre=NULL;

//中序遍历
tree Inorder(tree t)
{
	//递归条件 节点不为空
	if(t)
	{
		//将根节点左孩子作为参数 一直递归向左延申
		Inorder(t->lchild);
		//找到叶子节点
		if(t->lchild==NULL&&t->rchild==NULL)
		{
			//如果是第一个
			if(pre==NULL)
			{
				//头节点赋值
				head = t;
				/*保留前驱节点 前驱节点为当前的一个节点 保证后面再次遇到叶子节点的时候知道
				前驱节点是谁 并且能够进行连接*/
				pre=t;
			}
			//若不是第一个
			else
			{
				//就将前驱节点的右指针指向当前的节点
				pre->rchild=t;
				//更新前驱节点 保证后面再次遇到叶子节点的时候知道前驱节点是谁 并且能够连接
				pre=t;
			}
		}
		//左子树递归完成了 向左递归右子树
		Inorder(t->rchild);
		//右子树递归完后可以将最后一个节点也就是更新后的前驱节点的右指针指向空
		pre->rchild=NULL;
	}
	//最后返回头节点即可 可以通过头节点来遍历整个节点链
	return head;
}

//主函数 测试
int main()
{
	tree t;
	buildtree(t);
	Inorder(t);
	while(head)
	{
		cout<<head->data<<" ";
		head = head->rchild;
	}
	return 0;
}
/*
            A
	      B   C
		D  E  F  G
*/
//ABD##E##CF##G##

8.设计算法判断两个二叉树是否相似。

//两颗二叉树是否相似
#include<iostream>
using namespace std;

//树节点结构体
typedef struct treenode{
    //节点的值
	char data;
	//节点的左右孩子指针
	struct treenode *lchild,*rchild;
}treenode,*tree;

//建树 赋值节点
void buildtree(tree &t)
{
	char ch;
	cin>>ch;
	if(ch=='#') t=NULL;
	else
	{
		//对节点进行内存分配
		t=(treenode *)malloc(sizeof(treenode));
		//对节点进行赋值
		t->data=ch;
		//初始化 左右孩子节点为空
		t->lchild=NULL;
		t->rchild=NULL;
		//递归去赋值左右子树
		buildtree(t->lchild);
		buildtree(t->rchild);
	}
}

//递归比较左右子树 空+空->1  空+非空->0
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;
	buildtree(t1);
    buildtree(t2);
    cout<<similar(t1,t2)<<endl;
	return 0;
}

/*
      A
	B
  测试t1:AB###


  C
    E
  测试t2:C#E##
*/

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125418146