二叉树遍历
通过二叉树前序遍历、中序遍历、后序遍历、广度优先遍历、深度优先搜索其中的两个搜索的顺序可以构造出完整的二叉树
代码实现
前序遍历:根节点->左子树->右子树
中序遍历:左子树->更节点->右子树
后序遍历:左子树->右子树->根节点
广度优先遍历:一层一层的遍历用队列易于实现
深度优先遍历:若存在子树则一直遍历子树,知道没有子树后返回
例题
给出一棵二叉树的后序遍历和中序遍历求出这棵二叉树的先序遍历,深度优先遍历和广度优先遍历
方法一:
#include <stdio.h>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 110;
int n;//节点的个数
struct node
{
int left=-1;//左子树
int right=-1;//右子树
}t[maxn];
int per[maxn],in[maxn],aft[maxn];
int cnt=0;
queue<int> q;
int find(int afl,int afr,int inl,int inr)
{
if(afl>afr)//后序遍历结果中左子树在右子树的后面,不符合要求,返回
return -1;
int x=aft[afr];
int i=inl;
per[cnt++]=x;
for(i;i<=inr;i++)
{
if(x==in[i])
break;
}
int cnt=i-inl-1;
t[x].left=find(afl,afl+cnt,inl,i-1);//左子树
t[x].right=find(afl+cnt+1,afr-1,i+1,inr);//右子树
return x; //返回根节点
}
void dfs(int index)
{
q.push(index);
if(t[index].left!=-1)//是否为左子树
dfs(t[index].left);
if(t[index].right!=-1)//是否为右子树
dfs(t[index].right);
return ;
}
void bfs(int index)
{
q.push(index);
while(!q.empty())
{
int x=q.front();
q.pop();
cout<<x<<' ';
if(t[x].left!=-1)//是否为左子树
q.push(t[x].left);
if(t[x].right!=-1)//是否为右子树
q.push(t[x].right);
}
return ;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&aft[i]);
}
for(int i=0;i<n;i++)
{
scanf("%d",&in[i]);
}
find(0,n-1,0,n-1);
printf("先序遍历:");
for(int i=0;i<n;i++)
{
printf("%d ",per[i]);
}
cout<<endl;
dfs(per[0]);
cout<<"深度遍历:";
while(!q.empty())
{
cout<<q.front()<<' ';
q.pop();
}
cout<<endl;
cout<<"广度遍历:";
bfs(per[0]);
return 0;
}
方法二:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n;//节点的个数
int pre[maxn],in[maxn],af[maxn];//先序 中序 后序遍历
struct node
{
int left=-1;//左子树
int right=-1;//右子树
}t[maxn];
int create(int inl,int inr,int afl,int afr)//中序遍历 后序遍历的左右节点的起止坐标
{
int x=af[afr];
if(inl>inr)
return -1;
int i=inl;
for(;i<=inr;i++)
{
if(x==in[i])
{
break;
}
}
int cnt=i-inl;//左子树的个数
t[x].left=create(inl,i-1,afl,afl+cnt-1);
t[x].right=create(i+1,inr,afl+cnt,afr-1);
return x;
}
void qian(int x)
{
cout<<x<<" ";
if(t[x].left!=-1)
qian(t[x].left);
if(t[x].right!=-1)
qian(t[x].right);
}
void dfs(int x)
{
cout<<x<<" ";
if(t[x].left!=-1)
dfs(t[x].left);
if(t[x].right!=-1)
dfs(t[x].right);
}
void bfs(int x)
{
queue<int> q;
q.push(x);
while(!q.empty())
{
int y=q.front();
q.pop();
cout<<y<<" ";
if(t[y].left!=-1)
q.push(t[y].left);
if(t[y].right!=-1)
q.push(t[y].right);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&af[i]);
}
for(int i=0;i<n;i++)
{
scanf("%d",&in[i]);
}
int root=af[n-1];//根节点
create(0,n-1,0,n-1);
//前序遍历
cout<<endl<<"先序遍历:";
qian(root);
//深度优先
cout<<endl<<"深度:";
dfs(root);
cout<<endl<<"广度:";
bfs(root);
}
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
可以构建出如图所示的二叉树
并查集
并查集是一种维护集合的数据结构,他的名字中“并”、“查”、“集”分别取自Union(合并)、Find(查找)、Set(集合)这三个单词,并查集支持下面两个操作:
- 合并:合并两个集合
- 查找:判断两个元素是否在一个集合
并查集一般有三个操作步骤
- 初始化:一开始,每个元素都是一个独立的集合,所有需要令所有元素的父节点等于他本身
- 查找:由于每个元素都存在一个根节点,因此查找元素的根节点可以用递推或递归的方式反复查找其父亲节点,直到其自身节点和父亲节点相同
例如如下一个父节点的情况
father[1]=1
father[2]=1
father[3]=2
father[4]=2
father[5]=5
father[6]=5
可以用如下图表示
从中可以看出:节点1、2、3、4的根节点都为1,5、6的根节点都为5
可以用递归反复查找的方法实现
代码实现
#include <stdio.h>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
int father[7]={
0,1,1,2,2,5,6};
int findfather(int x)
{
while(x!=father[x])
{
x=father[x];
}
return x;
}
int main()
{
for(int i=1;i<7;i++)
{
cout<<findfather(i)<<endl;
}
return 0;
}
- 合并:合并是指把两个集合合并成一个集合,一般会给出两个元素,要求将这两个元素所在的集合进行合并。
步骤:1.分别查找两个元素的根节点
2.判断两个元素的根节点是否相同,如果相同:则两个元素属于同一个集合不需要合并,否则进行合并(即一个元素的根节点指向另一个属于的根节点)
代码实现
void unio(int a,int b)
{
int fa=findfather(a);//查找a的父亲节点
int fb=findfather(b);//查找b的父亲节点
if(fa!=fb)//不属于一个集合
{
father[fa]=fb;//进行合并
}
}
例题讲解:https://pintia.cn/problem-sets/994805046380707840/problems/994805056736444416
代码实现:
#include <stdio.h>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn =10010;
int father[maxn]={
0};
int findfather(int x)
{
if(x==father[x])
return x;
return father[x]=findfather(father[x]);
}
void unio(int a,int b)
{
int fa=findfather(a);//查找a的父亲节点
int fb=findfather(b);//查找b的父亲节点
if(fa!=fb)//不属于一个集合
{
father[fa]=fb;//进行合并
}
}
int main()
{
int n,m;
scanf("%d",&n);
int cnt=0;
for(int i=0;i<maxn;i++)
{
father[i]=i;
}
for(int i=0;i<n;i++)
{
int x,a,b;
scanf("%d %d",&x,&a);
cnt=max(cnt,a);
for(int j=1;j<x;j++)
{
scanf("%d",&b);
cnt=max(cnt,b);
unio(a,b);
}
}
int sum=0;
for(int i=1;i<=cnt;i++)
{
if(findfather(i)==i)
sum++;
}
printf("%d %d\n",cnt,sum);
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
if(findfather(a)==findfather(b))
{
printf("Y\n");
}
else
printf("N\n");
}
return 0;
}
打表
打表是一种金典的空间换时间的技巧,一般是指将所有可能用到的结果事先计算出来,这样后面需要用到是直接查表获得,常见打表方法有以下两种:
- 在程序中一次性计算出所有需要用到的结果,之后的查询直接取这些结果
- 对一些感觉不会做的题目,先暴力计算小范围数据的结果,然后找规律获取能发现“蛛丝马迹”
相关博客:https://blog.csdn.net/a17865569022/article/details/83038133?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-10.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-10.control
https://www.cnblogs.com/lyj1/p/11516914.html