二叉树是有左、右孩子的树,存储方式有顺序存储和链式存储。
二叉树的链式存储
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) {}
};
做题思路:
NULL
、0
或res
(结果集)。遇到叶子结点进行某种操作或判定后,再返回。遇到左、右孩子结点,进行某种操作或判定后,再继续向下遍历。TreeNode*
的常会用到返回的结点进行判定或孩子指针指向返回的节点(root->left=traversal(root->left)
)。vector
中;树的孩子结点个数不局限于两个。
深度优先遍历:前、中、后序(递归或迭代法用栈模拟)
广度优先遍历:层次遍历(用队列模拟)
43、【树和二叉树】二叉树的先、中、后和层次遍历方法合集(C/C++版)
递归
先序遍历的递归方式,常采用一个全局变量或深拷贝的变量进行配合加减,在末尾会返回这个变量并退出。
void preorderTraversal(TreeNode *root){
if(root == NULL) return ;
visit(root->data);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
非递归
void preorderTraversal(TreeNode* root) {
if(root == NULL) return ;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()) {
TreeNode* node = st.top();
st.pop();
visite(node->val);
if(node->right != NULL) st.push(node->right); // 因为栈是先进后出,因此先压右,再压左,可保证弹出为先左后右
if(node->left != NULL) st.push(node->left);
}
}
二叉树的先序遍历——递归+非递归
递归
常用于BST相关,递增序列的题。
void inorderTraversal(TreeNode *root){
if(root == NULL) return ;
inorderTraversal(root->left);
visit(root->data);
inorderTraversal(root->right);
}
非递归
void inorderTraversal(TreeNode* root){
stacck<TreeNode*> st;
TreeNode* p = root;
while(p != nullptr || !st.empty()){
while(p != nullptr){
st.push(p);
p = p->left;
}
p = st.top();
st.pop();
visit(node->data);
p = p->right;
}
}
二叉树的中序遍历——递归+非递归
后序遍历的递归方式,通常需要对底层的结点处理后,将信息返回给上一层结点,上一层的结点需要用到下面的信息进行返回。结点间的信息处理,通过利用栈返回的数值进行处理。
注:先序遍历,结点间的信息处理,通过一个深拷贝变量或全局变量进行处理。
递归
void postorderTraversal(TreeNode* root){
if(root == NULL) return ;
postorderTraversal(root->left);
postorderTraversal(root->right);
visit(root->data);
}
非递归
void postorderTraversal(TreeNode* root){
if(root == nullptr) return {};
stack<TreeNode*> st;
st.push(root);
// 遍历一个,存一个,先按照中右左的顺序存储
while(!st.empty()) {
TreeNode* node = st.top();
st.pop();
visit(node->data);
if(node->left != nullptr) st.push(node->left);
if(node->right != nullptr) st.push(node->right);
}
// 反转,将中右左变成左右中
reverse(res.begin(), res.end());
return res;
}
二叉树的后序遍历——递归+非递归
void levelorder(TreeNode* root){
/**
* 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 levelOrder(TreeNode* root) {
if(root == nullptr) return {};
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
// 获取一层的结点个数,遍历该层所有结点
int n = que.size();
while(n--) {
TreeNode* node = que.front();
que.pop();
visit(node->val);
// 将下一层的左右孩子存入队列中
if(node->left != nullptr) que.push(node->left);
if(node->right != nullptr) que.push(node->right);
}
}
return ;
}
};
}
二叉树的层次遍历、107.二叉树的层次遍历 II
求值:求平均值、最大最小值、最右侧值、左叶子之和、最左下角叶子
求路径:求所有路径、求满足某一条件的路径、N叉树的遍历
求每层结点的平均值
637.二叉树的层平均值
层次遍历,确定每层结点之和与结点数,相除之后,将结果加入结果集。
求每层最大值
515. 在每个树行中找最大值
迭代:层次遍历,确定每层结点数,找到这几个结点中的最大值,加入结果集当中。
求每层最右端元素
199. 二叉树的右视图
迭代:层次遍历,遍历到最后一个数时加入到结果集当中。
填充每个结点的下一个右侧结点
116. 填充每个节点的下一个右侧节点指针
117. 填充每个节点的下一个右侧节点指针 II
迭代:层次遍历。非尾结点,next指向续结点;尾结点,next指向NULL
层次遍历N叉树,对vector
进行for遍历获取孩子结点。
429. N 叉树的层序遍历
迭代:层次遍历。注意存储格式,int val
和vector
。对vector
求二叉树的所有路径
95、【树与二叉树】leetcode ——257. 二叉树的所有路径:递归法DFS/回溯法+迭代法DFS+层序遍历BFS(C++版本)
递归:先序遍历(回溯法)
迭代:先序遍历(两个递归栈)、层次遍历(两个队列)
求有某一路径之和满足目标值
98、【树与二叉树】leetcode ——112. 路径总和:5行精简代码回溯法[带剪枝]+迭代法(C++版本)
递归:先序遍历
迭代:先序遍历(两个递归栈)
求所有左叶子之和
96、【树与二叉树】leetcode ——404. 左叶子之和:递归法[先序+后序]+迭代法[先序+层次](C++版本)
根据左叶子特征判定出左叶子后,将左叶子的值加入到结果集中。
递归:先序遍历
迭代:先序遍历、层次遍历
求最左下角的叶子
97、【树与二叉树】leetcode ——513.找树左下角的值:层次遍历+回溯法(C++版本)
递归:先序遍历:设置一个全局高度记录,遇到第一个更大高度的,就记录该叶子,一定为最左端叶子。
迭代:层次遍历
先序遍历求解的是深度(从根到下),后序遍历求解的是高度(从下到根)。当所求的一个结点的深度=高度
时,先序和后续均可用。
递归方式求深度:一个局部变量depth
记录当前深度,一个全局变量res
整个数的最大或最小深度。
递归方式求高度: 获取左高度left
,右高度right
,再把结果返回给返回给上一层结点
深度: res = max(res, depth)
高度: return max(left, right) + 1;
求最大深度递归或迭代都可以,求最小深度优先用层序遍历方式。
求高度用后序遍历。
求整个二叉树的最大深度
90、【栈与队列】leetcode ——104. 二叉树的最大深度:层次遍历+DFS+子问题分解(C++版本)
递归:(1)先序遍历:回溯法;(2)后序遍历:子问题分解
迭代:层次遍历
求N叉树的最大深度
91、【栈与队列】leetcode ——559.n叉树的最大深度(递归DFS+迭代BFS)(C++版本)
递归:(1)先序遍历:回溯法;(2)后序遍历:子问题分解,获取每个结点的最大深度,结果传给上一层。
迭代:层序遍历
求整个二叉树的最小深度
92、【栈与队列】leetcode ——111. 二叉树的最小深度:层次遍历+先序DFS+后序DFS[子问题分解](C++版本)
递归:(1)先序遍历:回溯法;(2)后序遍历:子问题分解
迭代:层序遍历(优选)
求各结点高度差是否满足AVL条件
94、【树与二叉树】leetcode ——110. 平衡二叉树(C++版本)
递归:后序遍历,求高度差和高度
分别向左子树和右子树遍历,后序方式获取两边的返回值,传给上一层结点。
交换二叉树的每层结点
88、【树与二叉树】leetcode ——226. 翻转二叉树:先中后序的递归与DFS非递归遍历+BFS层次遍历(C++版本)
类比于数组,每层中两个结点交换
递归:先序遍历、中序遍历、后序遍历
迭代:三序、层次遍历
查看是否为对称二叉树(以根为中轴对称)
89、【树与二叉树】leetcode ——101. 对称二叉树:后序递归+迭代法+层次遍历(C++版本)
设置左右双指针进行遍历
递归:后序遍历
迭代:后序遍历、层次遍历
根据完全二叉树性质,求完全二叉树的节点个数
93、【树与二叉树】leetcode ——222. 完全二叉树的节点个数:普通二叉树求法+完全二叉树性质求法(C++版本)
设置左右双指针遍历最左侧和最右侧深度,判定此时结点以下为完全二叉树还是满二叉树
递归:后序遍历(完全二叉树性质求法)、先序遍历(普通二叉树求法)
迭代:层次遍历(普通二叉树性质求法)、先序遍历(普通二叉树求法)
根据中序+前/后/层次遍历构造二叉树:根据后三个,可以先得到根节点,然后再根据根节点,再在中序序列中划分出左右子树序列。依据此划分,再在后三个中,划分出对应的左右子序列。然后,再分别将对应的左子树序列和右子树序列向下遍历。
中序+后序构造二叉树
前序+中序构造二叉树
100、【树与二叉树】leetcode ——105. 从前序与中序遍历序列构造二叉树+106. 从中序与后序遍历序列构造二叉树(C++版本)
每次找到数组中最大值作为根节点,再将左侧作为左子树,右侧作为右子树,递归构造。
102、【树与二叉树】leetcode ——654. 最大二叉树(C++版本)
合并两个二叉树
101、【树与二叉树】leetcode ——617. 合并二叉树:递归法+迭代法(C++版本)
BST是一个规定了数值大小排列顺序的树,即 左子树结点 < 当前结点 < 右子树结点。
搜索二叉树中某一数值,若没有则返回NULL。
103、【树与二叉树】leetcode ——700. 二叉搜索树中的搜索:递归法+迭代法(C++/Python版本)
判定是否为BST树
104、【树与二叉树】leetcode ——98. 验证二叉搜索树:递归法[先序+中序+后序]+迭代法(C++版本)
返回任意两结点之间的差值
105、【树与二叉树】leetcode ——530. 二叉搜索树的最小绝对差:中序遍历递归法+迭代法(C++版本)
寻找到BST中出现频率最高的树
106、【树与二叉树】leetcode ——501. 二叉搜索树中的众数:双指针法+哈希表法(C++版本)
找到二叉树中任意两结点的最近公共祖先
107、【树与二叉树】leetcode ——236. 二叉树的最近公共祖先:回溯法(C++版本)
找到BST中任意两结点的最近公共祖先
108、【树与二叉树】leetcode ——235. 二叉搜索树的最近公共祖先:普通树解法+BST性质解法(C++版本)
在BST中插入一个元素
109、【树与二叉树】leetcode ——701. 二叉搜索树中的插入操作:递归法+双指针迭代法(C++版本)
删除BST中的指定元素结点
110、【树与二叉树】leetcode ——450. 删除二叉搜索树中的节点:递归法+迭代法(C++版本)
修剪BST,使BST中仅含有规定范围的元素
111、【树与二叉树】leetcode ——669. 修剪二叉搜索树:递归法(C++版本)
将有序数组转化为BST
112、【树与二叉树】leetcode ——108. 将有序数组转换为二叉搜索树:二分查找树(C++版本)
累加树:每个结点的新值 ≥ node.val值之和
113、【树与二叉树】leetcode ——538. 把二叉搜索树转换为累加树:递增数组视角右中左遍历(C++版本)