二叉树刷题总结
一、构造二叉树输入
//
// 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;
}