您的位置:首页 > 其它

最大距离二叉树节点

2017-05-14 11:18 323 查看

题目

给定一个二叉树,定义两个节点的距离为这两个节点之间所有边的个数,要求这个二叉树中两个节点之间的最大距离。例如下图,两个节点之间的最大距离是5。



分析1

对于树中任何一个节点,包括该节点的树及其所有子树中存在的最大距离可以通过三种途径得到:

在该节点的左子树中;

在该节点的右子树中;

将该节点作为根,横跨左右子树,最大距离是该节点左子树最大深度,加上右子树最大深度,再加2。

这样很容易写出递归形式的实现,其中还需要计算的是该节点的最大深度,这同样可以利用递归实现。需要注意的是,在递归实现最大深度时,遍历到叶子节点的
null
节点后应该返回-1,这样叶子节点的深度才为0;而递归实现最大距离时,遍历到叶子节点的
null
节点后应该返回0,表示没有子树的叶子节点与它自身的距离为0。

代码1

public class BTDistance {
static class Node {
Node left, right;
}

static int maxDist(Node root) {
if (root == null) return 0;
// 当前最大距离存在于左右子树中
int maxDis = Math.max(maxDist(root.left), maxDist(root.right));
// 或者跨越了根节点
maxDis = Math.max(maxDis, maxDepth(root.left) + maxDepth(root.right) + 2);
return maxDis;
}

static int maxDepth(Node root) {
if (root == null) return -1;
int lDepth = maxDepth(root.left);// 左子树最大深度
int rDepth = maxDepth(root.right);// 右子树最大深度
return Math.max(lDepth, rDepth) + 1;// 当前最大深度是左右子树最大深度加1
}
}


分析2

简单的递归实现总是由于很多的重复计算而效率低下。对于上述实现,求每个节点的最大距离时都会将它的所有子树节点的最大距离重复计算一遍,并且求每个节点的最大深度时也会将它的所有子树节点的最大深度重复计算一遍。那么有什么好的改进方法呢?

在《算法导论》中我们见到过动态规划的自顶向下实现方法,其关键是加入了备忘机制,即在递归过程中维护一个全局性的表,它保存已经计算过的子问题的结果,对于在解决上层子问题中遇到的相同子问题直接查表即可,从而减少了重复的计算。那么这种方法能否应用到本题中呢?

首先考察这个问题是否能用动态规划来实现。对于所遍历到的任何一个节点,其具有的最大距离只需要在包含该节点的树及其所有子树中寻找,即满足动态规划的无后效性。对于一个节点v,定义maxDist[v]表示包含该节点的树及其所有子树中的存在的最大距离,定义maxDepth[v]表示以该节点为根的树的最大深度,根据之前分析的三种情况,很容易写出递归式:

maxDist[v]=⎧⎩⎨⎪⎪0,max(maxDist[v.left],maxDist[v.right],maxDepth[v.left]+maxDepth[v.right]+2),v=nullv≠nullmaxDepth[v]={−1,max(maxDepth[v.left],maxDepth[v.right])+1,v=nullv≠null

其次考虑如何加入备忘机制。我们需要保存的中间结果有两个,即maxDist和maxDepth。由于二叉树是链表形式的,我们不可能再使用常规的数组来记录中间解。一种方法是创建包含maxDist和maxDepth两个域的
Memo
类,且和节点类一样包含左右子树指针,然后按照原二叉树构造一个结构完全一样的
Memo
为节点的二叉树,于是在原二叉树的递归轨迹就可以同样应用于这个
Memo
二叉树并更新其中的域。

再仔细分析可以发现,对于节点v的状态,仅仅取决于它的左右子树的状态;同时,对原二叉树节点v的递归求解的过程中,只有当它的左右子树v.left和v.right求解完毕才返回到v的求解。因此,节点v的
Memo
类只需要在当前递归层创建即可,它的左右子树的
Memo
类对它进行更新后直接丢弃,最后返回v的
Memo
类到上一递归层即可。从而
Memo
类并不需要具有树形结构,减少了存储空间。

代码2

public class BTDistance {
static class Node {
Node left, right;
}

static class Memo {
int maxDepth, maxDist;

Memo(int depth, int dis) {
this.maxDepth = depth;// 该节点左右子树中的最大深度
this.maxDist = dis;// 包括该节点的树及其所有子树中的最大距离
}
}

static Memo maxDist(Node root) {
if (root == null) {
return new Memo(-1, 0);
}
Memo lMemo = maxDist(root.left);
Memo rMemo = maxDist(root.right);
Memo curMemo = new Memo(-1, 0);
// 当前最大深度是左右子树最大深度加1
curMemo.maxDepth = Math.max(lMemo.maxDepth, rMemo.maxDepth) + 1;
// 当前最大距离存在于左右子树中
curMemo.maxDist = Math.max(lMemo.maxDist, rMemo.maxDist);
// 或者跨越了根节点
curMemo.maxDist = Math.max(curMemo.maxDist, lMemo.maxDepth + rMemo.maxDepth + 2);
return curMemo;
}
}


测试代码

public class Test {
static Node binaryTree() {
Node root = new Node();
Node root2 = new Node();
root.right = root2;
Node l = new Node();
Node r = new Node();
root2.left = l;
root2.right = r;
Node ll = new Node();
Node lr = new Node();
l.left = ll;
l.right = lr;
Node rr = new Node();
r.right = rr;
Node lrl = new Node();
lr.left = lrl;
return root;
}

public static void main(String[] args) {
Node root = binaryTree();
Memo memo = maxDist(root);
System.out.println(memo.maxDist);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: