Day16 二叉树:513.找树左下角的值 112.路径总和 106.从中序与后序遍历序列构造二叉树

83 阅读6分钟

513.找树左下角的值

题目链接:513.找树左下角的值

难度指数:😀😐😕

二叉树中最后一行最靠左侧的值就是我们要求的树左下角的值。

本题迭代法比递归法更简单,迭代法在流程上也比递归法更直观。

本题重点还是建议掌握递归法,因为迭代法比较简单。

我们要求深度最大的叶子节点,它一定是在最后一行;

那么,如何求最后一行最靠左侧的节点?

这就涉及到遍历顺序,这道题使用前中后序遍历都是可以的:中左右、左中右、左右中

本题中是没有节点的处理逻辑,只要先遍历就可以,前中后序都是先遍历的左,这样在遍历的过程中,因为优先遍历的是左节点,我们一旦得到了这个深度最大的节点,那么优先遍历的是左节点的话,我们得到的就是最后一行,最靠左侧的节点。

递归解法:

代码思路:

定义一个全局变量,用来记录二叉树里面的最大深度,(因为在递归遍历中,递归到一个深度,就需要把这个最大深度记录下来)

将它初始化为int里面的最小值;

定义一个result,(每次遇到的深度,若比目前这个maxDepth大的话,就更新这个result,这样深度最大的节点的值就放进了result。)

终止条件:

遍历到叶子节点,我们就要去看深度了,我们要找深度最大的叶子节点。

回溯的过程就隐藏在递归函数的下面,(我们平时在写递归的时候,可能没有用到回溯,所以没有注意到)。

 int maxDepth = INT_MIN;  //记录遍历的所有深度里面最大的深度
 int result;
 //定义函数的参数和返回值      depth用来记录当前遍历的深度
 void traversal(root, depth) {
     //终止条件
     if (root->left == NULL && root->right == NULL) {  //找到叶子节点
         if (depth > maxDepth) {  //当前叶子节点的深度是不是比maxDepth大
             maxDepth = depth;  //那么最大深度就需要更新
             result = root->val;  //同时result要记录当前这个节点的数值
         }   //(这样result才能是遍历完整个二叉树之后,深度最大的叶子节点最左侧的节点)
     }
     //确定单层递归的逻辑      
     //左
     if (root->left) {  //向左递归遍历,会去确定左孩子是否为空 (避免操作空指针)
         depth++;
         traversal(root->left, depth);
         depth--;  //回溯
     }
     //右
     if (root->right) {  //若右孩子不为空
         depth++;
         traversal(root->right, depth);
         depth--;
     }
 }

没有的处理逻辑

所以本题前、中、后序遍历都可以。

代码写得有点冗余,目的是为了把回溯的过程体现出来。

AC代码: (核心代码模式)

 class Solution {
 public:
     int maxDepth = INT_MIN;  //遍历的当前这些深度里面最大的深度
     int result;
     void traversal(TreeNode* root, int depth) {
         if (root->left == NULL && root->right == NULL) {  //找到叶子节点
             if (depth > maxDepth) {
                 maxDepth = depth;
                 result = root->val;
             }
         }
         //左
         if (root->left) {
             depth++;
             traversal(root->left, depth);
             depth--;  //回溯
         }
         //右
         if (root->right) {
             depth++;
             traversal(root->right, depth);
             depth--;
         }
         return;
     }
     int findBottomLeftValue(TreeNode* root) {
         traversal(root, 0);
         return result;
     }
 };

112.路径总和

题目链接:112.路径总和

难度指数:😀😐

按照题目的意思,

这个二叉树中只要有一条从根节点到叶子节点的路径,相加结果等于 target (即找到一条合法的路径),就返回true;否则返回false。

当看到一个二叉树问题,就要想:如果使用递归法,该采用哪种遍历顺序? 也可以采用迭代法。

本题前、中、后序遍历都可以,因为并不涉及到的处理逻辑。(即放在哪里都可以)

递归解法:

代码思路:

确定函数的返回值和参数:返回值是bool类型

