数据结构(第六章)

图上
图(上)
第一讲
一.什么是图
图其实是一种很抽象的东西,它可以表示很多东西,举个例子,就你的朋友圈而言,你所交的朋友就可以构成一个图,生活中有很多很多的例子,因此图是比较复杂的,它表示的是“多对多"的关系。
回到数据结构,图一般包含两个要素:
一组顶点:通常用V表示顶点集合;
一组边:通常用E表示边的集合;
边是顶点对:(v,w)属于E,其中圆括号表示无向边,类似于一个这样的东西,——,双行线。
有向边<v,w>表示v——>w,它是单行线。
我们不考虑重边和自回路,重边容易理解,自回路是什么呢?自回路其实就是一个由自己指出的边然后又指回自己,这就是自回路。
二.图的表示方法
作为程序员的我们,怎么用程序来表示一个图呢?
这里我们要引出一个新的概念叫邻接矩阵。
邻接矩阵其实就是存放关系的矩阵,有关系则为1,没有关系则为0;当然数值不一定用01表示,你可以用任何数。这里来放一个图就一目了然了
在这里插入图片描述
在图中,我们很清楚的看到,如果v1与v2有关系,则为1,没有关系就为0,然后图中的一条红线,红线经过的数字全为0,这是为什么呢,因为我们没有自回路,也就是没有自己指向自己的。而且以红线为轴,两边是对称的,因为我们是无向边,很好理解。
同时我们会发现既然是对称的,那么我们就没必要存放两个一样的东西,这样会浪费了一半的空间呀,与此同时,完善的方法就诞生了。
我们可以用一个长度为N(N+1)/2的一维数组存储,存储的顺序是按照从上至下,从左至右的,
那么i与j的关系如何呢,通过计算也就是规律,可得 数组的下标为(i*(i+1)/2+j),然后我们看它对应的数组元素的值,就可以判断有无关系了。
邻接矩阵的缺点:很明显,浪费空间,但是也是相对而言的,对于稀疏图,也就是顶点很多,边很少,显然很浪费空间也浪费时间,但是对于稠密图,特别是对于完全图(一个点和所有点都有关系),还是很合算的。
看完了邻接矩阵,看看邻接表吧
邻接表:G[N]为指针数组,对应矩阵每行一个链表,只存非0元素,也就是说只存有关系的。
在这里插入图片描述
大概就长这个样子吧!让我们想一想,它真的比邻接矩阵要好吗?
我们仔细分析,一个结点既要存指针,还要存有关系的下标,就占了两个域了,然后每一个结点都有,所以也无法避免重复的情况,因此它也是浪费空间的。但是,它很方便找出所有的相邻的点,比邻接矩阵还是要节约一些空间的,但也存在浪费。
第二讲
一.图的遍历
从图中某这一点出发访遍图中其余顶点,且每一个顶点仅被访问一次,这一过程叫做图的遍历。
对于图的遍历来说,为了避免陷入死循环,设计出两种遍历方案:DFS(深度优先搜索),BFS(广度优先搜索)。
二.深度优先搜索
具体思想:打个比方,比如我们需要在100间房子里找一把钥匙,深度优先搜索就是,从任意的一间房子开始,把该房间里的所有地方都搜遍了,才肯离开,然后在另一间房间继续按照此方法找,知道找到为止。
深度优先搜索其实就像似一棵树的先序遍历,它是从图上的某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直到所有顶点都被访问到。如果有些顶点没有访问到,则另选图中的一个未被访问的顶点作为起始点,重复上述过程直到所有顶点被访问到。
邻接矩阵

void DFS(MGraph G,int i)//递归算法
{
    int j;
    visit[i]=1;//标记为已访问
    printf("%c",G.data[i]);
    for(j=0;j<G.num;j++)
    {
        if(G.map[i][j]&&!visit[j])//相邻的点,未被访问,直接DFS
            DFS(G,j);
    }
}
void DFSL(MGraph G)//遍历操作
{
    int i;
    for(i=0;i<G.num;i++)
    {
        visit[i]=0;
    }
    for(i=0;i<G.num;i++)
    {
        if(!visit[i])
            DFS(G,i);
    }
}

邻接表

void DFS(MGraph G,int i)//递归算法
{
    MGraph *p;
    visit[i]=1;
    printf("%c",G->List[i].data);
    p=G->List[i].first;
    while(p)
    {
        if(!visit[p->ad])
        {
            DFS(G,p->ad);//访问邻接点
        }
        p=p->next
    }
}
void DFSL(MFGraph G)//遍历操作
{
    int i;
    for(i=0;i<G.num;i++)
    {
        visit[i]=0;
    }
    for(i=0;i<G.num;i++)
    {
        if(!visit[i])
        DFS(G,i);
    }
}

三.广度优先搜索
具体思想:还是以房间为例,广度优先搜索是先把家里钥匙可能掉的地方找一遍,一步一步扩大面积去找,扩大查找范围,知道找到为止。广度优先搜索就类似与树的层序遍历。
邻接矩阵

void BFS(MGraph G)
{
    int i,j;
    Queue Q;
    for(i=0;i<G.num;i++)//初始化全部没有访问
        visit[i]=0;
    InitQueue(&Q);
    for(i=0;i<G.num;i++)
    {
        if(!visit[i])//未访问进入
        {
            visit[i]=1;
            printf("%c",g->data[i]);
            EnQueue(&Q,i);
            while(!QueueEmpty(Q))
            {
                DeQueue(&Q,&i);
                for(j=0;j<G.num;j++)
                {
                    if(G.map[i][j]&&!visit[j])
                    {
                        visit[j]=1;
                        printf("%c",G->data[j]);
                        EmqQueue(%&Q,j);
                    }
                }
            }
        }
    }    
}

邻接表

void BFS(MGraph G)
{
    int i;
    MGraph *p;
    Queue Q;
    for(i=0;i<G.num;i++)
    {
        visit[i]=0;
    }
    InitQueue(&Q);
    for(i=0;i<G.num;i++)
    {
        if(!visit[i])
        {
            visit[i]=1;
            printf("%c",G->ad[i].data);
            EnQueue(&Q,i);
            while(!QueueEmpty(Q))
            {
                DeQueue(&Q,&i);
                p=G->ad[i].first;
                while(p)
                {
                    if(!visit[p->da])
                    {
                        visit[p->da]=1;
                        printf("%c",G->ad[p->da].data);
                        EnQueue(&Q,p->da);
                    }
                    p=p->next;
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值