文章目录
今日记录
二叉树理论基础
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,
注意我没有包括unordered_map、unordered_set,unordered_map、unordered_set(底层实现是哈希表。)
存储方式
可以是链式存储,也可以是顺序储存
链式存储方式就用指针, 顺序存储的方式就是用数组。
顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。
链式存储
顺序存储
如何遍历:
父节点下标是i,左子节点:2i+1 右子节点:2i+2
二叉树遍历方式
二叉树主要有两种遍历方式:
1.深度优先遍历:先往深走,遇到叶子节点再往回走。
2.广度优先遍历:一层一层的去遍历。
- 深度优先遍历:(栈)
前序遍历——中前后:递归法,迭代法
中序遍历——左中右:递归法,迭代法
后序遍历——左右中:递归法,迭代法 - 广度优先遍历:(队列)
层次遍历:迭代法
二叉树定义
链式存储:
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
递归
1.确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
2.确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
3.确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
144.二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
class Solution {
public:
// 确定递归函数的参数和返回值:每次递归需要处理的是存放遍历节点的vector,不需要返回值void
void traversal(TreeNode* cur, vector<int>& vec) {
// 确定终止条件:当前遍历的节点为空
if (cur == NULL)
return;
// 确定单层递归的逻辑:前序遍历:中左右顺序
vec.push_back(cur->val); // 中
traversal(cur->left, vec);
traversal(cur->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
145.二叉树的后序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL)
return;
traversal(cur->left, vec); // left
traversal(cur->right, vec); // right
vec.push_back(cur->val); // middle
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
94.二叉树的中序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& middle) {
if (cur == NULL)
return;
traversal(cur->left, middle);
middle.push_back(cur->val);
traversal(cur->right, middle);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
迭代(栈)
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
此时大家应该知道我们用栈也可以是实现二叉树的前后中序遍历了。
动画参考:
压栈的顺序理解
144.二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL)
return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // middle
st.pop();
result.push_back(node->val);
if (node->right)
st.push(node->right); // right
if (node->left)
st.push(node->left); // left
}
return result;
}
};
145.二叉树的后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root == NULL)
return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left)
st.push(node->left);
if (node->right)
st.push(node->right);
}
reverse(result.begin(), result.end());
return result;
}
};
94.二叉树的中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
result.push_back(cur->val);
cur = cur->right;
}
}
return result;
}
};
层序遍历
102.二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
使用队列实现
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()) {
int size = que.size();
vector<int> vec;
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;
}
};
107.二叉树的层序遍历Ⅱ
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
在最后将数组反转即可
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL)
que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
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);
}
reverse(result.begin(), result.end());
return result;
}
};
199.二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL)
que.push(root);
vector<int> result;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
// 判断是否遍历到最后一个元素
if (i == (size - 1))
result.push_back(node->val);
if (node->left) {
que.push(node->left);
}
if (node->right) {
que.push(node->right);
}
}
}
return result;
}
};
637.二叉树的层平均值
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10^-5 以内的答案可以被接受。
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL)
que.push(root);
vector<double> result;
while (!que.empty()) {
int size = que.size();
double sum = 0;
double average = 0;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
sum += node->val;
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
average = sum / size;
result.push_back(average);
}
return result;
}
};
429.N叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
遍历N个子节点
for (int i = 0; i < node->children.size(); i++) {
if (node->children[i])
que.push(node->children[i]);
}
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node*> que;
if (root != NULL)
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);
// N个子节点
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 != NULL)
que.push(root);
vector<int> result;
while (!que.empty()) {
int size = que.size();
int Max = INT_MIN;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
Max = node->val > Max ? node->val : Max;
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
result.push_back(Max);
}
return result;
}
};
116.填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != NULL)
que.push(root);
while (!que.empty()) {
int size = que.size();
Node* nodePre;
Node* node;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = que.front();
que.pop();
node = nodePre;
} else {
node = que.front();
que.pop();
nodePre->next = node;
nodePre = nodePre->next;
}
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
nodePre->next = NULL;
}
return root;
}
};
117.填充每个节点的下一个右侧节点指针II
给定一个二叉树:
struct Node { int val; Node *left; Node *right; Node *next; }
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。初始状态下,所有 next 指针都被设置为 NULL 。
实现过程同上
104.二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
层序遍历的应用
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
int depth = 0;
if (root != NULL)
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
depth++;
}
return depth;
}
};
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL)
que.push(root);
int depth = 0;
while (!que.empty()) {
depth++;
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (!node->left && !node->right)
return depth;
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
}
return depth;
}
};