LeetCode 第 1080 题:根到叶路径上的不足节点

说明:本文是根据自己在 LeetCode 中文网站上发布的题解写成的,即自己转载了自己的文章。
原文地址:
https://leetcode-cn.com/problems/insufficient-nodes-in-root-to-leaf-paths/solution/hou-xu-bian-li-python-dai-ma-java-dai-ma-by-liweiw/

首先考虑结点如何删除

首先我们考虑如何删除结点的问题。已知一个二叉树中的结点要被删除,有两种办法:1、自己删除自己;2、告诉父亲结点,自己需要从二叉树中被删除。

“自己删除自己”让我想到了“单链表删除某个结点”,如果这个要被删除的结点是末尾结点,那还麻烦了。不过第 2 种办法“告诉父亲结点,自己需要从二叉树中被删除”,就很简单了,父亲结点收到孩子结点这个信号以后,只要把对孩子结点的引用切断即可

其次考虑使用哪一种遍历方式

二叉树的问题一定离不开遍历,遍历有 DFS 和 BFS,根据题目中的描述“考虑它所有 从根到叶的路径”,就知道不能用 BFS 了,那么 DFS 又有 3 种,分别如下:

1、先序遍历

(1)先执行当前结点的逻辑;
(2)如果有左结点,就递归执行左结点的逻辑;
(3)如果有右结点,就递归执行右结点的逻辑。

2、中序遍历

(1)如果有左结点,就递归执行左结点的逻辑;
(2)先执行当前结点的逻辑;
(3)如果有右结点,就递归执行右结点的逻辑。

3、后序遍历

(1)如果有左结点,就递归执行左结点的逻辑;
(2)如果有右结点,就递归执行右结点的逻辑;
(3)先执行当前结点的逻辑。

再看看我们首先考虑的问题,“告诉父亲结点,自己是否需要从二叉树中被删除”,那么首先两个子结点(如果存在的话)要清楚自己是不是需要被删除,明显使用“后序遍历”。

因此,删除结点(也可以称为“剪枝”)的过程是从下到上的

最后编码实现

进行后序遍历的时候,要告诉父亲节点自己是否需要从二叉树中删除,返回一个布尔值就可以了。这里编码要注意几个细节:

1、 使用 Python 编码的朋友,尽量少使用 not,否定的判断出现太多,比较容易把自己绕晕,我这一版代码是改过几次的,原先我的 __dfs 方法设置的返回值的意思是“是否保留”。后来我把返回值的含义改成“是否删除”,就是为了让逻辑中少一些 not

2、当一个结点不是叶子结点的时候,它是否被删除,也要看它的孩子结点,只要孩子结点有一个被保留,父亲结点就不能被删,换句话说,父亲结点被删除当且仅当它的两个孩子结点均被删除

image.png

3、返回值的含义设置成“是否删除”的前提下,左右孩子的默认策略是删除,因为当只有一个孩子结点存在的时候,这个孩子结点的删除与否直接决定了父亲结点是否被删除,逻辑运算符 and 把不存在的那一边设置为 True ,就符合这个逻辑,不妨看看真值表,把其中一列全部设置成 Trueand 的结果就正好和另外一列是一样的:

左子树是否被删除 右子树是否被删除 and or
True True True True
True False True False
False True False True
False False False False

如果你把 __dfs 方法的返回值意义设置成 是否保留,你就得看 or 那一列,并且左右孩子的默认策略就是保留。

下面的示例代码:

1、Python 代码:__dfs 方法返回值的意义是“当前结点是否被删除”;

2、Java 代码:__dfs 方法返回值的意义是“当前结点是否被保留”。

请读者比较它们二者的差别。

Python 代码:

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:

    def __dfs(self, node, s, limit):
        """
        后序遍历
        :param node: 当前遍历的结点
        :param s: 当前累计的和
        :param limit: 题目中给出的 limit
        :return: 是否要删除 node 这个结点,True 表示要删除,False 表示不删除
        """
        # 先写递归终止条件:如果小于 limit,根据题意,要删除
        if node.left is None and node.right is None:
            return s + node.val < limit

        # 默认为左右结点均剪枝,注意:初值不能设置成 False
        l_tree_deleted = True
        r_tree_deleted = True

        # 如果有左子树,就先递归处理左子树
        if node.left:
            l_tree_deleted = self.__dfs(node.left, s + node.val, limit)

        # 如果有右子树,就先递归处理右子树
        if node.right:
            r_tree_deleted = self.__dfs(node.right, s + node.val, limit)

        # 左右子树是否删除的结论得到了,由自己来执行是否删除它们
        if l_tree_deleted:
            node.left = None
        if r_tree_deleted:
            node.right = None

        # 只有左右子树都被删除了,自己才没有必要保留
        return l_tree_deleted and r_tree_deleted

    def sufficientSubset(self, root: TreeNode, limit: int) -> TreeNode:
        root_deleted = self.__dfs(root, 0, limit)
        if root_deleted:
            return None
        return root

Java 代码:

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }
}

public class Solution {

    /**
     * @param node
     * @param s
     * @param limit
     * @return 返回 node 结点是否被保留(注意:这个返回值的意义,直接影响整个逻辑。)
     */
    private Boolean dfs(TreeNode node, int s, int limit) {
        if (node.left == null && node.right == null) {
            return s + node.val >= limit;
        }

        // 注意:如果 dfs 的返回值的意义是这个结点是否被保留,它们的默认值应该设置为 false
        boolean ltree_saved = false;
        boolean rtree_saved = false;

        // 如果有左子树,就先递归处理左子树
        if (node.left != null) {
            ltree_saved = dfs(node.left, s + node.val, limit);
        }

        // 如果有右子树,就先递归处理右子树
        if (node.right != null) {
            rtree_saved = dfs(node.right, s + node.val, limit);
        }

        // 左右子树是否保留的结论得到了,由自己来执行是否删除它们
        if (!ltree_saved) {
            node.left = null;
        }

        if (!rtree_saved) {
            node.right = null;
        }

        // 左右子树有一颗被保留,自己就应该被保留
        return ltree_saved || rtree_saved;
    }

    public TreeNode sufficientSubset(TreeNode root, int limit) {
        boolean root_saved = dfs(root, 0, limit);
        if (!root_saved) {
            return null;
        }
        return root;
    }
}

复杂度分析

  • 时间复杂度:O(n)n 为二叉树结点的个数。

  • 空间复杂度:O(1)

以上,欢迎指正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容

  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,471评论 0 15
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,806评论 0 13
  • 数据结构和算法--二叉树的实现 几种二叉树 1、二叉树 和普通的树相比,二叉树有如下特点: 每个结点最多只有两棵子...
    sunhaiyu阅读 6,462评论 0 14
  • 目录 1、什么是树 2、相关术语 3、二叉树 3.1、二叉树的类型 3.2、二叉树的性质 3.3、二叉树的结构 3...
    我哈啊哈啊哈阅读 2,548评论 0 10
  • 不要轻易告诉我答案再留多一些时间给春鸟筑巢给蚂蚁寻穴给满塘的青苔换一季绝好的衣裳黛色的山峦隐匿风干的陈年故事小舟摇...
    古真阅读 154评论 0 3