算法(图的遍历)

初识图
:图简单来说,是由一些小圆点(称为顶点)和链接这些小圆点的直线(称为边)组成的,是由N个顶点,M条边所组成的集合。

图又分为有向图和无向图,如果给图的每条边规定一个方向,那么得到的图也被称为有向图,其边也被称为有向边,在有向图中,与一个点相关联的边又可以被分为出边入边,而与一条边有关的顶点也可以分为始点终点

时间戳:按照图中顶点访问顺序,我们把这些访问顺序称之为时间戳。

存在一种方法,可以用一种一一对应的数组来表示这些点和边的关系,我们通常使用:图的邻接矩阵存储法
对于无向图来说,我们可以用我们想要的这种一一对应的关系,cur代表的是当前的顶点的坐标,i则表示通过搜索找到的下一个点的坐标,但是由于cur和i是不会相等的,所以每一个点都代表着一个对应关系。

那么遍历的方式,我们在之前已经学过了广度优先搜索和深度优先搜索,现在来复习一下:
深度优先搜索:首先从一个未走到过的顶点作为起始顶点,沿着这个顶点的边去尝试访问其他未走到过的顶点,每走到一个顶点,就沿着这个顶点边去走其他未走到过的顶点,直到到达最末端,然后返回,尝试从别的边来访问其他的点,直到又到达另外一个末端,一次往复,直到整个图都没有了可走的边为止。

显然,深度优先搜索是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止.
代码实现如下:
dfs主体的部分

void dfs(int cur)
{
    printf("%d",cur);
    sum++;//每访问到一个点就sum++,用来判断边界
    if(sum==n)
    {
        return ;//边界的判断
    }
    for(i=1;i<=n;i++)//从1号顶点到n号顶点依次尝试,看哪些顶点与cur有边相连
    {
        if(e[cur][i]==1 &&book[cur][i]==0)//有边相连而且没有被访问过
        {
            book[cur][i]=1;//标记已经走过
            dfs(i);//开始下一次的dfs遍历
        }
    }
    return ;
}

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define LIMIT 999999999
//我们用LIMIT这个标志量来表示不可以通行,不可以到达
int book[1005];
int e[1005][1005];
int sum,n;
void dfs(int cur)//cur是当前所在点的编号
{
    printf("%d ",cur);
    sum++;//每访问到一个点就sum++
    if(sum==n)//当所有的点都访问过了就退出
    {
        return ;
    }
    for(int i=1;i<=n;i++)//从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点有边相连
    {
        if(e[cur][i]==1  && book[i]==0  )  //可以通行,而且这个点没被访问过
        {
            book[i]=1;
            dfs(i);//在这个点的基础上继续找下一个点,注意这时候的n是多少?
        }
    }
    return ;
}

int main()
{
    int i,j,m,a,b;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)//表示顶点的坐标
            {
                e[i][j]=0;
            }
            else
            {
                e[i][j]=LIMIT;
            }
        }
    }
    //接下来读入顶点周围的边
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        e[a][b]=1;
        e[b][a]=1;
        //无向图,需要矩阵对称,表示既可以从a到b,也可以从a到b
    }
    book[1]=1;
    dfs(1);
    return 0;
}

广度优先搜索:
其思想为:首先以一个未被访问过的顶点作为初始顶点,访问其所有相邻的顶点,然后再对每一个相邻的顶点,在访问他们未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define LIMIT 999999999
#define MAXLEN 101*101
int main()
{
    int i,j,a,b,m,n,cur,e[101][101],book[101]={0};
    int que[MAXLEN],head,tail;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
            {
                e[i][j]=0;
            }
            else
            {
                e[i][j]=LIMIT;
            }
        }
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        e[a][b]=1;
        e[b][a]=1;
    }
    //BFS
    head=1;
    tail=1;
    que[tail]=1;
    tail++;
    book[1]=1;
    while(head<tail)//当队列不为空的时候就循环
    {
        cur=que[head];//当前正在访问的顶点
        for(i=1;i<=n;i++)
        {
            if(e[cur][i]==1 && book[i]==0)
            {
                //如果通过这条边可以访问到当前的结点,而且没有被访问,那么就入队
                que[tail]=i;
                tail++;
                book[i]=1;//标记已经走过
            }
            if(tail>n)//边界的条件
            {
                break;
            }
        }
        head++;//当一个点扩展了之后,将其出队,才能进行下一次的扩队
    }
    for(i=1;i<tail;i++)
    {
        printf("%d ",que[i]);
    }
    return 0;
}

