二叉树刷了十几道题才想起来记录,由于数据结构忘了好多,树的部分容易养成代码框架化思维,所以从二叉树开始刷起。
二叉树
二叉树有两种遍历方式:
-
深度优先遍历:从根节点往深走,遇到叶子节点再往回走。
(1)前序遍历(根左右)
(2)中序遍历(左根右)
(3)后序遍历(左右根) -
广度优先遍历:一层一层进行遍历。
一、递归遍历
先写一个最简单的前序遍历
144. 二叉树的中序遍历
题目描述:
给定一个二叉树的根节点 root ,返回它的中序遍历。
示例:
代码题解:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
// 递归函数
void inorder(TreeNode* root, vector<int>& res) { // 确定 参数 和 返回类型(利用res数组来存放节点的数值,所以不需要有返回值)
if (root == nullptr) return; // 终止条件
// 单层递归逻辑
inorder(root -> left, res); // 左
res.push_back(root -> val); // 根
inorder(root -> right, res); // 右
}
};
671. 二叉树中第二小的节点
题目描述:
- 给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。 更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。
- 要求:给出这样的一个二叉树,你需要输出所有节点中的 第二小的值 。如果第二小的值不存在的话,输出 -1 。
示例:
思路: 每个节点的子节点树不是2就是0,那么如果有子节点,根节点就是值最小的节点。
代码题解:
public:
int findSecondMinimumValue(TreeNode* root) {
if (root == nullptr) return -1; // 不存在第二小的情况输出-1
if (root -> left == nullptr && root -> right == nullptr) return -1; // 同上
int i = root -> left -> val; // 当前节点左子树的值
int j = root -> right -> val; // 当前节点右子树的值
if (i == root -> val) i = findSecondMinimumValue(root -> left); // 若等于最小值,递归查找左右子树
if (j == root -> val) j = findSecondMinimumValue(root -> right); // 右子树递归
if (i != -1 && j != -1) return min(i, j); // 第二小是除过根节点之外最小的值。
if (i != -1) return i;
return j;
}
【总结】
二叉树算法设计路线:只管当前节点要做的事,其他抛给递归框架。如果当前节点会对下面的子节点有影响,可以通过辅助函数增长参数列表,借助参数传递信息。
三要素法:
(1)确定递归函数的参数和返回值:明确每次递归的返回值是什么进而确定递归函数的返回类型。
(2)确定终止条件:终止条件不写或不对会导致栈溢出的错误。(基本上就是当前遍历的节点为空时本层递归结束)
(3)确定单层递归的逻辑:确定每一层递归需要处理的信息,就是上面说的当前节点要做的事。
二、层序遍历
从左到右一层一层地去遍历二叉树。应用图论中的广度优先遍历。
层序遍历需要借用一个辅助队列来实现,队列先进先出,所以符合一层一层遍历的逻辑。在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历的节点数,就能保证此次遍历的都是当前层的节点。
102. 二叉树的层序遍历
题目描述:
给你二叉树的根节点 root ,返回其节点值的层序遍历 。(即逐层地,从左到右访问所有节点)。
示例:
代码题解:
之后的层序遍历题目基本套用此框架模板
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que; // 初始化队列que
if (root != NULL) que.push(root);
vector<vector<int>> result; // 初始化二维数组存放结果
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.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;
}
};
递归方式:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
int depth = 0;
order(root, result, depth);
return result;
}
void order(TreeNode* root, vector<vector<int>>& result, int depth) {
if (root == nullptr) return;
if (result.size() == depth) result.push_back(vector<int>());
result[depth].push_back(root -> val);
order(root -> left, result, depth + 1);
order(root -> right, result, depth + 1);
}
};
637. 二叉树的层平均值
题目描述:
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
示例:
在层序遍历的时候每层求总和再取个均值
代码题解:
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> q;
if (root != nullptr) q.push(root);
vector<double> res;
while (!q.empty()) {
int size = q.size();
double sum = 0;
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
sum += node -> val; // 求和
if (node -> left) q.push(node -> left);
if (node -> right) q.push(node -> right);
}
res.push_back(sum / size); // 取平均
}
return res;
}
};
429. N 叉树的层序遍历
题目描述:
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
示例:
继续延用上面的层序遍历大框架
代码题解:
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node *> que;
if (root != nullptr) que.push(root);
vector<vector<int>> result;
while(!que.empty()) {
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
Node* node = que.front();
que.pop();
vec.push_back(node -> val);
// 遍历孩子节点
for (int i = 0; i < node -> children.size(); i++) {
if(node -> children[i]) que.push(node -> children[i]);
}
}
result.push_back(vec);
}
return result;
}
};
515. 在每个树行中找最大值
题目描述:
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
示例:
代码题解:
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
vector<int> result;
while(!que.empty()) {
int size = que.size();
int maxVaule = INT_MIN; // 用INT_MIN防止下溢 可能有负值出现
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
maxVaule = max(maxVaule, node -> val);
// 或者maxValue = node->val > maxValue ? node->val : maxValue;
if (node -> left) que.push(node -> left);
if (node -> right) que.push(node -> right);
}
result.push_back(maxVaule);
}
return result;
}
};
513. 找树左下角的值
题目描述:
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例:
代码题解:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
vector<int> vec;
if (root != nullptr) que.push(root);
while(!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node -> val);
if (node -> right) que.push(node -> right); // 每层先右后左,那么最后一层最外边的节点就是左下角的值
if (node -> left) que.push(node -> left);
}
}
int s = vec.size();
return vec[s-1]; // 最后一个元素
}
};