数据结构 第五章 树和二叉树 作业(已批改)

知识点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
来个例题:
在这里插入图片描述
前序遍历:ABDECFG
中序遍历:DBEAFCG
后序遍历:DEBFGCA
层序遍历:ABCDEFG

判断题

1-1
There exists a binary tree with 2016 nodes in total, and with 16 nodes having only one child.

译文:存在一棵总共有2016个结点的二叉树,其中有16个结点只有一个孩子

T F

分析: 假设没有孩子的结点(叶结点)个数为n₀,只有一个孩子的结点(度为1的结点)个数为n₁,有两个孩子的结点(度为2的结点)个数为n₂。
则n₀+n₁+n₂=2016 ∵n₀=n₂+1(二叉树的性质:叶结点个数等于度为2的结点个数加1) ∴n₀+n₁+n₂=2016
⇨n₂+1+16+n₂=2016 ⇨2n₂=1999 n₂除不尽,所以答案错误。
公式在课本P118;

1-2
存在一棵总共有2016个结点的二叉树,其中有16个结点只有一个孩子。

T F

同 1-1

1-3
某二叉树的前序和中序遍历序列正好一样,则该二叉树中的任何结点一定都无右孩子。

T F

应是任一结点无左孩子

1-4
一棵有124个结点的完全二叉树,其叶结点个数是确定的。

T F

分析: 假设没有孩子的结点(叶结点)个数为n₀,只有一个孩子的结点(度为1的结点)个数为n₁,有两个孩子的结点(度为2的结点)个数为n₂。
则 n₀+n₁+n₂=124 ∵n₀=n₂+1(二叉树的性质:叶结点个数等于度为2的结点个数加1) ∴n₀+n₁+n₂=124
⇨1+n₁+2n₂=124 ⇨n₁=0 或 n₁=1,又因为n1=0时不成立,所以n1只能为1,所以叶节点的个数是确定的;

1-5
若一个结点是某二叉树的中序遍历序列的最后一个结点,则它必是该树的前序遍历序列中的最后一个结点。

T F

不一定;

单选题

2-1
树最适合于用来表示

A. 有序数据元素
B. 无序数据元素
C. 元素之间无联系的数据
D. 元素之间具有分支层次关系的数据

课本P111:树是以分支关系定义的层次结构;

2-2
设树T的度为4,其中度为1、2、3、4的结点个数分别为4、2、1、1。则T中有多少个叶子结点?

A. 4
B. 6
C. 8
D. 10

度为1,2,3,4的结点个数分别为4,2,1,1
意思就是有只有一个分支的结点有4个,有两个分支的结点有2个,…结点的度:结点拥有的子树数.(每个结点有多少个分支)
叶子(终端结点):度为零的结点.(没有分支的结点) 树的度:树内各结点的度的最大值.由树的性质知:结点数为所有结点的度数之和加1
同时注意到叶子结点的度数为0 则总结点数(设叶子结点数为X)
1 * 4+2 * 2+3 * 1+4 * 1+X * 0 + 1 = 16
叶子结点数为 X=16-4-2-1-1=8

2-3
三叉树中,度为1的结点有5个,度为2的结点3个,度为3的结点2个,问该树含有几个叶结点?

A. 8
B. 10
C. 12
D. 13

总结点数:1 * 5 + 2 * 3 + 3 * 2 + X * 0 + 1 = 18
叶子结点数:X = 18 - 5 - 3 - 2 = 8

2-4
有一个四叉树,度2的结点数为2,度3的结点数为3,度4的结点数为4。问该树的叶结点个数是多少?

A. 10
B. 12
C. 20
D. 21

总结点数:1 * 0 + 2 * 2 + 3 * 3 + 4 * 4 + X * 0 + 1 = 30
叶子结点数:X = 30 - 2 - 3 - 4 = 21

2-5
一棵二叉树中,双分支结点数为15,单分支结点数为30,则叶子结点数为()个。

