习题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-&gt;lchild, B, 2 * i);
    PostOrder(p-&gt;rchild, B, 2 * i + 1);
    B[i] = p-&gt;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-&gt;lchild);
    int R=PostOrder(p-&gt;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-&gt;firstchild);
    int d=1+Post(p-&gt;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&lt;rear)
    {
        p=q[front++];
        if (p-&gt;lchild!=NULL)
        q[rear++]=p-&gt;lchild;
        if (p-&gt;rchild!=NULL)
        q[rear++]=p-&gt;rchild;
        if(front&gt;last)//该层已访问完
        {
            depth++;
            printf(&quot;%d %d %d\n&quot;, 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 &amp;&amp; q==NULL)
    return 1;
    if (p==NULL || q==NULL)
    return 0;
    if (p-&gt;data!=q-&gt;data)
    return 0;
    if (Pre0rder(p-&gt;lchild, q-&gt;rchild)==0)
    return 0;
    if (Pre0rder(p-&gt;rchild,q-&gt;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-&gt;left, p, q);
    BTree *right = PostOrder(root-&gt;right, p, q);
    if (left != NULL &amp;&amp; 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-&gt;left==NULL &amp;&amp; p-&gt;right==NULL) // p是叶结点
    WPL+=depth*p-&gt;weight;
    Pre0rder(p-&gt;left, depth+1); // 递归调用左孩子
    Pre0rder(p-&gt;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-&gt;left);// 递归调用左孩子
    int R=Post0rder(p-&gt;right); // 递归调用右孩子
    if (p-&gt;left==NULL &amp;&amp; p-&gt;right==NULL)
    return p-&gt;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-&gt;left!=NULL |l p-&gt;right!=NULL) &amp;&amp; (p!=root))
    printf(&quot;(&quot;);
    In0rder(p-&gt;left, root); //递归调用左子树
    printf(&quot;%s&quot;, p-&gt;data); //输出操作数
    In0rder(p-&gt;right,root);//递归调用右子树
    if ((p-&gt;left!=NULL |l p-&gt;right!=NULL) &amp;&amp; (p!=root))
    printf(&quot;)&quot;);
}
## 2022年41题 (13分)已知非空二叉树T的结点值均为正整数,采用顺序存储方式保存,数据结构定义如下:
typedef struct { // MAX_SIZE为已定义常量
    int SqBiTNode[MAX_SIZE]; //保存二叉树结点值的数组
    int ElemNum;
}SqBiTree;
<!-- page 185 -->
// 实际占用的数组元素个数
T 中不存在的结点在数组 SqBiTNode 中用-1 表示。例如,对于下图所示的两棵非空二叉树T1 和 T2,T1 的存储结果如下: 城村
T1.SqBiTNode
<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>
T1.ElemNum = 10
T2 的存储结果如下:
T2.SqBiTNode
<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>
T2.ElemNum = 11
请设计一个尽可能高效的算法,判定一棵采用这种方式存储的二叉树是否为二叉搜索树,若是,则返回 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; } ```