赞
踩
今天的题目都与二叉树的深度或高度相关,所以需要对相关概念熟悉:
- 二叉树节点的深度:根节点->该节点。最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始);
- 二叉树节点的高度:该节点->叶子节点。最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始);
- 二叉树的最小深度:根节点到最近叶子节点(左右孩子都为空的节点才是叶子节点)的最短路径上的节点数量;
- 根节点的高度就是二叉树的最大深度;
- 前序求的是深度(因为每进入一层+1),后序求的是高度(因为每回退一层+1)。
这次的题在有了前面的基础后大都不难。有些题目的递归只需要以当前节点为空作为出口,有些则需要以当前节点无子节点作为出口。前者的root为空情况会在递归函数内处理,后者的这一情况会在主函数中处理。另外关于二叉树深度、高度、完全二叉树、满二叉树的相关计算和判断方法需要熟悉。
第1题(104.二叉树的最大深度)的递归解法的后序遍历方法比较简单,取左右子树深度的最大值,再得到当前树的深度最大值。
- class Solution {
- public:
- int getDepth(TreeNode* cur, int depth) {
- if (cur == nullptr) {
- return depth;
- }
- int depthLeft = getDepth(cur->left, depth + 1);
- int depthRight = getDepth(cur->right, depth + 1);
- return max(depthLeft, depthRight);
- }
- int maxDepth(TreeNode* root) {
- return getDepth(root, 0);
- }
- };
也可以用前序遍历,需要设定一个全局变量depth,首先用当前节点更新depth,再先后用左、右子树更新depth,返回最后的depth。
- class Solution {
- public:
- int ans;
- void getDepth(TreeNode* cur, int depth) {
- if (cur == nullptr) {
- ans = max(ans, depth);
- return;
- }
- getDepth(cur->left, depth + 1);
- getDepth(cur->right, depth + 1);
- }
- int maxDepth(TreeNode* root) {
- ans = 0;
- getDepth(root, 0);
- return ans;
- }
- };
而题解的写法更为清晰,将root为空的情况独立在主函数中处理,然后将当前节点无子节点的情况作为递归出口,并确保左、右子节点非空时才对其进行递归。
- class Solution {
- public:
- int ans;
- void getDepth(TreeNode* cur, int depth) {
- if (cur->left == nullptr && cur->right == nullptr) {
- ans = max(ans, depth);
- return;
- }
- if (cur->left != nullptr) {
- getDepth(cur->left, depth + 1);
- }
- if (cur->right != nullptr) {
- getDepth(cur->right, depth + 1);
- }
- }
- int maxDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- ans = 0;
- getDepth(root, 1);
- return ans;
- }
- };
迭代解法即二叉树的层序遍历,在遍历途中记录总共有多少层即可。
- class Solution {
- public:
- int maxDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- queue<TreeNode*> que;
- que.push(root);
- int depth = 0;
- while (!que.empty()) {
- int size = que.size();
- ++depth;
- for (int i = 0; i < size; ++i) {
- TreeNode *cur = que.front();
- que.pop();
- if (cur->left) {
- que.push(cur->left);
- }
- if (cur->right) {
- que.push(cur->right);
- }
- }
- }
- return depth;
- }
- };
二刷:更简单的写法:
- class Solution {
- public:
- int maxDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- return max(maxDepth(root->left), maxDepth(root->right)) + 1;
- }
- };
第2题(559.n叉树的最大深度)相比第1题(104.二叉树的最大深度)只是二叉树变为n叉树。后序遍历的递归解法也只需要改为在取depth时取所有子节点depth的最大值即可。要注意遍历所有子节点之前,depth初始化为depth + 1,而不是0。否则如果当前节点的子节点个数为0时就不会进行遍历,最终就会直接返回0。
- class Solution {
- public:
- int getDepth(Node* cur, int depth) {
- if (cur == nullptr) {
- return depth;
- }
- int depthMax = depth + 1;
- vector<Node*> children = cur->children;
- for (int i = 0; i < children.size(); ++i) {
- depthMax = max(depthMax, getDepth(children[i], depth + 1));
- }
- return depthMax;
- }
- int maxDepth(Node* root) {
- return getDepth(root, 0);
- }
- };
前序遍历的递归解法则需要注意一点,用children的size()作为循环的次数的话,如果size()为0,即没有子节点,那么循环就不会进行,其中的递归也不会进行,不会像上一题 (104.二叉树的最大深度)的前序遍历递归解法一样将空指针传入函数作为递归参数,所以depth + 1也不会作为参数,res在此时不会更新。解决方法就是在第9行这里就将depth更新为depth + 1。
- class Solution {
- public:
- int res;
- void getDepth(Node* cur, int depth) {
- res = max(res, depth);
- if (cur == nullptr) {
- return;
- }
- res = max(res, depth + 1);
- vector<Node*> children = cur->children;
- for (int i = 0; i < children.size(); ++i) {
- getDepth(children[i], depth + 1);
- }
- }
- int maxDepth(Node* root) {
- res = 0;
- getDepth(root, 0);
- return res;
- }
- };
而如果使用与上一题题解的前序递归一致的写法,就不会有上面的问题,就仍与二叉树情况是一致的。
- class Solution {
- public:
- int res;
- void getDepth(Node* cur, int depth) {
- vector<Node*> children = cur->children;
- if (children.size() == 0) {
- res = max(res, depth);
- return;
- }
- for (int i = 0; i < children.size(); ++i) {
- getDepth(children[i], depth + 1);
- }
- }
- int maxDepth(Node* root) {
- if (root == nullptr) {
- return 0;
- }
- res = 0;
- getDepth(root, 1);
- return res;
- }
- };
迭代法与上一题(104.二叉树的最大深度)除了要放入队列的节点数增多外没有其他区别。
- class Solution {
- public:
- int maxDepth(Node* root) {
- if (root == nullptr) {
- return 0;
- }
- queue<Node*> que;
- que.push(root);
- int depth = 0;
- while (!que.empty()) {
- int size = que.size();
- ++depth;
- for (int i = 0; i < size; ++i) {
- Node *cur = que.front();
- que.pop();
- vector<Node*> children = cur->children;
- for (int i = 0; i < children.size(); ++i) {
- que.push(children[i]);
- }
- }
- }
- return depth;
- }
- };
二刷:更简单的写法:
- class Solution {
- public:
- int maxDepth(Node* root) {
- if (root == nullptr) {
- return 0;
- }
- int ans = 1; // 不能设置为0,因为当前节点非空已经说明当前节点深度至少为1了
- for (Node* child : root->children) {
- ans = max(ans, maxDepth(child) + 1);
- }
- return ans;
- }
- };
第3题(111.二叉树的最小深度)所求的最小深度即为二叉树的最小高度,所以递归法采用后序遍历和前序遍历均可。需要注意的是如果左右节点的某一个为空,因为题目所说的最小深度是根节点->最近叶子节点上的节点数,那么当前节点的最小高度不是1,而是(1+另一非空节点的高度),这一点是与第1题(104.二叉树的最大深度)不一样的。所以后序遍历方法需首先对空节点返回当前depth,再对上述情况进行相应return,最后即为取左,右子树深度最小值的情况。
- class Solution {
- public:
- int getDepth(TreeNode* cur, int depth) {
- if (cur == nullptr) {
- return depth;
- }
- if (cur->left != nullptr && cur->right == nullptr) {
- return getDepth(cur->left, depth + 1);
- }
- if (cur->left == nullptr && cur->right != nullptr) {
- return getDepth(cur->right, depth + 1);
- }
- return min(getDepth(cur->left, depth + 1), getDepth(cur->right, depth + 1));
- }
- int minDepth(TreeNode* root) {
- return getDepth(root, 0);
- }
- };
前序遍历方法也沿用这一思路(直接使用前两道题题解的写法),使用全局变量res保存结果,递归终止条件为当前节点无左右子节点。因为是取最小值,但depth是随递归的嵌套而·变大的,所以这一题只能在递归终止时更新res。然后在当前节点有左、右子节点的情况下分别进行左、右子节点的递归。
- #include<climits>
- class Solution {
- public:
- int res;
- void getDepth(TreeNode *cur, int depth) {
- if (cur->left == nullptr && cur->right == nullptr) {
- res = min(res, depth);
- return;
- }
- if (cur->left != nullptr) {
- getDepth(cur->left, depth + 1);
- }
- if (cur->right != nullptr) {
- getDepth(cur->right, depth + 1);
- }
- return;
- }
- int minDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- res = INT_MAX;
- getDepth(root, 1);
- return res;
- }
- };
迭代法仍与之前一样,只需要进行提前return即可,提前return的时机是当前节点没有子节点时。
- class Solution {
- public:
- int minDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- queue<TreeNode*> que;
- que.push(root);
- int depth = 0;
- while (!que.empty()) {
- int size = que.size();
- ++depth;
- for (int i = 0; i < size; ++i) {
- TreeNode *cur = que.front();
- que.pop();
- if (cur->left == nullptr && cur->right == nullptr) {
- return depth;
- }
- if (cur->left) {
- que.push(cur->left);
- }
- if (cur->right) {
- que.push(cur->right);
- }
- }
- }
- return depth;
- }
- };
二刷:更简单的写法:
- class Solution {
- public:
- int minDepth(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- if (root->left == nullptr) {
- return minDepth(root->right) + 1;
- }
- else if (root->right == nullptr) {
- return minDepth(root->left) + 1;
- }
- else {
- return min(minDepth(root->left), minDepth(root->right)) + 1;
- }
- }
- };
第4题(222.完全二叉树的节点个数)的有通用解法和针对完全二叉树的特定解法。
通用解法的递归解法可以使用后序遍历方式,递归出口为当前节点空时,之后将(1 + 左子树节点数 + 右子树节点数)作为结果返回。
- class Solution {
- public:
- int countNodes(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- return 1 + countNodes(root->left) + countNodes(root->right);
- }
- };
迭代法也还是沿用层序遍历的模板,只需在内层循环过程中记录节点数。
- class Solution {
- public:
- int countNodes(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- queue<TreeNode*> que;
- que.push(root);
- int cnt = 0;
- while (!que.empty()) {
- int size = que.size();
- for (int i = 0; i < size; ++i) {
- ++cnt;
- TreeNode *cur = que.front();
- que.pop();
- if (cur->left) {
- que.push(cur->left);
- }
- if (cur->right) {
- que.push(cur->right);
- }
- }
- }
- return cnt;
- }
- };
针对完全二叉树的特定解法需要少量的理论基础。首先,满二叉树的节点个数为2^depth - 1(depth为路径节点数);其次,完全二叉树分以下2种情况:
所以针对完全二叉树的特定解法也是递归解法,在满足1时可直接得到其节点个数,而在情况2则递归其左、右子节点,直至满足1为止,然后总结点个数为(1 + 左子树节点数 + 右子树节点数)。所以问题转化为了如何判断一个完全二叉树是否为满二叉树,这里的理论是完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,即最左节点深度等于最右节点深度的话,该完全二叉树就是满二叉树。
- class Solution {
- public:
- int countNodes(TreeNode* root) {
- if (root == nullptr) {
- return 0;
- }
- TreeNode *left = root->left;
- TreeNode *right = root->right;
- int leftDepth = 0, rightDepth = 0;
- while (left != nullptr) {
- left = left->left;
- leftDepth++;
- }
- while (right != nullptr) {
- right = right->right;
- rightDepth++;
- }
- if (leftDepth == rightDepth) {
- return (2 << leftDepth) - 1;
- }
- return 1 + countNodes(root->left) + countNodes(root->right);
- }
- };
代码第19行使用了左移的方法来代替次方计算,可以将“用2 << x代替pow(2, x)”作为一项基本技巧掌握。通用方法的递归和迭代解法时间复杂度都是O(n),而该针对完全二叉树的特定解法的递归次数是logn,且每次递归中计算左、右子树的最左、最右节点深度,这一步骤时间复杂度也是O(logn),所以总的算法时间复杂度是O(logn * logn)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。