二叉树_题目9:1080. 根到叶路径上的不足节点

83 阅读3分钟

题目9:1080. 根到叶路径上的不足节点

1080. 根到叶路径上的不足节点

同类题目

前置知识

题目描述

给你二叉树的根节点 root 和一个整数 limit ,请你同时删除树中所有 不足节点 ,并返回最终二叉树的根节点。

假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit,则该节点被称之为 不足节点 ,需要被删除。

叶子节点,就是没有子节点的节点。

示例 1:

 输入:root = [1,2,3,4,-99,-99,7,8,9,-99,-99,12,13,-99,14], limit = 1
 输出:[1,2,3,4,null,null,7,8,9,null,14]

示例 2:

 输入:root = [5,4,8,11,null,17,4,7,1,null,null,5,3], limit = 22
 输出:[5,4,8,11,null,17,4,7,null,null,null,5]

示例 3:

 输入:root = [1,2,-3,-5,null,4,null], limit = -1
 输出:[1,null,-3,4]

提示:

  • 树中节点数目在范围 [1, 5000]
  • -105 <= Node.val <= 105
  • -109 <= limit <= 109

思路分析

节点的删除判断

叶子节点: 根到叶子的路径仅有一条,如果这条路径的元素和小于 limit 那么该节点可以删除。

非叶子节点:当且仅当左右子节点都删除,该节点才应该删除,换句话说只要孩子结点有一个被保留,父亲结点就不能被删。当前节点的左子树的所有叶子节点均为「不足节点」,则左孩子需要删除,否则保留。右子树同理。

从上面的分析得出本题需要使用后序遍历,因为节点的删除需要依赖左右子节点的结果。

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

程序代码

 //https://leetcode.cn/problems/insufficient-nodes-in-root-to-leaf-paths/description/
 //1080. 根到叶路径上的不足节点
 ​
 #include<iostream>
 #include<vector>
 using namespace std;
 struct TreeNode {
     int val;
     TreeNode *left;
     TreeNode *right;
     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };
 ​
 ​
 class Solution {
 public:
     // 返回当前节点是否应该被删除
     bool travel(TreeNode* root, int limit,int sum)
     {
         // 判断头结点,如果传入null,那么返回false
         if(root == nullptr)
         {
             return false;
         }
         // 当前为叶子节点,返回叶子节点是否大于等于limit
         // sum >= limit 不删除
         // sum < limit 删除
         if(root->left == nullptr && root->right == nullptr)
         {
             sum += root->val;
             return sum >=limit;
         }
         // 非叶子节点,把当前节点的val 加上后,传入左右子节点中
         
         sum += root->val;
         bool leftv = travel(root->left,limit,sum);
         bool rightv = travel(root->right,limit,sum);
         // 如果leftv 为false 删除
         if(!leftv)
         {
             root->left = nullptr;
         }
         // 如果rightv 为false 删除
         if(!rightv)
         {
             root->right = nullptr;
         }
         // 返回给父节点,当前节点是否可以删除
         return leftv || rightv;
     }
 ​
     TreeNode* sufficientSubset(TreeNode* root, int limit) {
         bool res = travel(root,limit,0);
         return res ? root : nullptr;
     }
 };

复杂度分析

时间复杂度O(N),其中 N 表示树中节点的数目,深度递归遍历,每个节点遍历一次。

空间复杂度O(N),其中 N 表示树中节点的数目,最差情况下退化为单链表,高度为 N

题目感悟

本题需要使用后序遍历,但本质上使用了「分解问题」的思路:因为当前节点接收并利用了子树返回的信息,这就意味着你把原问题分解成了当前节点 + 左右子树的子问题。

删除节点:使用父亲节点删除孩子节点的方式。

更多相关知识:github.com/pingguo1987…