A. 15
B. 16
C. 17
D. 47

总结点数:1 * 30 + 2 * 15 + X * 0 + 1 = 61
叶子结点数:X = 61 - 30 - 15 = 16

2-6
在一棵度为 3 的树中,度为 2 的结点个数是 1,度为 0 的结点个数是 6,则度为 3 的结点个数是 __

A. 2
B. 3
C. 4

总结点数:1 * 0 + 2 * 1 + 3 * X + 0 * 6 + 1 = 3 + 3 * X
叶子结点数:6 = 3 + 3 * X - 1 - X = 2 + 2 * X
X = 2

2-7
已知一棵二叉树的先序遍历结果是ABC,则以下哪个序列是不可能的中序遍历结果:

A. ABC
B. BAC
C. CBA
D. CAB
在这里插入图片描述

2-8
已知一棵完全二叉树的第6层(设根为第1层)有8个叶结点,则该完全二叉树的结点个数最多是:

A. 39
B. 52
C. 111
D. 119

该完全二叉树第6层的非叶子结点有 2^(6-1) - 8 = 24,
那么第7层最多有 24 * 2 = 48 个结点
而前6层最多有 2^6 - 1 = 63 个结点
所以该完全二叉树的结点个数最多有 63 + 48 = 111 个

2-9
在一个用数组表示的完全二叉树中,如果根结点下标为1,那么下标为17和19这两个结点的最近公共祖先结点在哪里(数组下标)? (注:两个结点的“公共祖先结点”是指同时都是这两个结点祖先的结点)

A. 8
B. 4
C. 2
D. 1

若父节点的下标为 i,则左子节点下标为 2i,右子节点下标为 2i+1;
所以当 2i + 1 = 17,2j + 1 =19 时,可得 i = 8,j = 9;
当 2a = 8,2b + 1 = 9时,可得 a = 4,b = 4;
所以公共祖先结点的下标为 4;

2-10
具有65个结点的完全二叉树其深度为(根的深度为1):

A. 8
B. 7
C. 6
D. 5

因为2^6-1< 65 < 2^7-1
所以是6+1=7

2-11
具有1102个结点的完全二叉树一定有__个叶子结点。

A. 79
B. 551
C. 1063
D. 不确定

设n2为度为2的节点,设n1为度为1的节点,n0为度为0的节点;
叶结点个数等于度为2的结点个数加1,n0=n2+1
n0+n1+n2=1102=n
n = 2 * n2 + 1 + n1
完全二叉树度为1的节点只能有0个或1个
所以n1=0或者1,用n=2*n2+n1+1;算一下,n2肯定是整数,把0舍去;
求出n2=550;
度为0的节点数等于度为2的节点数+1;
所以叶子节点数为551

2-12
已知二叉树的先序遍历序列为ABCDEFGH,中序遍历序列为CBEDFAGH,则该二叉树形态中,父节点的右子节点为()。

A. D
B. H
C. G
D. F
在这里插入图片描述

编程填空题

5-1 下列代码的功能是将二叉树T中的结点按照层序遍历的顺序输出。

typedef struct TreeNode *Tree;
struct TreeNode
{
    
    
   int Key;
   Tree  Left;
   Tree  Right;
};

void Level_order ( Tree T )
{
    
    
   Queue Q;
   if ( !T ) return; 
   Q = CreateQueue( MaxElements ); 
   Enqueue( T, Q ); //入队操作
   while ( !IsEmpty( Q ) ){
    
    
      T = Front_Dequeue ( Q ); /* return the front element and delete it from Q */
      printf("%d ", T->Key);
      if (T->Left)//如果左子树不为空将左节点入队
        Enqueue(T->Left,Q);
      if (T->Right) //如果右子树不为空将右节点入队
         Enqueue(T->Right,Q);
   }
}

编程题

