习题4-2
强化讲义 · 原始转写 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照
## 习题4-2
设计一个算法,将一棵以二叉链表存储的二叉树T按顺序方式存储到一维数组BI]中(根结点下标为1,结点的权值>=0,数组值等于-1 表示该结点不存在)。二叉树结点的数据类型定义如下: -AX
typedef struct node{
int data;
struct node *lchild, *rchild;
}BTNode;
(1) 给出算法的基本设计思想。(2) 根据设计思想,采用 C 或 C++ 语言描述算法,关键之处给出注释。(3)说明所设计算法的时间复杂度。
【解析】本题先、中、后序皆可,下面采用后序遍历。【答案】(1)初始时对数组全部元素赋值为-1,对二叉树进行后序遍历,根结点下标为1,i号结点左孩子下标为2×i,右孩子下标为2×i+1,将对应结点的权值存入数组中。(2)代码如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 175 页及 Markdown 上下文人工校对
> return 被 OCR 移到 if 外,使所有非空结点也立即返回;递归函数名和 ans 的花括号也被破坏。
void PostOrder(BTNode *p, int B[], int i)
{
if (p == NULL)
{
B[i] = -1;
return;
}
PostOrder(p->lchild, B, 2 * i);
PostOrder(p->rchild, B, 2 * i + 1);
B[i] = p->data;
}
void ans(BTNode *T, int B[])
{
PostOrder(T, B, 1);
}
(3)时间复杂度:O(n)
请大家思考,如果根结点下标为0应该怎么写?
这种情况下,根结点下标为 0,号结点左孩子下标为 2×i+1,右孩子下标为 2×i+2
<!-- page 176 -->
## 习题4-3
已知由一棵二叉链表表示的二叉树,设计算法,输出二叉树的高度(只有根结点的二叉树高度为1)。二叉树结点数据类型定义如下: 算
typedef struct node{
int data;
struct node *lchild, *rchild;
}BTNode;
(1)给出算法的基本设计思想,关键之处给出注释。(2)根据设计思想,采用C或C++语言描述算法。(3)说明所设计算法的时间复杂度。42
【答案】(1)对二叉树进行先序/中序/后序遍历,遍历时设置变量depth保存当前子树的高度= max(左子树高度,右子树高度)+1,最后输出整棵树的高度即可。(2) 代码如下:
int PostOrder(BTNode* p) //后序遍历
{
if (p==NULL)
return 0; //该结点不存在
int L=PostOrder(p->lchild);
int R=PostOrder(p->rchild);
int depth=max(L, R)+1; // 访问当前结点vis1t(p)
} return depth;
(3)时间复杂度:O(n)
## 习题4-4
设树采用孩子兄弟链表表示(数据域、孩子域、兄弟域),设计算法计算树的度。
(1)写出树结点的数据结构定义。(2) 给出算法的基本设计思想。(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释 (4)说明所设计算法的时间复杂度。
【答案】(1)定义如下:
typedef struct node{
int data;
struct node *firstchild, *nextbro;
}TNode;
<!-- page 177 -->
(2)核心思想:一个父结点的度,等于其第一个孩子结点的兄弟链表的长度。算法通过一个后根序的递归函数Post(p)遍历树,该函数的主要功能是计算并返回以结点p开头的兄弟链表的长度。同时,用一个全局变量degree记录在整个计算过程中遇到的最长的兄弟链长度。(3)代码如下:
int degree=0; // 全局变量,最终输出degree
int Post(TNode* p)
{ //后序遍历 主道
if (p==NULL)
return 0; // 该结点不存在
Post(p->firstchild);
int d=1+Post(p->nextbro); //d是指到该结点为止有多少个兄弟
degree=max(degree, d);
return d;
}
## (4) 时间复杂度:O(n)
## 树的度:递归算法可视化
原资料配图 · 转写内容请回源核验
## 习题4-5
设二叉树以二叉链表形式存放,根结点的深度为1,一棵二叉树的繁茂程度定义为各层结点数与该层深度的乘积之和。试设计算法,求二叉树的繁茂程度。数据类型定义如下:
typedef struct node{
int data;
struct node *lchild, *rchild;
}BTNode;
<!-- page 178 -->
(1) 给出算法的基本设计思想。(2) 根据设计思想,采用 C 或C++ 语言描述算法,关键之处给出注释。(3)说明所设计算法的时间复杂度和空间复杂度。
【答案】(1)按照二叉树的层次遍历BFS,front 保存队头元素后一个位置下标,rear 保存下数为 rear-last,依次算出每一层的繁茂程度,并求出最大值。(2) 代码如下:
int ans(BTNode *T)
{
BTNode* q[MAX_SIZE];
int front=0, rear=0, depth=1;
int last=0; ///本层的最后一个结点下标
if (T!=NULL) q[rear++]=T;
BTNode *p; // 当前访问的结点p
int sum=1*1; //第一层的繁茂程度
while(front<rear)
{
p=q[front++];
if (p->lchild!=NULL)
q[rear++]=p->lchild;
if (p->rchild!=NULL)
q[rear++]=p->rchild;
if(front>last)//该层已访问完
{
depth++;
printf("%d %d %d\n", front, rear, depth);
sum+=(rear-front)*depth;
last=rear-1;
}
}
return sum;
}
(3)时间复杂度:O(n)空间复杂度:O(n)
## 习题4-6
<!-- page 179 -->
给定一棵二叉树,设计算法检查它是否是镜像对称的,如果是镜像对称则返回1,否则返回
原资料配图 · 转写内容请回源核验
0。例如下图所示就是镜像对称的。
(1)给出算法的基本设计思想。(2) 根据设计思想,采用C 或C++ 语言描述算法,关键之处给出注释。(3)说明所设计算法的时间复杂度。【解析】本题采用先序/中序/后序遍历都可以,设置两个指针p、q访问即可。【答案】(1)按照先序遍历,在遍历的时候设置两个指针 p、q,分别指向当前访问结点和它对称的位置,每次比较p和q的值是否相同。当p递归左(右)孩子的时候,q递归右(左)孩子,这样可以保证p和q一直是对称位置,如果发现p和 q 的值不一样则返回0。(2)代码如下:
int Pre0rder(BTNode *p,BTNode *q)
{
if (p==NULL && q==NULL)
return 1;
if (p==NULL || q==NULL)
return 0;
if (p->data!=q->data)
return 0;
if (Pre0rder(p->lchild, q->rchild)==0)
return 0;
if (Pre0rder(p->rchild,q->lchild)==0)
return 0;
return 1;
}
(3) 时间复杂度:O(n)
## 习题4-7
(13 分)二叉树中两个结点p 和q 的最低公共祖先是二叉树中离根结点尽可能远且同时是p和q祖先的结点,结点的祖先可以是自己。给定一棵非空二叉树T,采用二叉链表存储。例如,对于下图所示的非空二叉树T,结点3和结点4的最低公共祖先是结点3,结点2和结点
<!-- page 180 -->
原资料配图 · 转写内容请回源核验
4的最低公共祖先是结点1。
二叉树结
点定义如下:
typedef struct node
{
int data;
struct node *left, *right;
}BTree;
root 为指向T的根结点的指针,请设计算法求出p和 q的最低公共祖先的地址,要求:1)给出算法的基本设计思想。2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
【答案】(1)算法设计思想:使用递归后序遍历二叉树,对于当前访问的结点root:
若 root == p 或 root == q,直接返回 root ;
• 递归分别在左子树、右子树查找 p 和 q;
若左右子树递归结果均不为 NULL,说明 p和q 分别在左右子树中,root 即为最近公共祖先;
若其中一个子树递归结果为 NULL,则返回另一侧的结果(说明两结点均在某一子树中)。(2) 算法代码:
<!-- verified-by-hand -->
> [!success] 已结合原文第 180 页及 Markdown 上下文人工校对
> 函数名中的 O 被识别成 0,逻辑或被破坏,右子树递归整行和结尾返回语句丢失。
BTree *PostOrder(BTree *root, BTree *p, BTree *q)
{
if (root == NULL) return NULL;
if (root == p || root == q) return root;
BTree *left = PostOrder(root->left, p, q);
BTree *right = PostOrder(root->right, p, q);
if (left != NULL && right != NULL) return root;
return left != NULL ? left : right;
}
<!-- page 181 -->
(3) 时间复杂度:O(n)空间复杂度:O(n)
<!-- page 182 -->
## 4.7.2 真题
## 2014年41题
(13分)二叉树的带权路径长度(WPL)是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树T,采用二叉链表存储,结点结构如下:
<table><tr><td>left</td><td>weight</td><td>right</td></tr></table>
其中叶结点的weight域保存该结点的非负权值。设root为指向T的根结点的指针,请设计求T的WPL的算法,要求:
(1) 给出算法的基本设计思想。(2) 使用C 或 C++ 语言,给出二叉树结点的数据类型定义。(3)根据设计思想,采用C或C++ 语言描述算法,关键之处给出注释。
带权路径长度WPL 有两种计算方法:① WPL的定义:WPL = 所有(叶结点的权值 Wi×该结点深度 Di)求和。
②推导。如果该树的所有非叶结点满足以下条件:它的权值等于它的孩子结点权值之和。则WPL=所有非叶结点的权值和。
因此本题有两种解法,都可以拿满分。
## 【解法1】前序/中序/后序遍历,下面给出的是先序遍历
(1)先序遍历二叉树,设置全局变量WPL,每次p访问到叶子结点时p的权值与深度相乘
typedef struct BTNode{
int weight;
struct BTNode *left, *right;
}BTNode;
(3) 算法如下:
int WPL=0; //WPL是全局变量
void PreOrder(BTNode *p, int depth) // 当前结点p,深度depth
{
if (p==NULL) // p是空结点
return;
if (p->left==NULL && p->right==NULL) // p是叶结点
WPL+=depth*p->weight;
Pre0rder(p->left, depth+1); // 递归调用左孩子
Pre0rder(p->right, depth+1); // 递归调用右孩子
}
<!-- page 183 -->
## 【解法2】后序遍历
(1)后序遍历二叉树,设置全局变量WPL,如果访问到叶结点则把权值返回给父亲,如果是非叶结点则该结点权值等于左、右孩子的权值和,累加到WPL中,并将该权值返回给父亲。
## (2) 类型定义:
typedef struct BTNode{
int weight;
struct BTNode *left, *right;
}BTNode;
## (3) 算法如下:
int WPL=0; //WPL是全局变量
int PostOrder(BTNode *p)
{
if (p==NULL) // p是空结点
return 0;
int L=Post0rder(p->left);// 递归调用左孩子
int R=Post0rder(p->right); // 递归调用右孩子
if (p->left==NULL && p->right==NULL)
return p->weight; // p是叶结点
WPL=WPL+L+R; // p是非叶结点
return L+R;
}
## 2017年41题
(15分)请设计一个算法,将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。例如,当下列两棵表达式树作为算法的输入时,输出的等价中缀表达式分别为(a+b)×(c×(-d))和(a×b)+(-(c-d))。
<!-- page 184 -->
原资料配图 · 转写内容请回源核验
二叉树结点定义如下:
原资料配图 · 转写内容请回源核验
typedef struct node{
char data[10]; //存储操作数或操作符
struct node *left, *right;
}BTree;
要求:(1)给出算法的基本设计思想。(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
【解法】中序遍历,递归(1)中序遍历二叉树,对于当前访问的非空结点p,则先输出“(”,然后递归调用左子树,输出p的权值,递归调用右子树,输出“)”,如果p是根或者叶结点则不需要输出“(”或“)”。(2)算法如下:
void InOrder(BTree* p, BTree* root) // 王定
{
if (p==NULL) return; // p是空结点
if ((p->left!=NULL |l p->right!=NULL) && (p!=root))
printf("(");
In0rder(p->left, root); //递归调用左子树
printf("%s", p->data); //输出操作数
In0rder(p->right,root);//递归调用右子树
if ((p->left!=NULL |l p->right!=NULL) && (p!=root))
printf(")");
}
## 2022年41题
(13分)已知非空二叉树T的结点值均为正整数,采用顺序存储方式保存,数据结构定义如下:
typedef struct { // MAX_SIZE为已定义常量
int SqBiTNode[MAX_SIZE]; //保存二叉树结点值的数组
int ElemNum;
}SqBiTree;
<!-- page 185 -->
T 中不存在的结点在数组 SqBiTNode 中用-1 表示。例如,对于下图所示的两棵非空二叉树T1 和 T2,T1 的存储结果如下: 城村
<table><tr><td rowspan=1 colspan=1>40</td><td rowspan=1 colspan=1>25</td><td rowspan=1 colspan=1>60</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>30</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>80</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>27</td><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1></td></tr></table>
T2 的存储结果如下:
<table><tr><td rowspan=1 colspan=1>40</td><td rowspan=1 colspan=1>50</td><td rowspan=1 colspan=1>60</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>30</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>-1</td><td rowspan=1 colspan=1>35</td><td rowspan=1 colspan=1>X</td></tr></table>
请设计一个尽可能高效的算法,判定一棵采用这种方式存储的二叉树是否为二叉搜索树,若是,则返回 true,否则,返回false。要求:(1)给出算法的基本设计思想。(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
【解法】中序遍历(1)如果当前结点的数组下标为i则左孩子下标为2i+1,右孩子下标为2i+2,对该树进行递归的中序遍历,pre保存当前访问结点的前驱结点的权值,如果SqBiTree[i]] < pre 则说明中序遍历得到的不是升序,即当前子树返回 false,如果SqBiTree[i]>=pre 且左子树和右子树都确定是二叉排序树则返回 true。(2) 算法如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 185 页及 Markdown 上下文人工校对
> 函数主体语义完整,但闭合花括号丢失。保留讲义当前的非递减判定;若课程定义要求关键字互异,比较符应另行核对原 PDF。
```c
int pre = 0;
bool InOrder(SqBiTree *T, int i)
{
if (i >= T->ElemNum || T->SqBiTNode[i] == -1) return true;
if (!InOrder(T, 2 * i + 1)) return false;
if (pre > T->SqBiTNode[i]) return false;
pre = T->SqBiTNode[i];
if (!InOrder(T, 2 * i + 2)) return false;
return true;
}
```