一.图的表示
1.邻接表
一般用来表示稀疏图(边的条数|E|远远小于|V|^2)可以对其进行简单修改来支持许多其他的图的变种。邻接表的缺陷是无法快速的的判断一条边(u,v)是否是图中的一条边,唯一的办法是在邻接表[u]里面搜索节点v。邻接表表示法的储存空间需求为Q(V+E)。
2.邻接矩阵
一般用来表示稠密矩阵(|E|接近|V|^2),表示法简单,可以比较快的判断一条边(u,v)是否属于图,储存空间需求为Q(V^2)。在图的规模比较小的时候一般倾向于邻接矩阵表示法。
二.图的遍历
1.广度优先遍历(BFS)
(1)给定一个图(V,E)和一个节点S后,BFS可以用来找到从S可到达的所有节点,以及S到每个节点的最小距离(最少的边数),并生成一颗广度优先搜索树。BFS需要在找到距离S最小距离为K的所有节点后,才开始找距离S为K+1的节点。
(2)算法实现
int BFS(int ** G,int n,int s) //G用于接收邻接矩阵,n为节点数,s为遍历初始点
{
int *YBL=new int [n]; //YBL数组用于标记遍历过的点,若Vi节点遍历过YBL[i]=1,否则YBL[i]=0;
for(int i=0;i<n;i++)
YBL[i]=0;
YBL[s]=1; //给初始节点已经遍历标记
int **JG=new int*[n]; //JG用于保存遍历结果
for(int i=0;i<n;i++) //JG[i][0]储存节点下标,JG[i][1]储存节点距离初始遍历点的距离
JG[i]=new int[3]; //JG[i][2]生成遍历树中节点的父节点
JG[0][0]=s,JG[0][1]=0,JG[0][2]=-1; //初始节点S加入到JG,S距离初始节点0,S的父节点-1(不存在)
int hig=1; //hig等于当前JB中保存节点个数
for(int i=0;i<hig;i++) //对JB中保存的节点从前到后遍历,一次找到与他们相连接的节点,并加入JB
{
for(int j=0;j<n;j++)
{
if((*((int*)G+n*JG[i][0]+j))&&(YBL[j]==0))//若节点JG[i][0]与节点j连通,且J节点未被遍历
{
JG[hig][0]=j; //把j节点加入JG中
JG[hig][1]=JG[i][1]+1; //j节点距初始节点距离等于JG[i][0]节点到初始节点距离加1
JG[hig][2]=JG[i][0]; //j节点的父节点为JG[i][0]
hig=hig+1; //JB中保存的节点加1
YBL[j]=1; //给j节点做已遍历标记
}
}
YBL[i]=1;
}
for(int i=0;i<n;i++) //输出结果
{
cout<<"V"<<JG[i][0]<<"距离节点V"<<s<<":"<<JG[i][1]<<" 广度优先搜索树父节点是:V"<<JG[i][2]<<endl;
}
delete YBL;
delete [] JG;
return 0;
}
(3)举例:
int main()
{
int G[7][7] = {{0,1,1,0,0,0,0}, //上图
{1,0,0,1,0,0,0},
{1,0,0,0,1,0,0},
{0,1,0,0,0,1,1},
{0,0,1,0,0,1,1},
{0,0,0,1,1,0,1},
{0,0,0,1,1,1,0}};
BFS((int **)G,7,1);
return 0;
}
结果:
V-1表示在生成的广度遍历树中V1没有父节点,既V1是根节点
2.深度优先遍历(DFS)
(1)优先深度优先总是对最近才发现的节点V进行探索,直到该节点的所有出发边都被发现为止。一旦节点V的所有出发点都被发现,搜索则”回溯“到V的前驱结点(V是经过该节点才被发现的)
(2)算法实现(DFS)
int DFS(int **G,int n,int s) //G用于接收邻接矩阵,n为节点数,s为遍历初始点</span></span>
{
int F_DFS(int **G,int n,int*YBL,int *JG);
int *YBL=new int[n]; //用于标记已遍历过的节点数
for(int i=0;i<n;i++)
YBL[i]=0;
YBL[s]=1;
int *JG=new int [n];
for(int i=0;i<n;i++)
JG[i]=0;
JG[0]=s; //用来按顺序保存遍历得点
F_DFS(G,n,YBL,JG);
return 0;
}
int F_DFS(int **G,int n,int*YBL,int *JG)
{
static int len=0; //用来保存遍历的个数
static int temp=0; //用来表示当前检查(遍历)的点
if(len>=n-1)
{
return 0;
}
else
{
int flag=1;
for(int i=0;i<n;i++)
{ if(((*((int *)G+JG[temp]*n+i))==1)&&(YBL[i]==0))
{
flag=0;
cout<<JG[temp]<<"---"<<i<<endl;
YBL[i]=1;
len=len+1;
JG[len]=i;
if(temp==0) //重要,当回溯到起始节点时,若还有下一个点可遍历,会被存在JG[len]中,
temp=len; //下一次遍历是用JG[len]做起点,而不是Len[1];
temp=temp+1; //但这样有一个问题当再次回溯到JG[len-1]时,因为temp=len-1会对JG[len-1...0]节点再次检查
//但是不影响结果,因为与JG[len-1...0]节点相连的点已被标记为遍历过,所以不会再次遍历
if(len==n-1) //若不用对结果做记录,出现一个节点就输出到屏幕一个,
for(int i=0;i<n;i++) //既用栈的思想来处理,就可以解决这个问题
cout<<JG[i]<<" ";
F_DFS(G,n,YBL,JG);
}
}
if(flag==1)
{
temp=temp-1;
F_DFS(G,n,YBL,JG);
}
}
}
(3)举例
int main()
{
int G[7][7] = {{0,1,1,0,0,0,0}, //上图
{1,0,0,1,0,0,0},
{1,0,0,0,1,0,0},
{0,1,0,0,0,0,1},
{0,0,1,0,0,1,0},
{0,0,0,0,1,0,0},
{0,0,0,1,0,0,0}};
DFS((int **)G,7,1);
return 0;
}
结果: