题目1:222.完全二叉树的节点个数
解法一:按照普通二叉树的解法
递归法
与之前求二叉树深度的解法类似,不过把深度换成了节点数
class Solution {
public:
int getnodenum(TreeNode* node) {
if (node == NULL) return 0;
int leftnum = getnodenum(node->left); //左
int rightnum = getnodenum(node->right); //右
return 1 + leftnum + rightnum; //中
}
int countNodes(TreeNode* root) {
getnodenum(root);
//也可以精简成:return 1 + countNodes(root->left) + countNodes(root->right);
}
};
迭代法
迭代法也是一样,将层数改为节点数
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
queue<TreeNode*> que;
que.push(root);
int num = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
num++;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return num;
}
};
解法二:按照完全二叉树的解法
根据完全二叉树的定义,有两种可能,即要么是满二叉树、要么是最后一层节点不满
对于满二叉树可以利用其性质2h-1计算,对于非满二叉树,可变遍历其左右孩子,直到某一深度的左右孩子为满二叉树
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftHeight = 0, rightHeight = 0; // 初始为0为了下面求指数方便
while (left) { // 求左子树深度
left = left->left;
leftHeight++;
}
while (right) { // 求右子树深度
right = right->right;
rightHeight++;
}
if (leftHeight == rightHeight) {
return (2 << leftHeight) - 1; // 注意(2<<1) 相当于2^2,所以leftHeight初始为0
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
题目2:110.平衡二叉树
要注意二叉树高度和深度的定义
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
在leetcode上根节点的深度为1
根据定义,如果一棵树是平衡二叉树,那么它的所有子树都是平衡二叉树。
求深度一般用前序遍历(中左右);
求高度一般用后序遍历(左右中)。
解法一:递归法
自顶向下的递归(前序遍历):
对于当前遍历到的节点,首先计算左右子树的高度;
如果高度差不大于1,再分别递归的遍历左右子节点,判断左右子树是否平衡。
class Solution {
public:
int getheight(TreeNode* node) {
if (node == NULL) return 0; //遇到空节点终止,当前节点为根节点的树高度为0
else return 1 + max(getheight(node->left), getheight(node->right)); //获取一个节点的最大高度
}
bool isBalanced(TreeNode* root) {
if (root == NULL) return true; //如果根节点为0,则高度差不超过1,为平衡二叉树
//当一个节点的左右子树高度差不超过1 且 该节点的左子树的左右子树高度差不超过1 且 该节点的右子树的左右子树高度差不超多1 返回真,否则返回假
else return abs(getheight(root->left) - getheight(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
};
这种写法,时间复杂度很高,从最后返回的结果就能看出来
自低向上的递归(后续遍历)
对于当前遍历到的节点,先递归的判断其左右子树是否平衡,再判断以当前节点为根节点的子树是否平衡。
class Solution {
public:
int getheight(TreeNode* node) {
if (node == NULL) return 0; //遇到空节点终止,当前节点为根节点的树高度为0
int leftheight = getheight(node->left); //求左子树的高度
if (leftheight == -1) return -1; //迭代发现某一节点的左右子树不平衡
int rightheight = getheight(node->right); //求右子树的高度
if (rightheight == -1) return -1;
int result; //如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
if (abs(leftheight - rightheight) > 1) result = -1; //abs求数据的绝对值,判断左右子树是否平衡
else result = 1 + max(leftheight, rightheight); //以当前节点为根节点的树的高度
return result;
}
bool isBalanced(TreeNode* root) {
return getheight(root) == -1 ? false : true;
}
};
化简后:
class SOlution {
public:
int getheight(TreeNode* node) {
if (node == NULL) return 0;
int leftheight = getheight(node->left);
int rightheight = getheight(node->right);
if (leftheight == -1 || rightheight == -1 || abs(leftheight - rightheight) > 1) return -1;
else return max(leftheight, rightheight);
}
bool isBalanced(TreeNode* root) {
return getheight(root) >= 0;
}
};
题目3:257.二叉树的所有路径
解法一:递归法
通过前序遍历让父节点指向孩子节点,然后把路径记录下来,通过回溯来退回当前路径进入另一条路径,寻找叶子节点
class Solution {
private:
void traversal(TreeNode* node, vector<int>& path, vector<string>& result) { //传入根节点、path记录每一条路径、result存放结果集
path.push_back(node->val); //前序遍历,先处理中间节点
if (node->left == NULL && node->right == NULL) { //左右节点都为空,遇到了叶子节点,需要回溯
string sPath;
for (int i = 0; i < path.size() - 1; i++) { //注意这里i<path.size()-1,意味着最后一个节点不放
sPath += to_string(path[i]); //将path里记录的路径转换成string格式
sPath += "->";
}
sPath += to_string(path[path.size() - 1]); //记录最后一个节点(叶子节点)
result.push_back(sPath);; //将记录的路径放入结果集里
return;
}
if (node->left) { //判空
traversal(node->left, path, result); //递归和回溯是一一对应的
path.pop_back(); //回溯,删除路径中最后一个元素
}
if (node->right) {
traversal(node->right, path, result);
path.pop_back(); //回溯
}
}
public:
vector<string>binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
};
代码可以优化为:
class Solution {
public:
void traversal(TreeNode* node, string path, vector<string>& result) { //定义string path,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果
if (node != NULL) {
path += to_string(node->val); //判空,并将中间节点加入路径
if (node->left == NULL && node->right == NULL) { //当前节点为叶子节点
result.push_back(path);
}
else {
traversal(node->left, path + "->", result); //回溯隐藏在 path + "->" 中
traversal(node->right, path + "->", result); //每次函数调用完,path如果没有加上"->",就回溯了
}
}
};
public:
vector<string>binaryTreePaths(TreeNode * root) {
vector<string> result;
string path;
traversal(root, path, result);
return result;
}
};
解法二:迭代法
用栈:
两个栈,一个模拟递归,一个存放遍历路径
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> tree; //保存遍历的节点
stack<string> pathST; //保存遍历路径
vector<string> result;
if (root == NULL) return result;
tree.push(root);
pathST.push(to_string(root->val));
while (!tree.empty()) {
TreeNode* node = tree.top(); //取出节点
tree.pop();
string path = pathST.top(); //取出该节点对应的值
pathST.pop();
if (node->left == NULL && node->right == NULL) result.push_back(path); //叶子节点
if (node->right) {
tree.push(node->right);
pathST.push(path + "->" + to_string(node->right->val));
}
if (node->left) {
tree.push(node->left);
pathST.push(path + "->" + to_string(node->left->val));
}
}
return result;
}
};
用队列:
官方题解给出了队列的解法
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
if (root == nullptr) {
return paths;
}
queue<TreeNode*> node_queue;
queue<string> path_queue;
node_queue.push(root);
path_queue.push(to_string(root->val));
while (!node_queue.empty()) {
TreeNode* node = node_queue.front();
string path = path_queue.front();
node_queue.pop();
path_queue.pop();
if (node->left == nullptr && node->right == nullptr) {
paths.push_back(path);
} else {
if (node->left != nullptr) {
node_queue.push(node->left);
path_queue.push(path + "->" + to_string(node->left->val));
}
if (node->right != nullptr) {
node_queue.push(node->right);
path_queue.push(path + "->" + to_string(node->right->val));
}
}
}
return paths;
}
};