代码随想录算法训练营第11天|二叉树理论基础、二叉树的递归遍历、二叉树的迭代遍历、二叉树的层序遍历
二叉树理论基础
代码随想录视频内容简记
二叉树的种类
-
满二叉树
-
完全二叉树
-
二叉搜索树
-
平衡二叉搜索树
二叉树的存储方式
-
链式存储
-
线性存储
关于如何用代码“画”一个二叉树,其实就是用链表定义好左孩子和右孩子,之后返回这个链表的根节点即可
二叉树的遍历方式
和图论中的搜素方式一致,都大致可以分为两类
-
深度优先搜索
-
前序
-
中序
-
后序
-
-
广度优先搜索
二叉树中就是层序遍历
而在二叉树的前中后序遍历中,每一种便利的代码实现都可以使用递归法和迭代法两种来实现
二叉树的定义
二叉树的定义一定会要手写,应为在力扣中核心代码模式平常直接是定义好的,时间长不用就忘了
大致代码内容
关于二叉树代码的定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x): val(x), left(NULL), right(NULL) {}
};
注意其写法
二叉树的递归遍历
二叉树的递归遍历方式,k哥称递归三部曲
-
递归函数的参数和初始条件
-
递归的终止条件
-
确定单层递归的逻辑
关于前序遍历的写法
void traversal(cur, vector) {
if (cur == NULL) return;
// 根节点
push (cur, vector);
// 左子树
traversal(left);
// 右子树
traversal(right);
}
中序遍历就是
void traversal(cur, vector) {
if (cur == NULL) return;
// 左子树
traversal(left);
// 根节点
push (cur, vector);
// 右子树
traversal(right);
}
后序遍历就是
void traversal(cur, vector) {
if (cur == NULL) return;
// 左子树
traversal(left);
// 右子树
traversal(right);
// 根节点
push (cur, vector);
}
LeetCode144
2025-02-02 20:40:04 星期日
题目描述:力扣144
文档讲解:代码随想录(programmercarl)144. 二叉树的前序遍历
大致代码内容
在书写的时候注意要void traversal(TreeNode* root, vector<int>& vec)
这行的&
表示的是引用传递
void traversal(TreeNode* root, vector<int> vec) {
if (!root) return;
vec.push_back(root->val);
traversal(root->left, vec); // 复制 vec
traversal(root->right, vec); // 复制 vec
}
表示没有引用传递,vec在函数内部被修改,但是外部的vec不会受到影响
void traversal(TreeNode* root, vector<int>& vec) {
if (!root) return;
vec.push_back(root->val);
traversal(root->left, vec); // 修改原始 vec
traversal(root->right, vec); // 修改原始 vec
}
表示这里的vec是通过引用传递的,递归调用时修改的是同一个vec
LeetCode测试
点击查看代码
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
void traversal(TreeNode* root, vector<int>& vec) {
if (root == NULL) return;
vec.push_back(root->val);
traversal(root->left, vec);
traversal(root->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
LeetCode145
题目描述:力扣145
文档讲解:代码随想录(programmercarl)145.二叉树的后序遍历
LeetCode测试
代码很简单
点击查看代码
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec);
traversal(cur->right, vec);
vec.push_back(cur->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
};
LeetCode94
题目描述:力扣94
文档讲解:代码随想录(programmercarl)94.二叉树的中序遍历
LeetCode测试
点击查看代码
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec);
vec.push_back(cur->val);
traversal(cur->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
二叉树的迭代遍历
视频讲解:写出二叉树的非递归遍历很难么?这次让你不再害怕非递归!|二叉树的非递归遍历 | 二叉树的遍历迭代法 | 前序与中序
前序遍历
二叉树的迭代遍历中,遍历和处理结点是两个不同的概念。
其次,在二叉树的迭代法书写中,要用到栈这种数据结构
梳理
-
首先要传入一棵二叉树,只需要传入他的根节点就可以了
-
首先将根节点存入栈中
-
不断循环遍历,将栈顶元素弹出放入数组
-
之后先存入右子树,再存入左子树,将树按照先右后左的顺序放入栈中,弹出的时候才能是先左后右
大致代码内容
-
首先定义一个栈和一个数组,
stack<TreeNode*> st
,vector<int> vec
栈用于存放二叉树的结点,数组用于存放前序或者后序的结果(注意不能是中序,因为中序的遍历和处理顺序不一致) -
进入遍历,定义获取的node结点为栈顶部结点,
TreeNode* node = st.top()
,while (st!= NULL)
,之后判断存放的结点是不是空结点,若不是,则存放到数组if (node != NULL) vec.push_back(node->val) else continue
;若是,则continue
。 -
对前序遍历,先存放右子树,
st.push(node->right)
,后左子树,st.push(node->left)
后序遍历
梳理
后序遍历的顺序是LRN。
在前序的迭代法中,处理的顺序是按照NLR,在基础之上,只需要反转右子树和左子树的压入顺序,先变成NRL,之后再反转最后的处理数组变成LRN即可
中序遍历
梳理
-
定义指针来遍历这棵树
-
定义一个栈来对树中元素进行记录
-
如果指针不为空,则首先遍历他的左子树,并将遍历到的所有元素入栈
-
如果指针为空,则弹出栈顶元素的同时,需要遍历右子树
大致代码内容
-
while (cur != NULL || !stack.empty())
首先进入循环。注意这里的连接符号是||
,只要一个条件成立,循环继续,只有cur为空且stack为空才会终止 -
if (cur != NULL)
,cur = cur->left
-
cur = stack.top()
,首先给当前指针赋值栈顶元素,表示指针的回退,stack.pop()
,之后这个栈顶元素弹出,并加入数组,vec.push_back(cur->val)
,最后再进行cur = cur->right
LeetCode测试
前序遍历
注意每次获取完了栈顶部的结点之后要进行弹出
点击查看代码
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
// 获取顶部元素
TreeNode* node = st.top();
// 弹出
st.pop();
// 若结点有值,则压入数组
if (node != NULL) vec.push_back(node->val);
else continue;
// 遍历右子树和左子树
st.push(node->right);
st.push(node->left);
}
return vec;
}
};
后序遍历
点击查看代码
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()) {
TreeNode* node = st.top();
st.pop();
if (node != NULL) vec.push_back(node->val);
else continue;
st.push(node->left);
st.push(node->right);
}
reverse(vec.begin(), vec.end());
return vec;
}
};
中序遍历
点击查看代码
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> vec;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
vec.push_back(cur->val);
cur = cur->right;
}
}
return vec;
}
};
二叉树的层序遍历
LeetCode102
题目描述:力扣102
文档讲解:代码随想录(programmercarl)102.二叉树的层序遍历
视频讲解:《代码随想录》算法视频公开课:讲透二叉树的层序遍历 | 广度优先搜索 | LeetCode:102.二叉树的层序遍历
代码随想录视频内容简记
实现层序遍历的关键一是要用到队列,而是需要用到一个size
对每一层的元素数量进行控制
大致代码内容
-
定义一个队列
que
,一个vec
数组。之后把根节点入队,之后进入循环 -
要在循环Ⅰ开始时获取队列的长度
size
-
再来一个循环Ⅱ,对
while(size--)
,获取队头元素,之后弹出,分别遍历到左子树和右子树的下一层 -
循环Ⅱ结束,将
vec
数组添加到一个二维result
数组中
LeetCode测试
这个有一些细节需要注意
在循环每次添加的vec
数组,需要定义到循环内部,这样每次添加的元素才会不重复
错误代码
vector<int> vec;
while (!que.empty()) {
while (size--) {
...
}
}
会出现下面的这种

正确的应该是
while (!que.empty()) {
vector<int> vec;
while (size--) {
...
}
}
点击查看代码
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> result;
if(root != NULL) que.push(root);
while (!que.empty()) {
int size = que.size();
// 在循环中每次都定义一个新的vec来进行添加
vector<int> vec;
while (size--) {
TreeNode* cur = que.front();
// 操作数组
vec.push_back(cur->val);
// 队列弹出
que.pop();
if (cur->left != NULL) que.push(cur->left);
if (cur->right != NULL) que.push(cur->right);
}
result.push_back(vec);
}
return result;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端