问题
有一棵二叉树,树上每个点标有权值,权值各不相同,请设计一个算法算出权值最大的叶节点到权值最小的叶节点的距离。二叉树每条边的距离为1,一个节点经过多少条边到达另一个节点为这两个节点之间的距离。
给定二叉树的根节点root,请返回所求距离。
JavaCode
import java.util.ArrayList;
import org.junit.Test;
public class NodeDistance {
class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
//计算二叉树上任意两个指定节点之间的距离
public int getDis(TreeNode root) {
int[] nodes = new int[]{Integer.MAX_VALUE, Integer.MIN_VALUE};
// getNodes(root, nodes);//得到最小值节点和最大值节点
getLeaves(root, nodes);//得到最小值叶节点和最大值叶节点
System.out.println("minNode: " + nodes[0] + "\nmaxNode: " + nodes[1]);
ArrayList<Integer> pathMin = new ArrayList<Integer>();
ArrayList<Integer> pathMax = new ArrayList<Integer>();
// getNodePath(root, new int[]{4,9}, pathMin, pathMax);//得到二叉树上任意两个节点到根节点的路径
getNodePath(root, nodes, pathMin, pathMax);
System.out.println("pathMin: " + pathMin + "\npathMax: " + pathMax);
int idxMin = pathMin.size()-1;
int idxMax = pathMax.size()-1;
//从两个存放路径的list的末尾开始遍历
while(pathMin.get(idxMin) == pathMax.get(idxMax)) {
if(--idxMin < 0) return idxMax;
if(--idxMax < 0) return idxMin + 1;//返回值加1
}
return idxMin + idxMax + 2;//返回值加2
}
//分别得到nodeNum中两个目标节点到根节点的路径(自底向上),分别保存在pathMin和pathMax中
public void getNodePath(TreeNode root, int[] nodeNum,
ArrayList<Integer> pathMin, ArrayList<Integer> pathMax) {
if(root == null) return;
//后序遍历二叉树
getNodePath(root.left, nodeNum, pathMin, pathMax);
getNodePath(root.right, nodeNum, pathMin, pathMax);
//如果当前节点就是目标节点,将其加入到路径中
if(root.val == nodeNum[0])
pathMin.add(root.val);
if(root.val == nodeNum[1])
pathMax.add(root.val);
//如果当前节点非叶子节点,判断当前节点是否是目标节点的父辈节点,
//nodeNum中保存目标节点在当前层次中的父辈节点,即已知的最高级的父辈节点
if(root.left != null) {
if(root.left.val == nodeNum[0]) {
pathMin.add(root.val);
nodeNum[0] = root.val;
}
if(root.left.val == nodeNum[1]) {
pathMax.add(root.val);
nodeNum[1] = root.val;
}
}
if(root.right != null) {
if(root.right.val == nodeNum[0]) {
pathMin.add(root.val);
nodeNum[0] = root.val;
}
if(root.right.val == nodeNum[1]) {
pathMax.add(root.val);
nodeNum[1] = root.val;
}
}
}
//得到二叉树中最小值节点和最大值节点,依次存储在nodes中
public void getNodes(TreeNode root, int[] nodes) {
if(root == null) return;
//前序遍历二叉树
if(nodes[0] > root.val) nodes[0] = root.val;
if(nodes[1] < root.val) nodes[1] = root.val;
getNodes(root.left, nodes);
getNodes(root.right, nodes);
}
//得到二叉树中最小值叶节点和最大值叶节点,依次存储在Leaves中
public void getLeaves(TreeNode root, int[] Leaves) {
if(root == null) return;
//前序遍历二叉树
if(root.left == null && root.right == null) {
if(Leaves[0] > root.val) Leaves[0] = root.val;
if(Leaves[1] < root.val) Leaves[1] = root.val;
}
getLeaves(root.left, Leaves);
getLeaves(root.right, Leaves);
}
@Test
public void test1() {
TreeNode node = new TreeNode(1);
node.left = new TreeNode(2);
node.right = new TreeNode(3);
TreeNode nodeL = node.left;
TreeNode nodeR = node.right;
nodeL.left = new TreeNode(4);
nodeL.right = new TreeNode(0);
nodeR.left = new TreeNode(8);
nodeR.right = new TreeNode(6);
nodeR.left.left = new TreeNode(7);
nodeR.left.right = new TreeNode(5);
System.out.println("Distance: " + getDis(node));
}
}
说明
原题只是要求得到最大值叶子节点与最小值叶子节点之间的距离,本题给的解法,可以求二叉树中任意两个指定节点之间的距离;
求二叉树中任意两个节点之间的距离等价问题是求这两个节点的最低公共父节点,这两个问题都可以转化为求两个节点到根节点的路径,这两条路径的典型关系就像是树中一个倒写的“Y”,他们路径开始分叉的节点就是最低公共父节点,两条路径的分叉段就是这两个节点之间的距离;
由于这里二叉树的每个节点中没有父指针域,所以必须要采用后序遍历的方式,在找到目标节点之后,递归逐层返回时依次纪录其父节点、祖父节点、曾祖父节点。。。这里有一个技巧,就是每次递归时传递一个ArrayList的引用,这个ArrayList用于记录节点,由于引用类型在被压栈之后,在任意一层递归调用中都可以通过这个引用操作其指向的对象,且递归返回即退栈之后,ArrayList中记录的节点信息仍然保留。类似的处理方式可以参考Leetcode - Binary Tree Paths以及Leetcode - Path Sum中的DFS解法。