7-1 玩转二叉树

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
1 2 3 4 5 6 7
4 1 3 2 6 5 7

输出样例:

4 6 1 7 5 3 2
#include<bits/stdc++.h>
using namespace std;

typedef struct BiNode {
    
    
	int data;
	struct BiNode *lchild, *rchild;		//左右孩子指针 
}BiNode, *BiTree;

BiNode *build(int n, int *be, int *in){
    
    
	if(n<=0)	return NULL;
	BiNode *T;
	T = new BiNode;
	T->data=be[0];
	T->lchild=NULL;
	T->rchild=NULL;
	int i;
	for(i=0;i<n;i++){
    
    
		if(be[0]==in[i])	break;		//根据前序与中序相比较获取下一个根节点位置 
	} 
	T->lchild=build(i,be+1,in);
	T->rchild=build(n-1-i,be+1+i,in+1+i);
	return T;
}

void seout(BiNode *T,int n ){
    
    
    queue<BiNode *> q;
    static int sum=n;
    q.push(T);
 
    while(!q.empty()){
    
    
        T=q.front();
        q.pop();
        n++;
        if(T!=NULL){
    
    
           cout<<T->data;
           sum--;
           if(sum>0)	cout<<" ";
           q.push(T->rchild);
           q.push(T->lchild);
        }
    }
	return;
}

int main(){
    
    
    int n,in[30],be[30];
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>in[i];
    for(int i=0;i<n;i++)
        cin>>be[i];
    BiNode *T=build(n,be,in);
    seout(T,n);
    return 0;
}

7-2 树的遍历

给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:
在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例

4 1 6 3 5 7 2
#include<bits/stdc++.h>
using namespace std;

typedef struct TreeNode{
    
    
	int val;
	struct TreeNode *lchild,*rchild;
}TreeNode;

int inorder[35],postor[35];//因为函数build需要用到,中序后序应该建成全局数组 
TreeNode* build(int inB,int inE,int postB,int postE){
    
    
	//建树 
	int i = 0;
	TreeNode* newnode =new TreeNode;
	newnode->val = postor[postE];//后序最后一个输入即根节点 
	while(inorder[inB + i] != postor[postE])//找到根节点 
		i++;
	if(i > 0)//左子树存在 
		newnode->lchild = build(inB,inB + i - 1,postB,postB + i -1);//中后左子树长度相等,且位置相同 
	else 
		newnode->lchild = NULL;//即使是空值也必须初始化! 
	if(inB + i < inE)//右子树存在 
		newnode->rchild = build(inB + i + 1,inE,postB + i ,postE - 1);//注意最后一个结点是已经赋给树的根节点,应该刨除 
	else 
		newnode->rchild = NULL;//同上,否则层序遍历无法进行 
	return newnode;
}//build

void levelorder(TreeNode* T){
    
    
	//借助队列,层次遍历
	queue<TreeNode*> q;
	if(T == NULL)
		exit(-1);
	q.push(T);
	TreeNode* front;
	while(!q.empty()){
    
    
		front = q.front();
		q.pop();
		if(front->lchild != NULL){
    
    //左结点入队 
			q.push(front->lchild);
		}
		if(front->rchild != NULL)//右结点入队 
			q.push(front->rchild);
		if(front->val != T->val)//输出 
			cout<<" ";
		cout<<front->val;
	}
} //levelorder

void DeleteTree(TreeNode * T)
{
    
    
    if(T == NULL) return;
    DeleteTree(T->lchild);
    DeleteTree(T->rchild);
    delete T;
}

int main(){
    
    
	int n,i;
	cin>>n;
	for(i = 1;i <= n; i++)//输入后序遍历 
		cin>>postor[i];
	for(i = 1;i <= n; i++)//输入中序遍历 
		cin>>inorder[i];
	TreeNode *T = build(1,n,1,n);//建树 
	levelorder(T);//层序遍历 
	DeleteTree(T);
	return 0;
}

