LintCode第1181题-二叉树的直径
描述
给定一颗二叉树,您需要计算树的直径长度。 二叉树的直径是树中任意两个节点之间最长路径的长度.
样例 1:
给定一棵二叉树 1/ \2 3/ \ 4 5
返回3, 这是路径[4,2,1,3] 或者 [5,2,1,3]的长度.
样例 2:
输入:[2,3,#,1]
输出:2解释:2/3/
1
思路:该题要计算二叉树的高度 宽度等等一些列问题 优先想到遍历树的常用方法 深度优先搜索和广度优先搜索 而高度问题经常使用深度优先搜索 而深度优先搜索在二叉树中常用的具体方法就是前序遍历 中序遍历和后序遍历 (先序和后序对三叉树、N 叉树一样适用,只要孩子是有序的(List 顺序)。中序只在二叉树里有标准唯一的定义;对三叉/多叉树没有唯一的“中序”)
常见的适用于
后序遍历的情况有:子树高度、直径、是否平衡
中序遍历的情况有:对二叉搜索树特别有用 或者二叉树得到有序队列
前序遍历的情况有:天然适合“序列化/复制结构”
对于该问题常用的解决方法有两种:
第一种好理解:
首先计算所有节点的左右子树高度
然后通过任意遍历(前序遍历,中序遍历,后序遍历都可以)
遍历所有节点 计算出最长的直径值 即为最终值
第二种速度快直观:直接使用后序遍历在遍历的同时 会计算出每一个节点的高度 会一次性的计算出该节点的直径值 即一次遍历即可求出最长的二叉树的直接(因为将第一种方法的两步放在一块了 原因是由于后序遍历的天然特性 在访问最终根节点前 左右子树的高度都已经被访问 即可以在访问根节点的同时拿到左右子树的高度)
先来第一种的代码:
代码如下:
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param root: a root of binary tree
* @return: return a integer
*/
//思路 深度优先遍历+递归 统计出所有的的点的最高高度
// 保存每个节点的“边数高度”:null -> -1, 叶子 -> 0
private Map<TreeNode, Integer> heightMap = new HashMap<>();
private int finalDiameter = 0; // 直径(边数)
public int diameterOfBinaryTree(TreeNode root) {
// 1) 先算出每个节点的高度(后序)
computeHeight(root);
// 2) 中序遍历,利用高度表更新直径
inorderAndUpdate(root);
return finalDiameter;
}
public int computeHeight(TreeNode treeNode)//该方法易错点 容易忘记返回值需要给上一级
{
if(treeNode==null)
{
return -1;
}
int heightLeft=computeHeight(treeNode.left);
int heightRight=computeHeight(treeNode.right);
int height=Math.max(heightLeft, heightRight) + 1;
heightMap.put(treeNode,height);
return height;
}
//
public void inorderAndUpdate(TreeNode treeNode) { //计算每个点的最长路径的长度 其中只需访问所有的节点 每个节点的高度上一步已经计算好
if(treeNode==null)
{
return;
}
inorderAndUpdate(treeNode.left);
int leftSubTreeHeight;
if(treeNode.left==null)
{
leftSubTreeHeight=-1;
}else
{
leftSubTreeHeight=heightMap.get(treeNode.left);
}
int rightSubTreeHeight;
if(treeNode.right==null)
{
rightSubTreeHeight=-1;
}else
{
rightSubTreeHeight=heightMap.get(treeNode.right);
}
int candidateDiameter = leftSubTreeHeight + rightSubTreeHeight + 2;//该节点经过根的候选直径
if (candidateDiameter > finalDiameter) {
finalDiameter = candidateDiameter;
}
}
}
第二种方法:
即在后序遍历的同时求出其高度 并在求出高度访问当前根节点的同时计算出直径数
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param root: a root of binary tree
* @return: return a integer
*/
//思路 深度优先遍历+递归 统计出所有的的点的最高高度
// 保存每个节点的“边数高度”:null -> -1, 叶子 -> 0
private int maximumDiameterByEdges = 0;
public int diameterOfBinaryTree(TreeNode root) {
// 1) 直接后序 算出每个节点的高度(后序)同时计算出直径
maximumDiameterByEdges = 0;
computeHeightAndUpdate(root);
return maximumDiameterByEdges;
}
private int computeHeightAndUpdate(TreeNode node) {
if (node == null)
{
return -1;
} // 空=-1 叶=0
int leftHeight = computeHeightAndUpdate(node.left);//leftHeight rightHeight接住的返回值
int rightHeight = computeHeightAndUpdate(node.right);
int currentHeight = Math.max(leftHeight, rightHeight) + 1; // 先算本节点高度
int candidateDiameter = leftHeight + rightHeight + 2; // 再用左右高更新直径
if (candidateDiameter > maximumDiameterByEdges) {
maximumDiameterByEdges = candidateDiameter;
}
return currentHeight; // 最后把高度回传给父节点
}
}