LeetCode0543: 二叉树的直径

题目介绍

描述:

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

示例 :
给定二叉树

          1
         / \
        2   3
       / \     
      4   5    
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。

解题思路:

递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果。

写树相关的算法,简单说就是,先搞清楚当前 root 节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。

二叉树题目的一个难点在于如何通过题目的要求思考出每一个节点需要做什么

二叉树解题策略

一 递归
二 队列 + 迭代 (层次遍历)
三 栈 + 迭代 (非递归遍历)
四 其它

三种基本的遍历方式,都可以用递归来实现。写递归算法的时候,需要注意递归退出条件以及递归操作的表达。

自己的解法实现

def diameterOfBinaryTree(self, root):
        if not root: return 0
        res = 0
        def depth(node):
            nonlocal res
            if not node: return -1
            left_depth = depth(node.left) + 1
            right_depth = depth(node.right) + 1
            res = max(res, left_depth + right_depth)
            return max(left_depth, right_depth)
        depth(root)
        return res

网上比较优秀的解法

解法一

方法:深度优先搜索
首先我们知道一条路径的长度为该路径经过的节点数减一,所以求直径(即求路径长度的最大值)等效于求路径经过节点数的最大值减一。

而任意一条路径均可以被看作由某个节点为起点,从其左儿子和右儿子向下遍历的路径拼接得到。

def diameterOfBinaryTree2(self, root):
        self.res = 1
        def depth(node):
            # 访问到空节点了,返回0
            if not node: return 0
            # 左儿子为根的子树的深度
            left_depth = depth(node.left)
            # 右儿子为根的子树的深度
            right_depth = depth(node.right)

            # 计算d_node即L+R+1 并更新ans
            self.res = max(self.res, left_depth + right_depth + 1)
            # 返回该节点为根的子树的深度
            return max(left_depth, right_depth) + 1
        depth(root)
        return self.res - 1

解法二

def diameterOfBinaryTree3(self, root):
        self.res = 0
        def depth(node):
            if not node: return 0
            left_depth = depth(node.left)
            right_depth = depth(node.right)
            # 将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者
            self.res = max(self.res, left_depth + right_depth)
            # 返回节点深度
            return max(left_depth, right_depth) + 1
        depth(root)
        return self.res

解法三

二叉树的解题技巧是,首先判断问题能否划分为子问题、应当划分为什么样的子问题。二叉树直径实际上就是二叉树中的最长路径,我们是可以划分出子问题的:

二叉树的最长路径 = max{左子树的最长路径,右子树的最长路径,经过根结点的最长路径}

其中左子树的最长路径和右子树的最长路径是两个可以递归求解的子问题,那么经过根结点的最长路径如何计算呢?是左子树的深度加上右子树的深度。代入上面的式子得到:
二叉树的最长路径=max{左子树的最长路径,右子树的最长路径,左子树的深度+右子树的深度}

def diameterOfBinaryTree4(self, root):
        def traverse(node):
            # 返回二元 tuple (depth, diameter)
            # depth 表示子树的最大深度,diameter 表示子树的最长路径(直径)
            if not node: return (0, 0)
            left_depth, left_diam = traverse(node.left)
            right_depth, right_diam = traverse(node.right)

            # 求二叉树深度的常规方法
            depth = max(left_depth, right_depth) + 1
            # 套用推导出的最长路径公式
            diam = max(left_diam, right_diam, left_depth + right_depth)
            return depth, diam
        depth, diam  = traverse(root)
        return diam

相关知识总结和思考

相关知识:

BFS:广度/宽度优先。其实就是从上到下,先把每一层遍历完之后再遍历一下一层。

可以使用Queue的数据结构。我们将root节点初始化进队列,通过消耗尾部,插入头部的方式来完成BFS。

二叉搜索树(BST)的特性:

  1. 若它的左子树不为空,则所有左子树上的值均小于其根节点的值
  2. 若它的右子树不为空,则所有右子树上的值均大于其根节点的值
  3. 它的左右子树也分别为二叉搜索树

递归与迭代的区别

递归:重复调用函数自身实现循环称为递归;
迭代:利用变量的原值推出新值称为迭代,或者说迭代是函数内某段代码实现循环;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章