7-3 列出叶结点

对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶节点。

输入格式:
首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 “-”。编号间以 1 个空格分隔。

输出格式:
在一行中按规定顺序输出叶节点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6

输出样例:

4 1 5
#include<iostream>
#include<queue>
#include<cstdio>
#include<vector>
using namespace std;
struct node
{
    
    
   int l,r;
}q[10];
int v[10];
vector<int> vv;//用来存放要输出的叶节点 
queue<int> Q;
void bfs()
{
    
    
   for(int i=0;i<Q.size();i++)
   {
    
    
      int num=Q.front();
      Q.pop(); 
      if(q[num].l==-1&&q[num].r==-1)
         vv.push_back(num);
      if(q[num].l!=-1)
         Q.push(q[num].l);
      if(q[num].r!=-1)
         Q.push(q[num].r);
    }
   if(Q.size())
     bfs();
}
int main()
{
    
    
   int n;
   cin>>n;
   char a,b;
   //数组v用来标记出现的结点,根节点不会出现在左右子树中,不会被标记 
   for(int i=0;i<n;i++)
   {
    
    
      cin>>a>>b;
      if(a=='-')
         q[i].l=-1;
      else
      {
    
    
          q[i].l=a-'0';
          v[a-'0']=1;
       }
      if(b=='-')
         q[i].r=-1;
      else
      {
    
    
          q[i].r=b-'0';
          v[b-'0']=1;
       } 
    }
    //找到根节点 
    int root;
    for(int i=0;i<n;i++)
    {
    
    
      if(v[i]==0)
      {
    
    
        root=i;
        break;
       }
     }
    Q.push(root);
    bfs();
    for(int i=0;i<vv.size()-1;i++)
        cout<<vv[i]<<" ";
    cout<<vv[vv.size()-1]<<endl;
    return 0;
}

7-4 小字辈

本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。

输入格式:
输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号,其中第 i 个编号对应第 i 位成员的父/母。家谱中辈分最高的老祖宗对应的父/母编号为 -1。一行中的数字间以空格分隔。

输出格式:
首先输出最小的辈分(老祖宗的辈分为 1,以下逐级递增)。然后在第二行按递增顺序输出辈分最小的成员的编号。编号间以一个空格分隔,行首尾不得有多余空格。

输入样例:

9
2 6 5 5 -1 5 6 4 7

输出样例:

4
1 9
#include<iostream>
#include<vector>
#include<cstdio>
#include<set>
#include<map>
#include<string>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<ctype.h>
#include<stack>
#include<queue>
#include<list>
using namespace std;
vector<int>v[200005];
int vv[100005];
queue<int>q;
int main(){
    
    
    int n,x,lzz;//lzz 记录老祖宗
    scanf("%d",&n);
    if(n==1){
    
    
 
    }
    for(int i=1; i<=n; i++){
    
    
        scanf("%d",&x);
        if(x==-1)
            lzz=i;
        else
            v[x].push_back(i);
    }
    if(n==1){
    
    
        printf("%d\n%d",1,1);
        return 0;
    }
    q.push(lzz);
    int bfen=1;        //统计辈分
    vv[lzz]=1;
    while(!q.empty()){
    
    
        int temp = q.front();
        q.pop();
        for(int i=0;i<v[temp].size();i++){
    
    
            vv[v[temp][i]] = vv[temp]+1;//更新每个人的辈分
            bfen = max(bfen,vv[v[temp][i]]);//更新(获得)最小辈分的值
            q.push(v[temp][i]);//把下一次要遍历的入队
        }
    }
    printf("%d\n",bfen);
    int flag = 0;
    for(int i=1;i<=n;i++){
    
    
        if(vv[i]==bfen){
    
    
            if(!flag){
    
    
                printf("%d",i);
                flag = 1;
            }
            else
                printf(" %d",i);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Jessieeeeeee/article/details/106323149
今日推荐