题1:二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和。给定一颗二叉树T,采用二叉链表存储,结点结构为:
left | weight | right |
其中叶结点的weight域保存该结点的非负权值。设root为指向T的根结点的指针,请设计求T的WPL的算法。
要求:
1)给出算法的基本设计思想
2)使用C或C++语言,给出二叉树结点的数据类型定义
3)根据设计思想,采用C或C++描述算法
4)给出该算法的时间复杂度
一、算法的设计思想
1.理解二叉树的带权路径长度
树中所有叶子结点的带权路径长度之和,记作:
WPL=w1×l1+w2×l2+⋯+wn×ln=∑wi×li(i=1,2,⋯,n)。
n为叶子结点的个数,wi为第i个结点的权值,即其数据域,li为第i个结点的路径长度。
2.求二叉树的带权路径长度示例
a.WPL=7×2+5×2+2×2+4×2=36
b.WPL=7×3+5×3+4×2+2×1=46
c.WPL=7×1+5×2+2×3+4×3=35
3.模拟算法求二叉树的带权路径长度WPL
(1)采用递归算法实现。
(2)二叉树的WPL值=树中全部叶结点的带权路径长度之和=根结点左子树中全部叶结点的带权路径长度之和+根结点右子树中全部叶结点的带权路径长度之和。
(3)叶结点的带权路径长度=该结点的weight域的值×该结点的深度
(4)设根结点的深度为0,若某结点的深度为d时,则其孩子结点的深度为d+1。
4.核心算法思想:
采用先序递归遍历
(1)声明一个sum变量,用来叠加存放每个叶节点加权值的和。用static对其进行修饰,sum变量的生命周期延长到程序执行结束时,离开定义它的WPL函数后不能使用,但如再次调用WPL函数时,它可继续使用,而且保存了前次被调用后留下的值。适用于在此递归算法中求和;
(2)第一个if分支当前结点没有左右子结点的时候,说明它已经是叶节点了,所以直接将其结点数据域×深度+sum,计算当前及之前叶节点的带权总和。该分支也是递归的出口;
(3)第二个if分支,若该结点是非叶节点,左子树不为空时,对左子树调用递归算法,深度参数在本结点的深度参数上加1;
(4)第三个if分支,右子树不为空时,对右子树调用递归算法,深度参数在本结点的深度参数上加1。
当上述分支都递归进入了第一分支的情况后,即求得了所有的叶节点带权值之和,算法结束。
二、二叉树结点数据类型定义
typedef struct Tree
{
int weight; // 存放数据域
struct Tree *leftchild; // 遍历左子树指针
struct Tree *rightchild; // 遍历右子树指针
} Tree,*BitTree;
三、描述算法
核心算法代码:
int WPL(Tree *root,int deep)
{
static long sum=0;
if(root->leftchild == NULL && root->rightchild==NULL)
//若为叶结点,累积求和
sum+=deep*root->weight;
if(root->leftchild!=NULL) //若左子树不空,对左子树递归遍历
WPL(root->leftchild,deep+1);
if(root->rightchild!=NULL) //若右子树不空,对右子树递归遍历
WPL(root->rightchild,deep+1);
return sum;
}
四、算法时间复杂度
WPL算法采用了分治思想,将其整棵树的带权和转换为根节点左右两个子树的带权和再相加,以此递归进行。但是这个分治有别于快速排序归并排序等,快速排序的n是数组长度,在排序过程中有重复访问,所以这种情况下时间复杂度为O(nlogn)。但是WPL算法是对左右子树进行访问,n个结点每个结点只访问一次,它访问到叶结点即求出带权和之后,直接到了递归出口,接着开始另外的结点遍历,不存在重复遍历的情况。
基于以上分析可以得到算法时间复杂度为O(n)。