二叉树的存储
P4715 【深基16.例1】淘汰赛
#include <bits/stdc++.h>
using namespace std;
int n, id, value[260], win[260], total, ans;
void dfs(int cur){
if(cur>total-1){
return;
}
int leftid=2*cur;
int rightid=2*cur+1;
dfs(leftid);
dfs(rightid);
if(value[leftid]>value[rightid]){
value[cur]=value[leftid];
win[cur]=win[leftid];
}
else{
value[cur]=value[rightid];
win[cur]=win[rightid];
}
}
int main()
{
scanf("%d", &n);
total=1<<n;
/*
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
*/
for(int i=0; i<total; ++i){
id=total+i;
scanf("%d", &value[id]);
win[id]=i+1;
}
dfs(1);
if(value[2]>value[3]){
ans=win[3];
}
else{
ans=win[2];
}
printf("%d", ans);
return 0;
}
P4913 【深基16.例3】二叉树深度
#include<bits/stdc++.h>
using namespace std;
int n, a[1000010][3], l, r, ans;
void dfs(int curdeep, int x)
{
if(a[x][1]==0 && a[x][2]==0){ //无左子树和右子树
ans=max(ans, curdeep);
return;
}
if(a[x][1]!=0){ //有左子树
dfs(curdeep+1, a[x][1]);
}
if(a[x][2]!=0){ //有右子树
dfs(curdeep+1, a[x][2]);
}
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
scanf("%d %d", &l, &r);
a[i][1]=l;
a[i][2]=r;
}
dfs(1, 1);
printf("%d", ans);
return 0;
}
P5076 【深基16.例7】普通二叉树(简化版)
#include <bits/stdc++.h>
using namespace std;
int q, x, opt, n, site, a[10001], b[10001];
int main()
{
scanf("%d", &q);
while(q--){
scanf("%d", &opt);
scanf("%d", &x);
if(opt==1){ //查询x的排名, 坑点:不用管x在不在里面, 只用找出有多少个比x小即可
int cnt=0;
for(int i=1; i<=n; ++i){
if(a[i]<x){
cnt++;
}
}
printf("%d\n", cnt+1);
}
else if(opt==2){ //查询排名为x的数, 坑点:不用去重
printf("%d\n", a[x]);
}
else if(opt==3){ //求x的前驱,未找到前驱则输出-2147483647
//坑点:不用管x在不在里面, 只用找到比x小的数里的最大值
int pre=-2147483647;
for(int i=1; i<=n; ++i){
if(a[i]<x){
pre=max(pre, a[i]);
}
else{
break;
}
}
printf("%d\n", pre);
}
else if(opt==4){ //求x的后继,若未找到后继则输出 2147483647
//坑点:不用管x在不在里面, 只用找到比x大的数里的最小值
bool flag=false;
for(int i=1; i<=n; ++i){
if(a[i]>x){
flag=true;
printf("%d\n", a[i]);
break;
}
}
if(!flag){ //没有后继
printf("2147483647\n");
}
}
else if(opt==5){ //插入一个数 x
//找到在哪个位置插入, 然后插入
if(n==0){ //插入第一个数时
n++;
a[n]=x;
}
else if(x<=a[1]){ //在开头插入
for(int i=n; i>=1; --i){
a[i+1]=a[i];
}
a[1]=x;
n++;
}
else if(x>=a[n]){ //在末尾插入
n++;
a[n]=x;
}
else{ //在中间插入
for(int i=1; i<=n-1; ++i){
if(x>=a[i] && x<=a[i+1]){
site=i+1;
}
}
for(int i=n; i>=site; --i){
a[i+1]=a[i];
}
a[site]=x;
n++;
}
}
}
return 0;
}
二叉树的三种遍历方法
P1030 [NOIP2001 普及组] 求先序排列
#include <bits/stdc++.h>
using namespace std;
string in_order, post_order;
int len;
//中序 从l1到r1,后序从l2到r2
void dfs(int l1, int r1, int l2, int r2)
{
int flag;
//剪枝,已经没有节点了,树为空
if(l1>r1 || l2>r2) return;
//后序遍历最后一个肯定是根节点
char root=post_order[r2];
//输出根结点
cout << root;
//在中根遍历序列中找到根结点所在的位置,即可将结点分为左子树和右子树
for(int i=l1; i<=r1; ++i){
if(in_order[i]==root){ //如果等于根
//flag记录的是中序遍历序列中,根结点的位置
//在中根序列中,l1到flag-1的节点都属于左子树,
//flag+1到r1的节点都属于右子树
flag=i;
break;
}
}
//现在问题的关键是如何找出新的左子树的后序遍历序列、新的右子树的后续遍历序列
//原来的后续遍历序列前面部分是左子树的后续序列,紧接着是右子树的后续序列,最后是跟结点
//所以需要知道左子树有多少个结点。
int left_node=flag-l1;
//左子树有left_node个结点,则在后续遍历序列中,左子树的范围是从l2到l2+left_node-1
//紧接着就是右子树,则在后续遍历序列中,右子树的范围是从l2+left_node到r2-1
if(l1<flag) //如果左边节点小于根结点的编号,说明有左子树,递归左子树
dfs(l1, flag-1, l2, l2+left_node-1);
if(flag<r1) //如果右边节点大于根结点的编号,说明有右子树,递归右子树
dfs(flag+1, r1, l2+left_node, r2-1);
}
int main()
{
cin >> in_order >> post_order;
len=in_order.length(); //获取结点的数量
//中序从0到len-1, 后序从0到len-1
dfs(0, len-1, 0, len-1);
return 0;
}
P1827 [USACO3.4]美国血统 American Heritage
先建树、后遍历
#include <bits/stdc++.h>
using namespace std;
char first;
int length;
string preorder, inorder;
//a[1]['C']='B', 表示'C'的左儿子是'B'
//a[2]['C']='G', 表示'C'的右儿子是'G'
map<char, char> a[3];
//s1 e1表示中序遍历序列的起终点下标
//s2 e2表示前序遍历序列的起终点下标
//对树的中序遍历、先序遍历进行递归, 找出每个结点的左右儿子
void dfs(int s1, int e1, int s2, int e2)
{
int rootid, leftnum=0;
if(s1==e1 || s2==e2){ //*表示为空
a[1][inorder[s1]]=a[2][inorder[s1]]='*';
a[1][preorder[s2]]=a[2][preorder[s2]]='*';
return;
}
//找到根
char root=preorder[s2];
//在中序遍历中找到根的下标
for(int i=s1; i<=e1; ++i){
leftnum++; //左子树的结点数(包含root)
if(inorder[i]==root){
rootid=i;
break;
}
}
//root根有左儿子
if(rootid>s1){
//记录左儿子
a[1][root]=preorder[s2+1];
//对左子树的中序遍历、先序遍历进行递归, 找出每个结点的左右儿子
dfs(s1, rootid-1, s2+1, s2+leftnum-1);
}
else{
a[1][root]='*';
}
//root根有右儿子
if(rootid<e1){
//记录右儿子
a[2][root]=preorder[s2+leftnum];
//对右子树的中序遍历、先序遍历进行递归, 找出每个结点的左右儿子
dfs(rootid+1, e1, s2+leftnum, e2);
}
else{
a[2][root]='*';
}
}
void postorder(char cur)
{
//没有左子树
if(a[1][cur]=='*'){ //*表示为空
//没有右子树
if(a[2][cur]=='*'){
printf("%c", cur);
}
else{ //有右子树
//对右子树进行后序遍历
postorder(a[2][cur]);
printf("%c", cur); //输出根节点
}
}
else{ //有左子树
//对左子树进行后序遍历
postorder(a[1][cur]);
//没有右子树
if(a[2][cur]=='*'){
printf("%c", cur); //输出根节点
}
else{ //有右子树
//对右子树进行后序遍历
postorder(a[2][cur]);
printf("%c", cur); //输出根节点
}
}
}
int main()
{
cin >> inorder >> preorder;
first=preorder[0]; //二叉树的根节点
length=inorder.length();
dfs(0, length-1, 0, length-1); //注意length-1
postorder(first); //递归求后序遍历
return 0;
}
P1364 医院设置
#include <bits/stdc++.h>
using namespace std;
int n, dis[110], cur, ans=2e9, u, v, w[110], a[110][4];
bool vis[110];
//id结点到医院的距离为x
void dfs(int id, int x)
{
//遍历id结点的左儿子、右儿子、父节点
for(int i=1; i<=3; ++i){
//如果有, 并且没被遍历过
if(a[id][i]!=0 && !vis[a[id][i]]){
//标记为遍历过
vis[a[id][i]]=true;
//更新最短距离
dis[a[id][i]]=x+1;
//深搜
dfs(a[id][i], x+1);
}
}
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
scanf("%d %d %d", &w[i], &u, &v);
a[i][1]=u; //i的左儿子
a[i][2]=v; //i的右儿子
a[u][3]=a[v][3]=i; //u、v的父节点
}
//枚举医院的所有可能
for(int i=1; i<=n; ++i){
cur=0;
memset(dis, 0, sizeof(dis));
memset(vis, 0, sizeof(vis));
//从医院出发, 去找其他n-1个点
vis[i]=true;
dfs(i, 0);
for(int j=1; j<=n; ++j){
cur+=w[j]*dis[j];
}
ans=min(ans, cur);
}
printf("%d", ans);
return 0;
}
P3884 [JLOI2009]二叉树问题
#include <bits/stdc++.h>
using namespace std;
int n, a[110][4], u, v, height=1, width, dis, num[110];
bool vis[110];
//找深度和宽度
void dfsheight(int id, int x)
{
for(int i=1; i<=2; ++i){
if(a[id][i]!=0){ //有儿子
height=max(height, x+1);
num[x+1]++; //第x+1层节点数加一
dfsheight(a[id][i], x+1);
}
}
}
//找距离
void dfsdis(int id, int x)
{
if(id==v){
dis=x;
printf("%d\n%d\n%d", height, width, dis);
exit(0);
}
for(int i=1; i<=3; ++i){
if(a[id][i]!=0 && !vis[a[id][i]]){ //有儿子, 并且没访问过
vis[a[id][i]]=true;
if(i==3){ //向上
dfsdis(a[id][i], x+2);
}
else{ //向下
dfsdis(a[id][i], x+1);
}
}
}
}
int main()
{
scanf("%d", &n);
for(int i=1; i<n; ++i){
scanf("%d %d", &u, &v);
if(a[u][1]==0){
a[u][1]=v; //u的左儿子是v
}
else{
a[u][2]=v; //u的右儿子是v
}
a[v][3]=u; //v的父节点是u
}
scanf("%d %d", &u, &v);
num[1]=1;
dfsheight(1, 1);
//找宽度
for(int i=1; i<=height; ++i){
width=max(width, num[i]);
}
//找距离
vis[u]=true; //标记
dfsdis(u, 0);
return 0;
}
P1229 遍历问题
#include <bits/stdc++.h>
using namespace std;
string s1, s2;
int length;
long long ans=1;
int main()
{
cin >> s1 >> s2;
reverse(s2.begin(), s2.end());
length=s1.length();
for(int i=0; i<length-1; ++i){
for(int j=j=0; j<length-1; ++j){
if(s1[i]==s2[j] && s1[i+1]==s2[j+1]){
ans*=2;
}
}
}
printf("%lld", ans);
return 0;
}