需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义
文章讲解:代码随想录
前中后遍历
递归遍历 (必须掌握)
step1:确定递归函数的参数和返回值
step2:确定终止条件
step3:确定单层递归的逻辑
二叉树的三种递归遍历掌握其规律后,其实很简单
题目链接:
文章讲解/视频讲解:代码随想录
前序:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode* cur, vector<int>& vec){//ATTENTION
if (cur == NULL) return;
vec.push_back(cur->val);
traversal(cur->left, vec);
traversal(cur->right, vec);
}
};
中序:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root,res);
return res;
}
void traversal(TreeNode* root, vector<int>& vec){
if(root == NULL) return;
traversal(root->left,vec);
vec.push_back(root->val);
traversal(root->right,vec);
}
};
后序:
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root,res);
return res;
}
void traversal(TreeNode* cur, vector<int>& vec){
if(cur == NULL) return;
traversal(cur->left,vec);
traversal(cur->right,vec);
vec.push_back(cur->val);
}
};
用visual studio调试
#include <windows.h>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
TreeNode* buildTree(const vector<int>& vals) {
if (vals.empty()) return nullptr;
TreeNode *root = new TreeNode(vals[0]);
queue<TreeNode*> q;
q.push(root);
size_t i = 1;
while (!q.empty() && i < vals.size()) {
TreeNode *node = q.front();
q.pop();
if (vals[i] != NULL) {
node->left = new TreeNode(vals[i]);
q.push(node->left);
}
++i;
if (i < vals.size() && vals[i] != NULL) {
node->right = new TreeNode(vals[i]);
q.push(node->right);
}
++i;
}
return root;
}
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode* node, vector<int>& res) {
if (node == NULL) return;
res.push_back(node->val);
traversal(node->left, res);
traversal(node->right, res);
}
};
int main(){
//定义二叉树--方法1
//vector<int> values = { 3, 2, 3, NULL, 3, NULL, 1 };
//TreeNode *root = buildTree(values);
//定义二叉树--方法2
TreeNode* root = new TreeNode(3);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left = new TreeNode(2);
root->right = new TreeNode(2);
Solution ceshi;
vector<int> result = ceshi.preorderTraversal(root);
std::cout << result[0] << result[1] << result[2] << result[3] << result[4] << std::endl;
}
迭代遍历
题目链接(和上面一样,但是用迭代做):
文章讲解/视频讲解:代码随想录
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。因此用栈也可以是实现二叉树的前后中序遍历了。
前序遍历
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(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
中序遍历
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(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
后序遍历:中右左--->左右中
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
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;
}
};
统一迭代 (值得记)【待补充】
题目链接(和上面一样,但是用迭代做)
文章讲解:代码随想录
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right);
st.push(node); //*
st.push(NULL); //*
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.top(); // 重新取出栈中元素
st.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
};
在前序遍历的基础上,更改root的结构
class Solution {
public:
void flatten(TreeNode* root) {
if(root == nullptr) return;
vector<TreeNode*> vec;
iter(root, vec);
for(int i = 1; i < vec.size(); i++){
TreeNode* pre = vec[i - 1];
TreeNode* cur = vec[i];
pre->left = nullptr;
pre->right = cur;
}
return;
}
void iter(TreeNode* node, vector<TreeNode*> &vec){//注意加这个&
if(node == nullptr) return;
vec.push_back(node);
if(node->left) iter(node->left, vec);
if(node->right) iter(node->right, vec);
}
};
层序遍历
题目链接:
- 102.二叉树的层序遍历(openwods new window)
- 107.二叉树的层次遍历II(opens new window)
- 199.二叉树的右视图(opens new window)
- 637.二叉树的层平均值(opens new window)
- 429.N叉树的层序遍历(opens new window)
- 515.在每个树行中找最大值(opens new window)
- 116.填充每个节点的下一个右侧节点指针(opens new window)
- 117.填充每个节点的下一个右侧节点指针II(opens new window)
- 104.二叉树的最大深度(opens new window)
- 111.二叉树的最小深度
文章讲解/视频讲解:代码随想录
102.二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;//ATTENTION:*
if(root != NULL) que.push(root);
while(!que.empty()){
int size = que.size();//ATTENTION
vector<int> vec; //ATTENTION:要在这里定义,不然的话vec会累加上一次的就不是单次的了
while(size--){//ATTENTION:每pop一个节点就push这个节点的左右孩子
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);//ATTENTION:->val
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
107.二叉树的层序遍历Ⅱ
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if(root != NULL) que.push(root);
while(!que.empty()){//ATTENTION: NOT: que != NULL
vector<int> vec;
int size = que.size();
while(size--){
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if(node->left!=NULL) que.push(node->left);
if(node->right!=NULL) que.push(node->right);
}
res.push_back(vec);
}
reverse(res.begin(),res.end());//ATTENTION
return res;
}
};
199.二叉树的右视图
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
queue<TreeNode*> que;
// step1: 导入根节点
if(root!=NULL) que.push(root);
while(!que.empty()){
int rightSide;
int size = que.size();
while(size--){
TreeNode* node = que.front();
if(size == 0) rightSide = node->val;//size = 0就是最后一个数咯
que.pop();
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
res.push_back(rightSide);
}
return res;
}
};
637.二叉树的层平均值
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<double> res;
queue<TreeNode*> que;
if(root != NULL) que.push(root);
while(!que.empty()){
int size = que.size();
int size_backup = size;//ATTENTION:size_backup
double sum = 0;//ATTENTION:注意数据类型是double
while(size--){
TreeNode* node = que.front();
sum += node->val;
que.pop();
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
res.push_back(sum/size_backup);
}
return res;
}
};
429.N叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> res;
queue<Node*> que;
if(root != NULL) que.push(root);
while(!que.empty()){
vector<int> cur;
int size = que.size();
while(size--){
Node* node = que.front();
cur.push_back(node->val);
que.pop();
for(int i = 0; i < node->children.size(); i++){//ATTENTION: i < node->children.size()
que.push(node->children[i]);
}
}
res.push_back(cur);
}
return res;
}
};
515.在每个树行中找最大值
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
vector<int> res;
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size();
int max = INT32_MIN;
while(size--){
TreeNode* node = que.front();
max = max < node->val?node->val:max;
que.pop();
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
res.push_back(max);
}
return res;
}
};
116.填充每个节点的下一个右侧节点指针
层序遍历,二刷,我的
完美二叉树
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if(root != NULL) que.push(root);
while(!que.empty()){
int size = que.size();
while(size--){
Node* node = que.front();
que.pop();
if(size != 0){
Node* node2 = que.front();
node->next =node2;
}else node->next = NULL;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
117. 填充每个节点的下一个右侧节点指针 II
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if(root != nullptr) que.push(root);
else return root;
while(!que.empty()){
int size = que.size();
Node* pre = nullptr;
while(size--){
Node* node = que.front();
if(size == 0) node->next = nullptr;
if(pre) pre->next = node;
pre = node;
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
104.二叉树的最大深度
(下面有)
111.二叉树的最小深度
(下面有)
103. 二叉树的锯齿形层序遍历
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if(root != NULL) que.push(root);
int i = 0;
while(!que.empty()){
int size = que.size();
vector<int> vec;
while(size--){
TreeNode* node = que.front();
vec.push_back(node->val);//或者这里用双端队列deque接,分情况push_back or push_front;
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
if(i % 2 == 0) res.push_back(vec);
else{
reverse(vec.begin(), vec.end());
res.push_back(vec);
}
i++;
}
return res;
}
};
226.翻转二叉树 (前序遍历√后序遍历√)
这道题目一些做过的同学理解的也不够深入,先看视频讲解,无论做过没做过,都会有很大收获。
题目链接:226. 翻转二叉树 - 力扣(LeetCode)
文章讲解/视频讲解:代码随想录
关键是swap函数记得用啊~
下面程序段都行,可以放心root都是变换后的根节点,无论前序还是后序遍历。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
traversal_reverse(root);
return root;
}
void traversal_reverse(TreeNode* node){
if(node ==NULL) return;
traversal_reverse(node->left);
traversal_reverse(node->right);
swap(node->left,node->right);//前序或者后序都可以,但是不可以中序
}
};
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
// TreeNode* res;
return traveral(root);
}
TreeNode* traveral(TreeNode* node){
//以后序遍历为例
if(node == NULL) return node;
traveral(node->left);
traveral(node->right);
swap(node->left, node->right);//后序遍历
return node;
}
};
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.对称二叉树(非严格的后序遍历√)
先视频讲解,关键是左右孩子相互翻转之后是否可以重叠,且节点的数值相同
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)
class Solution {
public:
bool isSymmetric(TreeNode* root) {
bool res = compare(root->left,root->right);
return res;
}
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;
bool outside = compare(left->left,right->right);
bool inside = compare(left->right,right->left);
return outside && inside;
}
};
和下面相同的树相比较,对称二叉树要考虑将迭代函数设置两个参数,迭代的时候还要考虑外侧和外侧比,内侧和内侧比。是个难点。
100. 相同的树
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == nullptr && q == nullptr) return true;
else if(p != nullptr && q == nullptr) return false;
else if(p == nullptr && q != nullptr) return false;
else if(p->val == q->val) return(isSameTree(p->left, q->left) && isSameTree(p->right,q->right));
else return false;
}
};
二叉树的深度与高度
对于某一个节点而言,前序求的就是节点的深度,使用后序求的是节点的高度。
对于二叉树而言,根节点的高度就是二叉树的最大深度。因此用后序求二叉树最大深度比较简单。而对于最小深度,有差异不可直接1+min。
104.二叉树的最大深度(后√/前o序遍历;迭代:层序遍历)
什么是深度,什么是高度,如何求深度,如何求高度,这里有关系到二叉树的遍历方式。
大家 要先看视频讲解,就知道以上我说的内容了,很多录友刷过这道题,但理解的还不够。
题目链接:104. 二叉树的最大深度 - 力扣(LeetCode)
文章讲解/视频讲解: 代码随想录
【迭代:层序遍历】
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();
while(size--){
TreeNode* node = que.front();
que.pop();
if(node->left!=NULL) que.push(node->left);
if(node->right!=NULL) que.push(node->right);
}
depth++;
}
return depth;
}
};
【递归:后序遍历】---判断平衡二叉树在此基础上修改
class Solution {
public:
int maxDepth(TreeNode* root) {
return getDepth(root);
}
int getDepth(TreeNode* node){
if(node == NULL) return 0;
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
return 1 + max(leftDepth,rightDepth);
}
};
求最大深度(需要回顾)
class Solution {
public:
int result;
void getDepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
getDepth(node->left, depth + 1);
}
if (node->right) { // 右
getDepth(node->right, depth + 1);
}
return ;
}
int maxDepth(TreeNode* root) {
result = 0;
if (root == 0) return result;
getDepth(root, 1);
return result;
}
};
N叉树的最大深度
题目链接:559. N 叉树的最大深度 - 力扣(LeetCode)
代码:
【层序遍历】
class Solution {
public:
int maxDepth(Node* root) {
queue<Node*> que;
if(root != NULL) que.push(root);
int depth = 0;
while(!que.empty()){
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
Node* node = que.front();
que.pop();
for(int j = 0; j < node->children.size() ; j++){//ATTENTION
if(node->children[j]) que.push(node->children[j]);
}
}
}
return depth;
}
};
【后序遍历】
class Solution {
public:
int maxDepth(Node* root) {
return getDepth(root);
}
int getDepth(Node* node){
if(node == NULL) return 0;
int maxCur = 0;
for(int i = 0; i < node->children.size(); i++){
maxCur = max(maxCur, getDepth(node->children[i]));//有函数可以用
// max = max > getDepth(node->children[i]) ? max : getDepth(node->children[i]);
}
return 1 + maxCur;
}
};
111.二叉树的最小深度
先看视频讲解,和最大深度 看似差不多,其实 差距还挺大,有坑。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。
什么是叶子节点,左右孩子都为空的节点才是叶子节点!
- 对于有左有右的节点,1+min(right,left)
- 对于只有一边儿的节点,1+这边的最小深度。(这时不能1+min(right,left),因为right或者left一定有个是0)
- 对于无左无右的节点,深度就是1
题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)
文章讲解/视频讲解:代码随想录
class Solution {
public:
int minDepth(TreeNode* root) {
return getHeight(root);
}
int getHeight(TreeNode* root){
if(root==NULL) return 0;
int left_height = getHeight(root->left);
int right_height = getHeight(root->right);
//当左节点是空,右节点不是空时:不是叶子节点
if(root->left == NULL && root->right != NULL) return 1+right_height;
//当右节点时空,左节点不是空时:不是叶子节点
if(root->right == NULL && root->left != NULL) return 1+left_height;
return 1+min(right_height, left_height);
}
};
222.完全二叉树的节点个数
需要了解,普通二叉树 怎么求,完全二叉树又怎么求
对于完全二叉树:如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量
题目链接:代码随想录 (programmercarl.com)
文章讲解/视频讲解:代码随想录
如果是普通二叉树
迭代
class Solution {
public:
int countNodes(TreeNode* root) {
//如果是空节点,返回
if(root == NULL) return 0;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
递归
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
result++; // 记录节点数量
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
对于完全二叉树,可以先判断是不是子节点是不是满二叉树,提前结束递归
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。
如何去判断一个左子树或者右子树是不是满二叉树呢?
在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树
class Solution {
public:
int countNodes(TreeNode* root) {
//如果是空节点,返回
if(root == NULL) return 0;
//如果遇到满二叉树,可以用公式求,就可以提前返回了
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftDepth = 0, rightDepth = 0;
while(left){
left = left->left;
leftDepth++;
}
while(right){
right = right->right;
rightDepth++;
}
if(leftDepth == rightDepth){
return (2<<leftDepth)- 1; //2<<1相当于2^2
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
110.平衡二叉树
再一次涉及到,什么是高度,什么是深度,可以巩固一下。
一棵高度平衡二叉树定义为:二叉树每个节点 的左右两个子树的高度差的绝对值不超过1
这个题的难点在于,需要在返回数据中同时存入两个信息:是否是平滑二叉树,以及,此节点二叉树的高度,因此用-1表示不是平衡二叉树,其他用来表示二叉树的高度。
题目链接:110. 平衡二叉树 - 力扣(LeetCode)
文章讲解/视频讲解:代码随想录
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
};
第二次写
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(iter(root) == -1) return false;
else return true;
}
int iter(TreeNode* node){// -1 no; x height;
//对空节点
if(node == nullptr) return 0;
//对非空节点
else{
int leftHeight = iter(node->left);
int rightHeight = iter(node->right);
if(leftHeight == -1 || rightHeight == -1) return -1;//只要有一个节点不满足,就统统不满足
else if(abs(leftHeight - rightHeight) <= 1) return max(leftHeight, rightHeight) + 1; //只有当高度差小于等于1才计算本节点高度
else return -1; //否则判断本节点不满足
}
}
};
257. 二叉树的所有路径 (前序遍历!回溯登场!)
题目链接:257. 二叉树的所有路径 - 力扣(LeetCode)
文章讲解/视频讲解:代码随想录
需要用到前序遍历,因为需要指向它的左孩子和右孩子
递归法:
原创:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
res.clear();
if(root == NULL) return res;
pathInt.push_back(root->val);
traversal(root);
return res;
}
private:
vector<string> res;
vector<int> pathInt;
string pathStr;
void traversal(TreeNode* node){
//if(node == NULL) return;
if(node->left == NULL && node->right == NULL){
pathStr = standard(pathInt);
res.push_back(pathStr);//path中包含本层节点
return;
}
if(node->left){
pathInt.push_back(node->left->val);
traversal(node->left);
pathInt.pop_back();
}
if(node->right){
pathInt.push_back(node->right->val);
traversal(node->right);
pathInt.pop_back();
}
return ;
}
string standard(vector<int> vec){
string str = "" ;
for(int i = 0; i < vec.size(); i++){
str += to_string(vec[i]);//to_string
if(i != vec.size() - 1) str += "->";
}
return str;
}
上下两个代码的区别是
1、path和res可以作为全局变量用,以保存值,也可以以指针的形式传到递归函数里面
2、上面的代码里path在进入本层之前已经包含本层节点;下面的代码里在本层将本层节点放在path里的
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
vector<int> path;
if(root == NULL) return res;
Iter(root,path,res);
return res;
}
private:
//Pre-order traversal
void Iter(TreeNode* node, vector<int>& path, vector<string>& res){
//middle
string res_cell;
path.push_back(node->val);
if(node->left == NULL && node->right == NULL){
res_cell = Standard(path);
res.push_back(res_cell);
return;
}
//front
if(node->left != NULL){
Iter(node->left, path, res);
path.pop_back();//ATTENTION:not pop
}
//back
if(node->right != NULL){
Iter(node->right, path, res);
path.pop_back();
}
}
string Standard(vector<int> digit){
int size = digit.size();
string res_cell;
for(int i = 0; i < size-1; i++){
res_cell += to_string(digit[i]);
res_cell += "->";
}
res_cell += to_string(digit[size-1]);
return res_cell;
}
};
404.左叶子之和 (后序遍历!需要回顾)
其实本题有点文字游戏,搞清楚什么是左叶子,剩下的就是二叉树的基本操作。
题目链接:404. 左叶子之和 - 力扣(LeetCode)
文章讲解/视频讲解:代码随想录
左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历
递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if(root == NULL) return 0;
if(root->left == NULL && root->right == NULL) return 0;
//后序遍历
int leftValue = sumOfLeftLeaves(root->left);
if(root->left != NULL && root->left->left == NULL && root->left->right == NULL){
leftValue = root->left->val;
}
int rightValue = sumOfLeftLeaves(root->right);
int sum = leftValue + rightValue;
return sum;
}
};
513.找树左下角的值
本地递归偏难,反而迭代简单属于模板题, 两种方法掌握一下
题目链接:文章讲解/视频讲解:代码随想录
递归:
迭代:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if(root != NULL) que.push(root);
int res;
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; i++){
TreeNode* node = que.front();
if(i == 0) res = node->val;
que.pop();
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
}
return res;
}
};
112.路径总和
本题 又一次设计要回溯的过程,而且回溯的过程隐藏的还挺深,建议先看视频来理解
112. 路径总和,和 113. 路径总和ii 一起做了。 优先掌握递归法。
没有中节点的处理逻辑,前中后序遍历都可以
没有必要遍历所有节点,如果找到一个条路径就可以立刻返回了
文章讲解/视频讲解:代码随想录
下面这个count是剪完本层之后的数值
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == NULL) return false;
return traveral(root, targetSum - root->val);//牢记这里的count是剪完本层之后的数值
}
bool traveral(TreeNode* node, int count){//count是剪完本层node之后的数据
if(node->left == NULL && node->right == NULL && count == 0) return true;
if(node->left == NULL && node->right == NULL && count != 0) return false;
if(node->left != NULL){
count -= node->left->val;
if(traveral(node->left, count) == true) return true;//一直到root之后就结束了
count += node->left->val;
//可以用一句:if(node->left != nullptr) if(iter(node->left, target - node->left->val)) return true;
}
if(node->right != NULL){
count -= node->right->val;
if(traveral(node->right, count) == true) return true;//一直到root之后就结束了
count += node->right->val;
}
return false;
}
};
下面这个count是还没剪完本层之后的数值
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == NULL) return false;
if(root->left == NULL && root->right == NULL && targetSum == root->val) return true;
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
但是下面这样不行,因为left或者right可能没被定义
errrrroooooorrrrrr//
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr) return false;
return iter(root, targetSum - root->val);
}
bool iter(TreeNode* node, int target){
if(node->left == nullptr && node->right == nullptr){
if(target == 0) return true;
else return false;
}
if(node->left != nullptr) bool left = iter(node->left, target - node->left->val) ;
if(node->right!= nullptr) bool right = iter(node->right, target - node->right->val) ;
return left || right;
}
};
113. 路径总和ii
题目链接:113. 路径总和 II - 力扣(LeetCode)
这里需要保存所有符合条件的路径,因此递归函数没有返回值,且需要有变量存路径值
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val);//先存一下根节点
traversal(root, targetSum - root->val);//count是减去当前之后的值
return result;
}
private:
vector<vector<int>> result;
vector<int> path;
void traversal(TreeNode* node, int count){
if(node->left == NULL && node->right ==NULL && count == 0) {
result.push_back(path);//count == 0存一下再返回
return ;
}
if(node->left == NULL && node->right ==NULL) return ;//count != 0直接返回
if(node->left){
path.push_back(node->left->val);
count -= node->left->val;
traversal(node->left, count);
count += node->left->val;
path.pop_back();//ATTENTION:没有参数
}
if(node->right){
path.push_back(node->right->val);
count -= node->right->val;
traversal(node->right, count);
count += node->right->val;
path.pop_back();
}
return ;
}
构造二叉树
106.从中序与后序遍历序列构造二叉树
本题算是比较难的二叉树题目了,大家先看视频来理解。
106.从中序与后序遍历序列构造二叉树,105.从前序与中序遍历序列构造二叉树 一起做,思路一样的
从后序遍历中判断中间节点
从中序遍历中分割左右节点
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
题目链接/文章讲解/视频讲解:代码随想录
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0 || postorder.size() == 0) return nullptr;
return iter(inorder,0,inorder.size(),postorder,0,postorder.size());
}
TreeNode* iter(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postBegin, int postEnd){
//返回条件
if(inorderEnd == inorderBegin) return nullptr;
//新建一个节点,值是--中
int nodevalue = postorder[postEnd - 1];
TreeNode* node = new TreeNode(nodevalue);
//叶子节点返回
if(postEnd - postBegin == 1) return node;//左闭右开,说明只有一个叶子节点了
//中在中序中的位置
int index;
for(int i = inorderBegin; i < inorderEnd; i++){
if(inorder[i] == nodevalue){
index = i;//中在中序中的位置
break;
}
}
//分割中序
int leftinorderBegin = inorderBegin;
int leftinorderEnd = index;
int rightinorderBegin = index + 1;
int rightinorderEnd = inorderEnd;
//分割后序
int leftpostorderBegin = postBegin;
int leftpostorderEnd = postBegin + (leftinorderEnd - leftinorderBegin);
int rightpostorderBegin = postBegin + (leftinorderEnd - leftinorderBegin);
int rightpostorderEnd = postEnd - 1;//排除最后一个元素
//递归
node->left = iter(inorder, leftinorderBegin,leftinorderEnd,postorder,leftpostorderBegin,leftpostorderEnd);
node->right = iter(inorder, rightinorderBegin, rightinorderEnd, postorder, rightpostorderBegin, rightpostorderEnd);
//如果进行到这里了,那返回的就是根节点了
return node;
}
};
105.从前序与中序遍历序列构造二叉树
654.最大二叉树
又是构造二叉树,昨天大家刚刚做完 中序后序确定二叉树,今天做这个 应该会容易一些, 先看视频,好好体会一下 为什么构造二叉树都是 前序遍历
题目链接:654. 最大二叉树
文章讲解:代码随想录
视频讲解:又是构造二叉树,又有很多坑!| LeetCode:654.最大二叉树_哔哩哔哩_bilibili
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
int index, maxVaule = INT_MIN;
if(nums.size() == 0) return nullptr;
for(int i = 0; i < nums.size(); i++){
if(nums[i] > maxVaule){
index = i;
maxVaule = nums[i];
}
}
TreeNode* node = new TreeNode(maxVaule);//中
//叶子节点
if(nums.size() == 1) return node;
if(index > 0){//存在左子树
vector<int> left(nums.begin(), nums.begin() + index);//因为左闭右开,得保证index > 0
node->left = constructMaximumBinaryTree(left);
}
if(index < nums.size() - 1){//存在右子树
vector<int> right(nums.begin() + index + 1, nums.end());
node->right = constructMaximumBinaryTree(right);
}
return node;
}
};
617.合并二叉树
这次是一起操作两个二叉树了, 估计大家也没一起操作过两个二叉树,也不知道该如何一起操作,可以看视频先理解一下。 优先掌握递归。
题目链接/文章讲解:代码随想录
视频讲解:一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树_哔哩哔哩_bilibili
这种做法没开辟新空间:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == nullptr && root2 == nullptr) return nullptr;
else if(root1 != nullptr && root2 == nullptr) return root1;
else if(root1 == nullptr && root2 != nullptr) return root2;
else{
root1->val += root2->val;//将root2加到root1上了
}
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
// 运行到这就是根节点了
return root1;
}
};
二叉搜索树
700.二叉搜索树中的搜索
递归和迭代 都可以掌握以下,因为本题比较简单, 了解一下 二叉搜索树的特性
题目链接:. - 力扣(LeetCode)
文章讲解: 代码随想录
视频讲解:不愧是搜索树,这次搜索有方向了!| LeetCode:700.二叉搜索树中的搜索_哔哩哔哩_bilibili
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root == nullptr) return nullptr;
if(root->val == val) return root;
if(root->val > val) return searchBST(root->left, val);
if(root->val < val) return searchBST(root->right, val);
return nullptr;
}
};
如果没用二叉搜索树的特性的话,一些案例会超时
///timeout
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root == nullptr) return nullptr;
else if(root != nullptr && root->val == val) return root;
else
if(searchBST(root->left, val)!= nullptr) return searchBST(root->left, val);
else if(searchBST(root->right, val)!= nullptr) return searchBST(root->right, val);
else return nullptr;
}
};
所以还是得检查node->val与val的大小,来判断搜索方向
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root == nullptr) return nullptr;
else if(root->val == val) return root;
else if(root->val > val) return searchBST(root->left, val);
else return searchBST(root->right, val);
}
};
98.验证二叉搜索树
遇到 搜索树,一定想着中序遍历,这样才能利用上特性。
题目链接:. - 力扣(LeetCode)
文章讲解:代码随想录
视频讲解:你对二叉搜索树了解的还不够! | LeetCode:98.验证二叉搜索树_哔哩哔哩_bilibili
但本题是有陷阱的:左子树小于右子树,而不只是左节点小于右节点。所以觉得先用中序遍历存起来,然后再判断是不是递增数列,比较直观。迭代遍历的vec可以使传引用,也可以是全局变量这样用。
class Solution {
public:
vector<int> vec;
bool isValidBST(TreeNode* root) {
// vector<int> vec;
if(root == nullptr) return true;
iter(root);
for(int i = 0; i < vec.size() - 1; i++){
if(vec[i] >= vec[i+1]) return false;
}
return true;
}
void iter(TreeNode* node){
if(node == nullptr) return;
iter(node->left);
vec.push_back(node->val);
iter(node->right);
}
};
530.二叉搜索树的最小绝对差
需要领悟一下二叉树遍历上双指针操作,优先掌握递归
题目链接:
文章讲解:代码随想录
视频讲解:二叉搜索树中,需要掌握如何双指针遍历!| LeetCode:530.二叉搜索树的最小绝对差_哔哩哔哩_bilibili
501.二叉搜索树中的众数
和 530差不多双指针思路,不过 这里涉及到一个很巧妙的代码技巧。
可以先自己做做看,然后看我的视频讲解。
文章讲解:代码随想录
视频讲解:不仅双指针,还有代码技巧可以惊艳到你! | LeetCode:501.二叉搜索树中的众数_哔哩哔哩_bilibili
236. 二叉树的最近公共祖先
本题其实是比较难的,可以先看我的视频讲解
文章讲解:代码随想录
视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先_哔哩哔哩_bilibili
235. 二叉搜索树的最近公共祖先
对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。
题目链接:
文章讲解:代码随想录
视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili
701.二叉搜索树中的插入操作
本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。
题目链接:
文章讲解:代码随想录
视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili
450.删除二叉搜索树中的节点 [回顾]
相对于插入操作,本题就有难度了,涉及到改树的结构
题目链接:450. 删除二叉搜索树中的节点
文章讲解:代码随想录
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili
这个题的难点在于删除节点之后的拼接,对于找到的节点左右都有节点的二叉树而言,可以认为将左树移植到了右数的左下角。
可以和二叉搜索树的搜索,这个题目类比记忆。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == nullptr) return nullptr;
if(root->val == key){
if(root->left == nullptr && root->right == nullptr){
auto reNode = root;
delete reNode;
return nullptr;
}else if(root->left == nullptr){
auto reNode = root->right;
delete root;
return reNode;
}else if(root->right == nullptr){
auto reNode = root->left;
delete root;
return reNode;
}else{
TreeNode* cur = root->right;
while(cur->left != nullptr){
cur = cur->left;
}
cur->left = root->left;
TreeNode* temp = root;
root = root->right;
delete temp;
return root;
}
}
if(root->val > key) root->left = deleteNode(root->left, key);
if(root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};
669. 修剪二叉搜索树
这道题目比较难,比 添加增加和删除节点难的多,建议先看视频理解。
题目链接:
文章讲解: 代码随想录
视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树_哔哩哔哩_bilibili
108.将有序数组转换为二叉搜索树
本题就简单一些,可以尝试先自己做做。
题目连接:108. 将有序数组转换为二叉搜索树
文章讲解:代码随想录
视频讲解:构造平衡二叉搜索树!| LeetCode:108.将有序数组转换为二叉搜索树_哔哩哔哩_bilibili
538.把二叉搜索树转换为累加树
本题不难,在 求二叉搜索树的最小绝对差 和 众数 那两道题目 都讲过了 双指针法,思路是一样的。
文章讲解:代码随想录
视频讲解:普大喜奔!二叉树章节已全部更完啦!| LeetCode:538.把二叉搜索树转换为累加树_哔哩哔哩_bilibili