只要我们找到一条路径,它的节点之和等于 target(符合条件),那我们就没有必要再遍历其他路径了,直接原地返回就行。

同学会说:参数中 count 你应该传入一个0啊,这样我们从根节点往下遍历,遇到一个节点就+1,遇到一个节点就+1,……

最后判断 count 是否与 target 相等,再判断是否找到了一条合法的路径。

这么想没毛病,但代码实现会麻烦一些


我们可以:直接向 count 传入目标值 target ,遍历过程中每遇到一个节点,就将 count 减去这个节点的值,当最后到达叶子节点了,若 count = 0,说明沿着该路径的节点数值之和等于 target。 (妙啊!)

思路差不多,但代码就会简洁一些

如何找到一条路径,立刻就返回?

靠的是返回值。

确定终止条件:遇到叶子节点了,就要判断这条路径是不是我们想要的。

 bool traversal(TreeNode* node, int count) {  //需要一个计数器count
     //终止条件
     if (node->left == NULL && node->right == NULL && count == 0) {  //遇到叶子节点,且count = 0时
         return true;
     }
     if (node->left == NULL && node->right == NULL && count != 0) {
         return false;
     }
     //单层递归里面的逻辑
     //左
     if (node->left) {  //若向左不为空,则向左递归
         count -= node->left->val;
         if (traversal(node->left, count)) return true;  //内层:向左递归,外层:如果这个递归返回true,那我们就向上返回true
         count += node->left->val;  //回溯  (有递归就有回溯)
     }
     //右
     if (node->right) {
         count -= node->right->val;
         if (traversal(node->right, count)) return true;
         count += node->right->val;
     }
     return false;
 }

//左 if (node->left) { //若向左不为空,则向左递归 count -= node->left->val; if (traversal(node->left, count)) return true; //向左递归 count += node->left->val; //回溯 }

向左递归,会有一个返回值返回来

16.01.png

AC代码: (核心代码模式)

 class Solution {
 public:
     bool traversal(TreeNode* node, int count) {
         if (node->left == NULL && node->right == NULL && count == 0) {
             return true;
         }
         if (node->left == NULL && node->right == NULL && count != 0) {
             return false;
         }
         //单层递归的逻辑
         //左
         if(node->left) {
             count -= node->left->val;
             if (traversal(node->left, count)) return true;  //如果递归里面返回的是true,那么我们返回上层的也是true
             count += node->left->val;  //回溯
         }
         //右
         if (node->right) {
             count -= node->right->val;
             if (traversal(node->right, count)) return true;
             count += node->right->val;
         }
         return false;
     }
 ​
 public:
     bool hasPathSum(TreeNode* root, int sum) {
         if (root == NULL) {
             return false;
         }
         return traversal(root, sum - root->val);
     }
 };

迭代解法:

AC代码: (核心代码模式)

 ​

113.路径总和Ⅱ

题目链接:113.路径总和Ⅱ

106.从中序与后序遍历序列构造二叉树

题目链接:106.从中序与后序遍历序列构造二叉树

代码思路:

1️⃣:如果数组大小为零的话,说明是空节点了。

2️⃣:如果不为空,那么取后序数组最后一个元素作为节点元素。

3️⃣:找到后序数组最后一个元素在中序数组的位置,作为切割点

4️⃣:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

5️⃣:切割后序数组,切成后序左数组和后序右数组

6️⃣:递归处理左区间和右区间

 //确定递归函数的参数和返回值  (参数是2个数组,中序遍历和后序遍历)
 TreeNode* traversal(inorder, postorder) {
     if (postorder.size() == 0) {
         return null;
     }
     rootvalue = postorder[postorder.size() - 1];  //2️⃣获取后序数组里面的最后一个元素 (就是根节点元素)
     TreeNode* root = new TreeNode(rootvalue);
     if (postorder.size() == 1) return root;
     index = 0;
     for (index = 0; index < index.size(); index++) {
         
     }
 }

AC代码: (核心代码模式)

 ​

105.从前序与中序遍历序列构造二叉树

题目链接:105.从前序与中序遍历序列构造二叉树