8. 对称二叉树
思路: 首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
递归法: 递归需要比较某层外侧与内侧对应的节点是否相同,首先确定参数是 两个树节点用于相互比较,其次确定终止条件 如果两个节点都为空那么返回true 如果有其中一个节点为空 那么返回false 剩余的情况就是两个节点都不为空,需要比较这两个节点的值以及 这两个节点左右子树是否对称的情况,对应的是左子树的左节点比较右子树的右节点,左子树的右节点比较右子树左节点
class Solution {
public:
bool compare(TreeNode* left,TreeNode* right) {
// 判断空结点的跳出循环的条件
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 当对比结点的值不相等时,返回false
// 剩余左右结点均不为空,且val相等的情况
// 继续向下递归,判断后续子树是否对称
// 比较外侧,左结点的左子树和右节点的右子树
bool outside = compare(left->left, right->right);
// 比较里侧,左节点的右子树和右节点的左子树
bool inside = compare(left->right, right->left);
// 判断当前结点的情况(左右都对称返回true)
return outside && inside;
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
迭代法: 利用队列存储每次要比较的结点,若相等,则将两结点的左右孩子入队,直至遍历完整个二叉树;反之,则直接返回false。
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
queue<TreeNode*> que;
que.push(root->left);
que.push(root->right);
while (!que.empty()) {
TreeNode* cur1 = que.front();
que.pop();
TreeNode* cur2 = que.front();
que.pop();
// 当一边为空,一边非空时,返回false
if (cur1 == NULL && cur2 != NULL) return false;
else if (cur1 != NULL && cur2 == NULL) return false;
// 当两边均为空时,表示当前子树符合条件,直接对比队中剩下的结点
else if (cur1 == NULL && cur2 == NULL) continue;
else if (cur1->val != cur2->val) return false; // 结点值不相等时,返回false
// 将需要比较的两节点,连续入队
// 左结点的左孩子和右节点的右孩子
que.push(cur1->left);
que.push(cur2->right);
// 左结点的右孩子和右节点的左孩子
que.push(cur1->right);
que.push(cur2->left);
}
return true;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
10. 二叉树的最大深度
思路: 与层序遍历9相同
class Solution {
public:
int maxDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> que;
if (root == NULL) return depth;
que.push(root);
while (!que.empty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (cur->left != NULL) que.push(cur->left);
if (cur->right != NULL) que.push(cur->right);
}
}
return depth;
}
};
10. 二叉树的最小深度
思路: 与层序遍历10一致
class Solution {
public:
int minDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> que;
if (root == NULL) return depth;
que.push(root);
while (!que.empty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (cur->left != NULL) que.push(cur->left);
if (cur->right != NULL) que.push(cur->right);
// 当左右孩子均为空时,返回当前深度
if (cur->left == NULL && cur->right == NULL) return depth;
}
}
return depth;
}
};
11. 完全二叉树的节点个数
方法一: 普通二叉树的做法,选定先/后/ 层序遍历之一,传入一个遍历,每轮遍历时加1,最后返回加和即可。
class Solution {
public:
void preorder(TreeNode* root, int &count) {
if (root == NULL) return;
count++;
preorder(root->left,count);
preorder(root->right, count);
}
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
int res = 0;
preorder(root, res);
return res;
}
};
时间复杂度: O(n)
空间复杂度: O(logn)
方法二: 针对完全二叉树的方法,对于根节点,判断是否为满二叉树。若为满二叉树,直接返回2^h -1 (h为二叉树的高度);反之,则按照正常的二叉树递归返回结果。递归时,判断孩子结点时,也可判断是否为完全二叉树。
判断是否为满二叉树: 分别一直遍历最左孩子和最右孩子,并记录深度。当左右深度相等时,则为满二叉树;反之,则不是满二叉树。
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
int leftheight = 0, rightheight = 0;
TreeNode* l = root->left;
TreeNode* r = root->right;
while (l) { // 求左边的深度
leftheight++;
l = l->left;
}
while (r) {
rightheight++;
r = r->right;
}
if (leftheight == rightheight)// 当左右深度相等时,为满二叉树,返回2^h-1
return (2 << leftheight) - 1; // 左移运算符 == 2 * 2^leftheight == 2^ h
// 若为非满二叉树,则用普通的二叉树递归计算
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
时间复杂度: O(logn * logn)
12. 平衡二叉树
思路: 求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中),接收左右子树的高度,判断当前子树是否为平衡二叉树。
递归法(传入一个bool类型,判断当前子树是否为平衡二叉树):
class Solution {
public:
int postorder(TreeNode* root, bool &isbalance) {
if (root == NULL) return 0;
int leftheight = postorder(root->left,isbalance);
int rightheight = postorder(root->right,isbalance);
if (leftheight - rightheight > 1 || rightheight - leftheight > 1)
isbalance = false;
return max(leftheight, rightheight) + 1;
}
bool isBalanced(TreeNode* root) {
if (root == NULL) return true;
bool isbalance = true;
postorder(root, isbalance);
return isbalance;
}
};
递归法(不传入bool变量作为参数来传递,直接使用返回值是否为-1进行判断)
class Solution {
public:
int postorder(TreeNode* root) {
if (root == NULL) return 0;
// 若当前子树的深度为-1,则该子树为非平衡二叉树
int leftheight = postorder(root->left);
if (leftheight == -1) return -1;
int rightheight = postorder(root->right);
if (rightheight == -1) return -1;
if (leftheight - rightheight > 1 || rightheight - leftheight > 1)
return -1;
return max(leftheight, rightheight) + 1;
}
bool isBalanced(TreeNode* root) {
if (root == NULL) return true;
return postorder(root) == -1 ? false : true;
}
};
13. 二叉树的所有路径
思路: 本题需要使用递归加回溯,使用一个数组保存根到当前结点的路径,若当前节点为叶结点,则将路径转换为字符串添加进结果数组中;若当前结点为非叶结点,则判断其左孩子和右孩子是否为叶节点,且当当层递归完成,删除路径的末尾元素,进行回溯。回溯和递归是一一对应的,有一个递归,就要有一个回溯
class Solution {
public:
void traversal(TreeNode* cur,vector<int>& path,vector<string> &res) {
path.push_back(cur->val); // 将当前结点的值加入路径中,叶节点的值也需加入
if (cur->left == NULL && cur->right == NULL) { // 左右孩子均为空,则找到叶节点
string curPath = "";
for (int i = 0; i < path.size() - 1; i++) { // 只添加n-1个,因为要单独加n-1个=》
curPath += to_string(path[i]); // 将int转换为字符串加入结果
curPath += "->";
}
curPath += to_string(path[path.size() - 1]); // 添加叶节点的值
res.push_back(curPath); // 添加结果
}
// 当不为叶节点时,保证下一层循环的 cur!= null
if (cur->left) {
traversal(cur->left, path, res);
path.pop_back(); // 回溯,路径path去掉cur->left->val
}
if (cur->right) {
traversal(cur->right, path, res);
path.pop_back(); // 回溯,同理
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
vector<int> path;// 使用int记录路径元素,当添加结果时,再进行转换
if (root == NULL) return res;
traversal(root, path, res);
return res;
}
};