递归写法:
前序遍历
cpp
class Solution {
vector<int> res{};
void dfs(TreeNode* root) {
if(root==nullptr) return;
res.push_back(root->val);
dfs(root->left);
dfs(root->right);
return;
}
public:
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
后序遍历
cpp
class Solution {
vector<int> res{};
void dfs(TreeNode* root) {
if(root==nullptr) return;
dfs(root->left);
dfs(root->right);
res.push_back(root->val);
return;
}
public:
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
中序遍历
cpp
class Solution {
vector<int> res{};
void dfs(TreeNode* root) {
if(root==nullptr) return;
dfs(root->left);
res.push_back(root->val);
dfs(root->right);
return;
}
public:
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
非递归写法:
前序遍历
cpp
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
// 非递归写法 根左右
vector<int> res{};
stack<TreeNode*> s;
if(root == nullptr) return {};
s.push(root);
while(!s.empty()){
auto node = s.top();
s.pop();
res.push_back(node->val);
if(node->right){
s.push(node->right);
}
if(node->left){
s.push(node->left);
}
// cout<<"{";
// for(auto val :s){
// cout<<","<<val<<endl;
// }
// cout<<"}"<<endl;
}
return res;
}
};
后续遍历 非递归写法就是 根右左 最后reverse就是 左右根了 注意和前序遍历非递归写法入栈顺序不同 前序非递归写法后入左孩子 后续需后入右孩子
cpp
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
// 非递归写法 根左右
vector<int> res{};
stack<TreeNode*> s;
if(root == nullptr) return {};
s.push(root);
while(!s.empty()){
auto node = s.top();
s.pop();
res.push_back(node->val);
if(node->left){
s.push(node->left);
}
if(node->right){
s.push(node->right);
}
// cout<<"{";
// for(auto val :s){
// cout<<","<<val<<endl;
// }
// cout<<"}"<<endl;
}
reverse(res.begin(),res.end());
return res;
}
};
中序遍历 注意这里非递归写法的前序和后序最大的不同是 入栈第一个元素 并是不取出来的第一个元素
cpp
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res{};
// 非递归写法 左根右
stack<TreeNode*> s;
auto cur = root;
if(cur==nullptr){
return {};
}
while(!s.empty()||cur!=nullptr){
while(cur){
s.push(cur);
cur=cur->left;
}
TreeNode * pnode = s.top();
s.pop();
res.push_back(pnode->val);
// cout<<"pnode->val:"<<pnode->val<<" s.size():"<<s.size()<<endl;
if(pnode->right){
cur = pnode->right;
}
}
return res;
}
};
ps:递归写法后续遍历不使用全局变量写法
```cpp
class Solution {
vector<int> res{};
void dfs(TreeNode* root) {
if(root==nullptr) return;
dfs(root->left);
dfs(root->right);
res.push_back(root->val);
return;
}
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root==nullptr) return{};
vector<int> le = postorderTraversal(root->left);
vector<int> ri = postorderTraversal(root->right);
vector<int> cur{};
if(!le.empty()){
cur.insert(cur.begin(), le.begin(),le.end());
}
if(!ri.empty()){
cur.insert(cur.end(), ri.begin(),ri.end());
}
cur.push_back(root->val);
// cout<<"le.size:"<<le.size()<<" ri.size:"<<ri.size()<<endl;
// cout<<"root:val:"<<root->val<<endl<<endl;
return cur;
}
};
**2.[二叉树层序遍历](https://leetcode.cn/problems/binary-tree-level-order-traversal/description/)**
```cpp
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
// 要用队列
deque<TreeNode*> qu;
vector<vector<int>> res_vv{};
if(root==nullptr){
return res_vv;
}
qu.push_back(root);
while(!qu.empty()){
int size = qu.size();
vector<int> res{};
for(int i=0;i<size;i++){
auto pnode = qu.front();
qu.pop_front();
res.push_back(pnode->val);
// 该节点是否存在左右节点
if(pnode->left){
qu.push_back(pnode->left);
}
if(pnode->right){
qu.push_back(pnode->right);
}
}
res_vv.push_back(res);
}
return res_vv;
}
};
上面代码需要注意的是 int size = qu.size(); 每层遍历的时候需要重新取一次 for(int i=0;i<qu.size();i++) 因为循环体内队列里面添加元素 qu.size()会实时变化的
3.翻转二叉树
cpp
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if(root==nullptr) return nullptr;
auto temp = root->left;
root->left = root->right;
root->right = temp;
invertTree(root->left);
invertTree(root->right);
return root;
}
};
4.对称二叉树
cpp
class Solution {
public:
bool dfs(TreeNode* leftroot, TreeNode* rightroot){
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if(leftroot == nullptr && rightroot == nullptr) return true;
if(leftroot != nullptr && rightroot == nullptr) return false;
if(leftroot == nullptr && rightroot != nullptr) return false;
// if(leftroot != nullptr && rightroot != nullptr)
if(leftroot->val != rightroot->val) return false;
// 后续遍历
return dfs(leftroot->left,rightroot->right) && dfs(leftroot->right,rightroot->left);
}
bool isSymmetric(TreeNode* root) {
// 自上而下 但是中途会有提前结束
if(root==nullptr) return true;
return dfs(root->left,root->right);
}
};
这里遍历顺序可以说是后续遍历 但是中途存在提前退出循环的可能 表面看起来是自顶向下(前序遍历)
5.二叉树的最大深度
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if(root == nullptr) return 0;
if(root->left ==nullptr && root->right ==nullptr) return 1;
int leftlen = maxDepth(root->left);
int rightlen = maxDepth(root->right);
int rootlen = max(leftlen,rightlen)+1;
return rootlen;
}
};
6.二叉树的最小深度
方法一 直接遍历到空节点
cpp
public:
int minDepth(TreeNode* root) {
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if(root ==nullptr) return 0;
int leftmin = minDepth(root->left);
int rightmin = minDepth(root->right);
int curtmin = 0;
if(leftmin == 0){
curtmin = rightmin+1;
}else if(rightmin == 0){
curtmin = leftmin+1;
}else {
curtmin = min(leftmin, rightmin) +1;
}
return curtmin;
}
};
明显需要后续遍历 每次取左右两个做小的深度 但是我们要注意一点 上面的遍历方式一定会遍历到某个节点的左孩子/右孩子节点是空 此时不能直接取最小值 需要做特殊处理
cpp
if(leftmin == 0){
curtmin = rightmin+1;
}else if(rightmin == 0){
curtmin = leftmin+1;
}else {
curtmin = min(leftmin, rightmin) +1;
}
方法二 不遍历空节点 (递归需要保证头结点不为空 因此需要在主函数中直接判断)
cpp
class Solution {
int posdfs(TreeNode* root) {
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if(root->left==nullptr && root->right==nullptr) return 1;
int leftmin, rightmin;
cout<<"start"<<endl;
if(root->left != nullptr && root->right == nullptr){
leftmin = minDepth(root->left);
// cout<<"leftmin:"<<leftmin+1<<endl;
return leftmin+1;
}
if(root->left == nullptr && root->right != nullptr){
rightmin = minDepth(root->right);
// cout<<"rightmin:"<<rightmin+1<<endl;
return rightmin+1;
}
// 左右都有高度
int rootlen;
leftmin = minDepth(root->left);
rightmin = minDepth(root->right);
rootlen = min(leftmin, rightmin)+1;
// cout<<"rootlen:"<<rootlen<<endl<<endl;
return rootlen;
}
public:
int minDepth(TreeNode* root) {
if(root==nullptr) return 0;
return posdfs(root);
}
};
我们先看看普通二叉树的节点数目写法 后序遍历
cpp
class Solution {
public:
int countNodes(TreeNode* root) {
if(root==nullptr) return 0;
int leftcount = countNodes(root->left);
int rightcount = countNodes(root->right);
int rootcount = leftcount+rightcount+1;
return rootcount;
}
};
完全二叉树写法(其实也就是 结束条件里面有中途rerurn逻辑场景)
cpp
class Solution {
public:
int countNodes(TreeNode* root) {
// 题目说是完全二叉树
// 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return) 确定单层循环逻辑
if (root == nullptr) return 0;
int leftdep=1,rightdep=1;
TreeNode* lpnode = root, *rpnode = root;
while(lpnode->left!=nullptr){
lpnode = lpnode->left;
leftdep++;
}
while(rpnode->right!=nullptr){
rpnode = rpnode->right;
rightdep++;
}
if (leftdep == rightdep) {
// 这个节点(包含当前节点)以下都是满二叉树
int curcount = (2<<(leftdep-1)) -1;
return curcount;
}
int leftcount = countNodes(root->left);
int rightcount = countNodes(root->right);
int curcount = leftcount + rightcount + 1;
return curcount;
}
};
8.平衡二叉树
9.二叉树的所有路径
10.左叶子之和
11.找树左下角的值
12.路径总和
14.最大二叉树