二叉树总结

二叉树刷题总结

一、构造二叉树输入

//
// Created by apple on 2023/4/17.
//
#include<iostream>
#include<vector>
using namespace std;
struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int a):val(a),left(nullptr),right(nullptr){};
};
TreeNode* maketree(vector<int>& data){
    vector<TreeNode*> tree(data.size(), nullptr);
    TreeNode* root;
    for(int i=0;i<data.size();i++){
        TreeNode* node = new TreeNode(data[i]);
        tree[i] = node;
        if(i==0) root = node;
    }
    for(int i=0;i*2+1<tree.size();i++){
        tree[i]->left = tree[i*2+1];
        if(i*2+2<tree.size()){
            tree[i]->right = tree[i*2+2];
        }
    }
    return root;
}
void travel(TreeNode* root){
    if(root== nullptr) return;
    travel(root->left);
    cout<<root->val<<endl;
    travel(root->right);
}
int main(){
    vector<int>data = {1,2,3,4,5};
    TreeNode* root = maketree(data);
    travel(root);
    return 0;
}

二、递归遍历

class Solution {
public:
    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;
    }
};

226 翻转二叉树

给你一棵二叉树的根节点 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:
    TreeNode* reverse(TreeNode* root){
        TreeNode* tmp = new TreeNode(0);
        if(root==nullptr) return root;
        tmp = root->left;
        root->left = root->right;
        root->right = tmp;
        if(root->left) reverse(root->left);
        if(root->right) reverse(root->right);
        return root;

    }
    TreeNode* invertTree(TreeNode* root) {
        return reverse(root);
    }
};
这道题目使用bfs,直接先反转再遍历就行

三、迭代遍历

