因为蒟蒻太蒟蒻,所以代码臭又长~~~~~~
算法:DFS、Floyd
文章目录
前言
最近在刷数据结构,刷到了二叉树的题单,二叉树存储遍历访问等等操作仍是不熟。昨天晚上刷到一题,思路明确,但是因为数据给出的顺序问题,没有满分,所以今天重构代码。思想算法和昨天一样,仅仅是调整了数据输入的顺序,AC了本题。
一、题目
题目描述
如下图所示的一棵二叉树的深度、宽度及结点间距离分别为:
- 深度:4
- 宽度:4
- 结点 8 和 6 之间的距离:8
- 结点 7 和 6 之间的距离:3
其中宽度表示二叉树上同一层最多的结点个数,节点 u, v之间的距离表示从 u 到 v 的最短有向路径上向根节点的边数的两倍加上向叶节点的边数。
给定一颗以 1 号结点为根的二叉树,请求出其深度、宽度和两个指定节点 x, y之间的距离。
输入格式
输出格式
输入三行,每行一个整数,依次表示二叉树的深度、宽度和 x, yx,y 之间的距离。
输入输出样例
输入 #1
10 1 2 1 3 2 4 2 5 3 6 3 7 5 8 5 9 6 10 8 6输出 #1
4 4 8说明/提示
二、题目分析
1. 边权(距离)?
节点 u, v之间的距离表示从 u 到 v 的最短有向路径上向根节点的边数的两倍加上向叶节点的边数。
什么两倍什么边数,都弄晕了QAQ
其实就是父亲到儿子距离为1,儿子到父亲距离为2.......
2. 基本思路
(1)深度?
遍历树节点,儿子深度=父亲深度+1,并用ans_depth记录最深的深度即可。
(2)宽度?
用数组width[n]记录深度为n的节点数,设树节点深度为i,则探索该节点时width[i]++。遍历整棵树的节点即可。用ans_width记录某一深度最多的节点数即可。
(3)给定x,y节点的最短距离?
emmm,求图中任意两点的最小距离?Floyd算法即可~
3. 初步代码
根据以上三个问题的解决算法,可以得到以下的源代码。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<limits.h>
#include<stack>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
int dis[105][105];
int n;
int depth[105],width[105];
int main()
{
/************初始化阶段***************/
cin>>n;
n-=1;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
dis[i][j]=INF;
dis[j][i]=INF;
if(i==j)
{
dis[i][j]=0;
}
}
}
memset(depth,0,sizeof(depth));
memset(width,0,sizeof(width));
/**************读入数据**************/
depth[1]=1;
width[1]=1;
int ans_depth=1;
int ans_width=1;
int x,y;
for(int i=1;i<=n;++i)
{
cin>>x>>y;
depth[y]=1+depth[x];
ans_depth=ans_depth>depth[y]?ans_depth:depth[y];
width[depth[y]]++;
ans_width=ans_width>width[depth[y]]?ans_width:width[depth[y]];
dis[x][y]=1;
dis[y][x]=2;
}
/*********Floyd求任意两点最短路径*****/
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(dis[i][k]+dis[k][j]<dis[i][j])
{
dis[i][j]=dis[i][k]+dis[k][j];
}
/*************答案输出部分***********/
cin>>x>>y;
cout<<ans_depth<<endl<<ans_width<<endl<<dis[x][y];
return 0;
}
//默认是按照从上往下从左往右的顺序读入所以只有73分,需要存树然后dfs
是不是思路清晰呢?然而没有AC,仅得到了73分。
这是为什么呢?
三、 代码改进
1. 代码错误分析
随然没有通过全部测试点,但也并没有全部WA,可见基本算法思路还是正确的。问题在哪呢?
上述代码,默认为读入的数据符合以下规则:
①读入u,v则u是父亲节点,v是儿子节点(然而题目只说明两个点相连)
②读入的顺序是从整棵树从上往下,从左往右(事实上,如果只是给出两个节点相连,可以给出任意满足整体相连规则的顺序)
也就是说,如果只是顺序地看输入的u,v,那么这棵树的模样,还是不能轻易构造出来的!
2. 代码修改思路
既然是这颗树建的不明白不清楚,那么我就老老实实把前期工作做好——把树建好。
最初的切入点:
(1)建树?
既然给的u,v顺序可以是打乱的,那我就先把u,v连接的这些信息一条条记录下来。
然后从根节点1出发,一步步找到孩子节点,再找孩子结点的孩子节点......
/******************************/
struct tp1{
int from,to;
};
tp1 data[105];
bool dt[105];
struct TreeNode{
int lchild,rchild;
bool flag;
};
TreeNode node[105];
/******************************/
nd.push(1);//根节点是1
while(nd.size()!=0)
{
int fa=nd.front();
for(int i=1;i<n;++i)
{
if(!dt[i])//如果该组数据没用过
{
int child=-1;
if(data[i].from==fa)
{
child=data[i].to;
}
if(data[i].to==fa)
{
child=data[i].from;
}
//看看谁是父亲
if(child!=-1)
{
dt[i]=true;//该组数据使用过了
if(node[fa].flag==true)//如果存在左子树,就添加右子树
{
node[fa].rchild=child;
}
else//否则添加左子树
{
node[fa].lchild=child;
node[fa].flag=true;
}
nd.push(child);
}
}
}
nd.pop();//该结点已经寻找完儿子节点了
}
/******************************/
(2) Floyd的距离矩阵?
树都已经建立好了,从根节点1开始,深搜遍历,就好啦~~(我封装成了一个函数)
void makeTree(int n)
{
int lchild=node[n].lchild,rchild=node[n].rchild;
if(lchild==INF&&rchild==INF)return;
if(lchild!=INF)
{
dis[n][lchild]=1;
dis[lchild][n]=2;
makeTree(lchild);
}
if(rchild!=INF)
{
dis[n][rchild]=1;
dis[rchild][n]=2;
makeTree(rchild);
}
}
(3)ans_width与ans_depth?
照之前所说的,遍历一边答案就出来了!同样还是用DFS(同样封装成一个函数)
void dfs(int n,int d)
{
depth[d]++;
ans_depth=ans_depth>d?ans_depth:d;
ans_width=ans_width>depth[d]?ans_width:depth[d];
int lchild=node[n].lchild,rchild=node[n].rchild;
if(lchild==INF&&rchild==INF)return;
if(lchild!=INF)
{
dfs(lchild,d+1);
}
if(rchild!=INF)
{
dfs(rchild,d+1);
}
}
3. 代码2.0
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<limits.h>
#include<stack>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
struct tp1{
int from,to;
};
tp1 data[105];
bool dt[105];
struct TreeNode{
int lchild,rchild;
bool flag;
};
TreeNode node[105];
queue<int> nd;
int n;
void initialize(void);//data,num,node,dis
int dis[105][105];
void makeTree(int);
int ans_depth,ans_width;
void dfs(int,int);
int depth[8];
int main()
{
IOS;
cin>>n;
initialize();
for(int i=1;i<n;++i)
{
cin>>data[i].from>>data[i].to;
}
nd.push(1);//根节点是1
while(nd.size()!=0)
{
int fa=nd.front();
for(int i=1;i<n;++i)
{
if(!dt[i])//如果该组数据没用过
{
int child=-1;
if(data[i].from==fa)
{
child=data[i].to;
}
if(data[i].to==fa)
{
child=data[i].from;
}
//看看谁是父亲
if(child!=-1)
{
dt[i]=true;//该组数据使用过了
if(node[fa].flag==true)//如果存在左子树,就添加给右子树
{
node[fa].rchild=child;
}
else//否则添加左子树
{
node[fa].lchild=child;
node[fa].flag=true;
}
nd.push(child);
}
}
}
nd.pop();//头结点已经寻找完子节点了
}
makeTree(1);
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(dis[i][k]+dis[k][j]<dis[i][j])
{
dis[i][j]=dis[i][k]+dis[k][j];
}
int x,y;
cin>>x>>y;
dfs(1,1);
cout<<ans_depth<<endl<<ans_width<<endl<<dis[x][y];
return 0;
}
void makeTree(int n)
{
int lchild=node[n].lchild,rchild=node[n].rchild;
if(lchild==INF&&rchild==INF)return;
if(lchild!=INF)
{
dis[n][lchild]=1;
dis[lchild][n]=2;
makeTree(lchild);
}
if(rchild!=INF)
{
dis[n][rchild]=1;
dis[rchild][n]=2;
makeTree(rchild);
}
}
void initialize()//dt,node,dis
{
memset(dt,false,sizeof(dt));
memset(depth,0,sizeof(depth));
for(int i=1;i<=n;++i)
{
node[i].flag=false;
node[i].lchild=INF;
node[i].rchild=INF;
for(int j=1;j<=n;++j)
{
dis[i][j]=INF;
}
}
}
void dfs(int n,int d)
{
depth[d]++;
ans_depth=ans_depth>d?ans_depth:d;
ans_width=ans_width>depth[d]?ans_width:depth[d];
int lchild=node[n].lchild,rchild=node[n].rchild;
if(lchild==INF&&rchild==INF)return;
if(lchild!=INF)
{
dfs(lchild,d+1);
}
if(rchild!=INF)
{
dfs(rchild,d+1);
}
}
总结
最初最直接的思路还是非常容易想到的,遇到的问题也非常直接——树的建立难题。千辛万苦将树建好,再运用蒟蒻的算法思路,就能成功AC本题啦!
这是蒟蒻的想法和冗杂操作,看到解法区还有什么ACL等等大犇算法,心生艳羡。
虽不能至,心向往之。与诸君共勉。