上述两种方法得到的遍历顺序叫做图的生成树。
这上述的基础上,我们添加一个路径长度的元素,用来表示每一个顶点之间的距离,那么就用来计算更为复杂的路径最小长度的问题。
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define LIMIT 999999999
#define MAXLEN 101*101

int book[101],e[101][101],n,min=LIMIT;

void dfs(int cur,int dis)//cur值得是现在是第几号城市,dis代表走过的路径长度
{
    int j;
    //如果发现当前走过的路径已经大于了之前所走过的最大的路径,那么就可以不用继续下去了,直接return
    if(dis>min)
    {
        return ;
    }
    if(cur==n)//判断是否到达边界条件
    {
        if(dis<min)//打擂
        {
            min=dis;
            return ;//返回最近一次调用dfs函数的位置
        }
    }
    for(j=1;j<=n;j++)//从1号城市到n号城市依次尝试
    {
        if(e[cur][j]!=LIMIT && book[j]==0)
        {
            book[j]=1;//标记当前这个点已经被走过了
            dfs(j,dis+e[cur][j]);
            book[j]=0;//解除被走过的痕迹,防止路被"堵住"
        }
    }
    return ;
}

int main()
{
    int i,j,m,a,b,c;//c是新增的变量,用于读入路径长度
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
            {
                e[i][j]=0;
            }
            else
            {
                e[i][j]=LIMIT;
            }
        }
    }
    for(j=1;j<=m;j++)
    {
        scanf("%d%d%d",&a,&b,&c);
        e[a][b]=c;//有向图,只能由a到b
    }
    book[1]=1;//标记1号城市已经在路径中
    dfs(1,0);//1表示当前城市的编号,0代表当前已经走过的路径
    printf("%d",min);
    return 0;
}

同样的,假如是我们引入一个step变量,用于表示需要经过边的转换次数,可以用BFS方法来解决。
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define LIMIT 999999999
#define MAXLEN 1000*1000+5
#define LEN 1000+5
struct note
{
    int x;//城市编号
    int step;//代表步数
};

struct note que[MAXLEN];
int e[LEN][LEN]={0},book[LEN]={0};

int main()
{
    int head,tail;
    int i,j,a,b,n,m,cur,flag=0,start,end;
    scanf("%d%d%d%d",&n,&m,&start,&end);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
            {
                e[i][j]=0;
            }
            else
            {
                e[i][j]=LIMIT;
            }
        }
    }
    for(i=1;i<=m;i++)//表示有几条边,现在读入
    {
        scanf("%d%d",&a,&b);
        e[a][b]=1;
        e[b][a]=1;
    }
    head=1;
    tail=1;
    //从start站开始,那么就将其入队
    que[tail].x=start;
    que[tail].step=0;
    book[1]=start;
    tail++;
    while(head<tail)
    {
        cur=que[head].x;
        for(j=1;j<=n;j++)
        {
            if(e[cur][j]!=LIMIT && book[j]==0)
            {
                book[j]=1;
                que[tail].x=j;
                que[tail].step=que[head].step+1;
                tail++;
            }
            if(que[tail].x==end)
            {
                flag=1;
                break;
            }
        }
        if(flag==1)
        {
            break;
        }
        head++;
    }
    printf("%d",que[tail-1].step);
    return 0;
}

以上这几种代码实现方式都是基于图的邻接矩阵存储法来实现的,当然也有很多种其他的方法,比如说邻接表法,这些以后再来接触。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值