/**
 * 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:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root!=nullptr) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top();
            if(node!=nullptr){
                st.pop();
                if(node->right) st.push(node->right);
                if(node->left) st.push(node->left);
                st.push(node);
                st.push(nullptr);
            }
            else{
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};
前半截处理结点,中结点后面压入null。后半部分判断null保存结果

四、层序遍历

迭代法

给你二叉树的根节点 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:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*>que;
        vector<vector<int>> result;
        if(root!=nullptr) que.push(root);
        while(!que.empty()){
            int size = que.size();
            vector<int> path;
            while(size--){
                TreeNode* node = que.front();
                que.pop();
                path.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            if(path.size()!=0) result.push_back(path);
        }
        return result;
    }
};
这里使用bfs,需要注意每次使用了que.front()之后,都需要进行pop操作,防止重复遍历,导致超时
通过存储每个que.size来保证层的个数,并且每个层用for循环存在内层vector
递归法
/**
 * 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 dfs(TreeNode* node,vector<vector<int>>& result,int index){
        if(node==nullptr) return;
        if(result.size()==index) result.push_back(vector<int>());
        result[index].push_back(node->val);
        dfs(node->left,result,index+1);
        dfs(node->right,result,index+1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        dfs(root,result,0);
        return result;
    }
};

##二叉搜索树
每一个结点,左边只有比该节点小的,右边只有比该节点大的

538. 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。

/**
 * 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:
    // int ans;
    void travel(TreeNode* cur,int& ans){
        if(cur==nullptr) return ;
         travel(cur->right,ans);
        // ans += cur->val;
        // cur->val=ans;
        cur->val+=ans;
        ans = cur->val;
        travel(cur->left,ans);
    }
    TreeNode* convertBST(TreeNode* root) {
        int ans=0;
        travel(root,ans);
        return root;
    }
};
在这道题目中,由于二叉搜索树是有序的,所以只需要右中左遍历叠加即可。。
需要注意int需要加引用

五、前序遍历

1026. 节点与其祖先之间的最大差值

给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。

(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)

/**
 * 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:
    int pre=0;
    // int mx=0;
    // int mn =1e5;
    int ans =0;
    void dfs(TreeNode* node,int mn,int mx){
        if (node == nullptr) return;
        // 虽然题目要求「不同节点」,但是相同节点的差值为 0,不会影响最大差值
        // 所以先更新 mn 和 mx,再计算差值也是可以的
        // 在这种情况下,一定满足 mn <= node.val <= mx
        mn = min(mn, node->val);
        mx = max(mx, node->val);
        ans = max(ans, max(node->val - mn, mx - node->val));
        dfs(node->left,mn,mx);
        dfs(node->right,mn,mx);

    }
    int maxAncestorDiff(TreeNode* root) {
        int mn=root->val;
        int mx = root->val;
        dfs(root,mn,mx);
        return ans;
    }
};
在这道题目中,将题目抽象为每次搜索保存当前的最大最小状态,然后和当前的node进行计算。
需要注意的是,需要通过dfs将当前状态进行传递,而不能使用全局变量。

二、bfs刷题总结

bfs的输入通常为表格和起点

2.1迷宫问题

2.1.1单个起点,求最小路径

P1746 离开中山路最短距离
#include<bits/stdc++.h>
using namespace std;
int n;



int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
int bfs(int x3,int y3,int x4,int y4,vector<vector<int>>& map1,vector<vector<int>>& result){
    queue<pair<int,int>> que;
    que.push({x3,y3});
    while(!que.empty()){
        auto c = que.front();
        que.pop();
        for(int i=0;i<4;i++){
            int a = c.first+dx[i];
            int b = c.second+dy[i];
            if(a<1||b<1||a>n||b>n) continue;
            if(result[a][b]>0) continue;
            if(map1[a][b]!=0) continue;
            que.push({a,b});
            result[a][b] = result[c.first][c.second]+1;
            if(result[x4][y4]>0) return result[x4][y4];
        }
    }
    return -1;
}
int main(){
    cin>>n;
    vector<vector<int>> map1(n+1,vector<int>(n+1,0));
    vector<vector<int>> result(n+1,vector<int>(n+1,0));
    for(int i=1;i<=n;i++){
        string tmp;
        cin>>tmp;
        for(int j=1;j<=n;j++){
           map1[i][j]=tmp[j-1]-'0';
        }
    }
    int x_1,y_1,x_2,y_2;
    cin>>x_1>>y_1>>x_2>>y_2;
     cout<<bfs(x_1,y_1,x_2,y_2,map1,result);
//    cout<<y_2;
    return 0;
}
这道题目中,需要注意输入数据没有空格,所以需要使用striing来进行转存
P1443 马的遍历
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int N = 410;
int n,m,x,y;
vector<vector<int>> map(N,vector<int>(N,0));
vector<vector<int>> dis(N,vector<int>(N,-1));
queue<pair<int,int>> que;
int dx[]={2,2,1,1,-1,-1,-2,-2};
int dy[]={1,-1,2,-2,2,-2,1,-1};
void dfs(int x,int y){
    que.push({x,y});
    dis[x][y]=0;
    while(!que.empty()){
        auto node = que.front();
        que.pop();
        for(int i=0;i<8;i++){
            int a = node.x+dx[i];
            int b = node.y+dy[i];
            if(a<1||b<1||a>n||b>m) continue;
            if(dis[a][b]>=0) continue;
            dis[a][b] = dis[node.x][node.y]+1;
            que.push({a,b});
        }
    }
}
int main(){

   cin>>n>>m>>x>>y;
    dfs(x,y);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            // cout<<dis[i][j];
            printf("%-5d",dis[i][j]);
        }
        cout<<endl;
    }

    
    return 0;
}
本题需要注意输出对齐,使用printf("%5d",dis[i][j])表示右对齐,左补空格
%-5d为左对齐,右补空格
P1747 好奇怪的游戏

可以走田和日

#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int N = 1000;


int dx[]={-2,-1,1,2,2,2,2,1,-1,-2,2,-2};
int dy[]={2,2,2,2,1,-1,-2,-2,-2,-2,1,-1};

int bfs(int x,int y){
    vector<vector<int>> dis(N,vector<int>(N,-1));
    queue<pair<int,int>> que;
    que.push({x,y});
    dis[x][y]=0;
    while(!que.empty()){
        auto node = que.front();
        que.pop();
        for(int i=0;i<12;i++){
            int a = node.x+dx[i],b = node.y+dy[i];
            if(a<1||b<1||a>1000||b>1000) continue;
            if(dis[a][b]>=0) continue;
            dis[a][b] = dis[node.x][node.y]+1;
            if(a==1&&b==1) return dis[1][1];
            que.push({a,b});
        }
    }
    return -1;
}
int main(){
    int x1,y1,x2,y2;
    cin>>x1>>y1;
    cin>>x2>>y2;
    cout<<bfs(x1,y1)<<endl;;
    cout<<bfs(x2,y2);
    return 0;
}
需要注意多次进入bfs时,vector数组需要清空

2.1.2 多个起点,求最小路径

军团是一个 n 行 m 列的矩阵,每个单元是一个血色先锋军的成员。感染瘟疫的人,每过一个小时,就会向四周扩散瘟疫,直到所有人全部感染上瘟疫。你已经掌握了感染源的位置,任务是算出血色先锋军的领主们感染瘟疫的时间,并且将它报告给巫妖王,以便对血色先锋军进行一轮有针对性的围剿。

#include<bits/stdc++.h>
using namespace std;
int n,m,a,b;
int N = 510;
#define  x first
#define  y second
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
vector<vector<int>> dist(N,vector<int>(N,-1));
void bfs(){
    queue<pair<int,int>> que;
    for(int i=0;i<a;i++){
        pair<int,int> tmp;
        cin>>tmp.x>>tmp.y;
        que.push(tmp);
        dist[tmp.x][tmp.y]=0;
    }
    while(!que.empty()){
        pair<int,int> node = que.front();
        que.pop();
        for(int i=0;i<4;i++){
            int x1 = node.x+dx[i];
            int y1 = node.y+dy[i];
            if(x1<1||y1<1||x1>n||y1>m) continue;
            if(dist[x1][y1]>=0) continue;
            dist[x1][y1]=dist[node.x][node.y]+1;
            que.push({x1,y1});
        }
    }

}
int main(){

    cin>>n>>m>>a>>b;
    bfs();
    for(int i=0;i<b;i++){
        int x2,y2;
        cin>>x2>>y2;
        cout<<dist[x2][y2]<<endl;
    }


    return 0;
}
思路为,在刚开始的时候,就将多个起点压入队列中,同时进行bfs搜索

2.2 涂色问题

包围涂色

P1162 填涂颜色

由数字 0组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1构成,围圈时只走上下左右
4 个方向。现要求把闭合圈内的所有空间都填写成 2

#include<bits/stdc++.h>
using namespace std;
int n;
int N = 35;
#define  x first
#define  y second
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
vector<vector<int>> dist(N,vector<int>(N,0));
vector<vector<int>> cmp(N,vector<int>(N,0));
void bfs(int x1, int y1){
  queue<pair<int,int>> que;
  que.push({x1,y1});
  while(!que.empty()){
      auto node = que.front();
      que.pop();
      for(int i=0;i<4;i++){
          int x2 = node.x+dx[i];
          int y2 = node.y+dy[i];
          if(x2<0||x2>n+1||y2<0||y2>n+1) continue;
          if(dist[x2][y2]==1) continue;
          if(cmp[x2][y2]) continue;
          cmp[x2][y2]=1;
          que.push({x2,y2});
      }
  }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>dist[i][j];
        }
    }
    bfs(0,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dist[i][j]==0&&cmp[i][j]==0){
                dist[i][j]=2;
            }
            printf("%-2d",dist[i][j]);
        }
        cout<<endl;
    }



    return 0;
}
在这个问题中,需要注意,如果刚送进队列的第一个点,不符合判断条件,则不会再有其他点送进队列。所以需要保证第一个点的合法性,修改搜索点为0,0.。并且边界放大1点。

2.3 图的遍历

797. 所有可能的路径
dfs
	给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)
	
	 graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。
		class Solution {
		public:
		    vector<int>path;
		    vector<vector<int>> result;
		    void dfs(vector<vector<int>>& graph,int s,vector<int>& path){
		        path.push_back(s);
		        if(s==(graph.size()-1)){
		            result.push_back(path);
		            path.pop_back();
		            return ;
		        }
		        for(auto c:graph[s]){
		            dfs(graph,c,path);
		            
		        }
		        path.pop_back();
		       
		    }
		    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
		        dfs(graph,0,path);
		        return result;
		    }
		};
		在这道题目中,需要注意的是图的遍历,每次送进去的是图节点。所以不需要index,而是每次传入当前的结点值。
		而且每一个结点完成遍历之后,都需要pop。由于dfs是在for循环外部,所以终止条件里也需要有一个pop
bfs
		class Solution {
public:
vector<vector<int>> result;
    void bfs(vector<vector<int>>& graph,int s){
        queue<vector<int>> que;
        que.push({s});
        int n = graph.size()-1;
        while(!que.empty()){
            int size = que.size();
            while(size--){
                auto node = que.front();
                que.pop();
                for(auto c:graph[node.back()]){
                    node.push_back(c);
                    que.push(node);
                    if(node.back()==n){
                        result.push_back(node);
                        node.pop_back();
                        continue;
                        
                    }
                    node.pop_back();
                }
            }
        }
    }
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        bfs(graph,0);
        return result;
    }
};
		在bfs解法中,由于没有递归,所以不能用回溯来维护每次path的路径。所以,直接将path路径存入队列中。
树上逃离

给定一棵树,这个树有n个节点,节点编号从0开始依次递增,0固定为根节点。在这棵树上有一个小猴子,初始时该猴子位于根节点(0号) 上,小猴子一次可以沿着树上的边从一个节点挪到另一个节点,但这棵树上有一些节点设置有障碍物,如果某个节点上设置了障碍物,小猴子就不能通过连接该节点的边挪动到该节点上。问小猴子是否能跑到树的叶子节点(叶子节点定义为只有一条边连接的节点),如果可以,请输出小猴子跑到叶子节点的最短路径(通过的边最少),否则输出字符串NULL。

#include<bits/stdc++.h>
using namespace std;
int n,edge,block;
vector<int> bfs(vector<vector<int>>& vec,vector<int>& blocks,vector<int>& visit){
    vector<int> path;

    queue<vector<int>> que;
    que.push({0});
    while(!que.empty()){
        auto node = que.front();
        que.pop();
        int index = node.back();
        if(vec[index].size()==0) return node;
        for(int i=0;i<vec[index].size();i++){
            int e = vec[index][i];
            if(blocks[e]||visit[e]){

                continue;
            }

            node.push_back(e);
            que.push(node);
        }
    }
    return path;
}
int main(){
    cin>>n>>edge;
    vector<vector<int>>vec(n+1,vector<int>());
    vector<int> blocks(n+1,0);
    vector<int> visit(n+1,0);
    for(int i=0;i<edge;i++){
        int s,e;
        cin>>s>>e;
        vec[s].push_back(e);
    }
    cin>>block;
    for(int i=0;i<block;i++){
        int tmp;
        cin>>tmp;
        blocks[tmp]=1;
    }
    vector<int> result = bfs(vec,blocks,visit);
    for(auto c:result){
        cout<<c;
        cout<<"->";
    }
    if(result.size()==0) cout<< NULL;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值