文章目录
Day 15 二叉树Part 2
102. 二叉树的层序遍历
递归法思路:使用一个记录当前节点深度的变量来控制当前节点值插入的位置。注意每当深入一层时都需要为向量数组扩容。
class Solution {
public:
void order(TreeNode* node, vector<vector<int>>& res, int depth){
if (node == NULL) return;
if (res.size() == depth)
res.push_back(vector<int>()); //注意这里为二维数组申请新的一行的写法
res[depth].push_back(node->val);
order(node->left, res, depth + 1);
order(node->right, res, depth + 1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
order(root, result, 0);
return result;
}
};
迭代法思路:使用一个队列来储存每一层的所有节点。注意到迭代法多数时候循环的判断条件为栈或队列是非空的。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()){
vector<int> vec;
int size = que.size();
for (int i = 0; i < size; i++){
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
226. 翻转二叉树
递归法思路较容易,注意三点:1. 交换两个相同种类的对象时使用c++标准库函数swap();2. 由于该题不用额外返回数组,所以使用单个函数直接递归即可。3. 下面的代码示例使用的是前序,使用中序时前后两个inverTree均需要传入root->left
(因为中间交换过了)。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
101. 对称二叉树
思路:此题与之前有所不同,思路上的重点有两个:1. 判断是否对称需要同时遍历根节点下的左右两颗树,所以递归函数的参数有两个;2. 递归函数应该具有bool类型的返回值,这样就可以使用&&来连接递归函数,在任何一个判断失败的情况下立刻返回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;
else return compare(left->left, right->right) && compare(left->right, right->left);
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
迭代法思路也值得一看:让左右子树的对称位置的节点两两入队(栈),并比较其是否相等:
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* leftNode = que.front(); que.pop();
TreeNode* rightNode = que.front(); que.pop();
if (!leftNode && !rightNode) { // 左节点为空、右节点为空,此时说明是对称的
continue;
}
// 左右一个节点不为空,或者都不为空但数值不相同,返回false
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
return false;
}
que.push(leftNode->left); // 加入左节点左孩子
que.push(rightNode->right); // 加入右节点右孩子
que.push(leftNode->right); // 加入左节点右孩子
que.push(rightNode->left); // 加入右节点左孩子
}
return true;
}
};
Day 16 二叉树Part 3
104. 二叉树的最大深度
简单直接的思路:借鉴层序遍历,在递归函数中传递一个深度参数。
class Solution {
public:
int countDepth(TreeNode* node, int depth){
if (node == NULL) return depth;
return max(countDepth(node->left, depth + 1), countDepth(node->right, depth + 1));
}
int maxDepth(TreeNode* root) {
int depth = 0;
return countDepth(root, depth);
}
};
实际上直接用原maxDepth函数的返回值传递深度就可以:
class solution {
public:
int maxdepth(treenode* root) {
if (root == null) return 0;
return 1 + max(maxdepth(root->left), maxdepth(root->right));
}
};
此外,本题还可以使用层序遍历时的迭代法,在每次队列清空并加入新一层的节点后depth++。
111. 二叉树的最小深度
思路:注意本题判断叶子节点的方式以及对左右只有一枝的节点的分类讨论。
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
if (!root->left && !root->right) return 1;
else if (!root->left) return 1 + minDepth(root->right);
else if (!root->right) return 1 + minDepth(root->left);
return 1 + min(minDepth(root->right), minDepth(root->left));
}
};
本题同样可以使用层序遍历的迭代法实现,在每一个节点出队时判断其是否为叶节点,是则立刻返回层深度。
222. 完全二叉树的节点个数
不利用完全二叉树的性质,直接遍历:
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
利用完全二叉树的性质,首先想到的是探测最下面一层的节点数目,可以使用二分法来缩短探测次数,但具体的行进路线怎么实现没有想通。力扣官方给出了使用最底层的节点序号的二进制表示来表示从根节点到当前节点的路径,详见exist()函数:
class Solution {
public:
bool exists(TreeNode* root, int level, int k) {
int bits = 1 << (level - 1);
TreeNode* node = root;
while (node != nullptr && bits > 0) {
if (!(bits & k)) {
node = node->left;
} else {
node = node->right;
}
bits >>= 1;
}
return node != nullptr;
}
int countNodes(TreeNode* root) {
if (root == nullptr) {
return 0;
}
int level = 0;
TreeNode* node = root;
while (node->left != nullptr) {
level++;
node = node->left;
}
int low = 1 << level, high = (1 << (level + 1)) - 1;
while (low < high) {
int mid = (high - low + 1) / 2 + low;
if (exists(root, level, mid)) {
low = mid;
} else {
high = mid - 1;
}
}
return low;
}
另一种方式是代码随想录给出的查找满二叉子树的思路,也很新奇:
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; // 这里初始为0是有目的的,为了下面求指数方便
while (left) { // 求左子树深度
left = left->left;
leftDepth++;
}
while (right) { // 求右子树深度
right = right->right;
rightDepth++;
}
if (leftDepth == rightDepth) {
return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};