06-图1 列出连通集 (25 分)C语言

给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:

输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:

按照"{ v​1​​ v​2​​ ... v​k​​ }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

输入样例:

8 6
0 7
0 1
2 0
4 1
2 4
3 5

输出样例:

{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }

 打算分别写用邻接矩阵和邻接表实现。目前写完邻接矩阵的


关于:void(*VisitFunc)(VertexType v);  来自:https://zhidao.baidu.com/question/349140172.html 的其中一个回答:

void(*VisitFunc)(VertexType v);
1,按运算符优先级, VisitFunc 先与* 结合, 说明VisitFunc是个指针
2,然后(*VisitFunc) 与后面的 ()结合, 说明*VisitFunc 是个函数

总结: VisitFunc 是个指向函数的指针, 函数的返回类型是void, 参数的类型是VertexType

问:

void Visit( Vertex V )
 
{
 
    printf("正在访问顶点%d\n", V);
 
}
 
void DFS( MGraph Graph, Vertex S, void (*Visit)(Vertex) );
 
void BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) );

两个搜索函数都传入了visit函数的原因是?

老师的解答:这样使访问visit可以用户自己定义(比如,不一定是printf,也许需要把结点的值累加起来)
 


代码参考链接:https://www.cnblogs.com/masterchd/p/7801841.html(非常感谢!

感想:

  1. 输出格式{ v1 v2 v3 }费了些时间来调。还要求每输出完一组就换行,那个换行符要调
  2. 本次代码涉及的东西有点多,还有队列,正好趁此机会复习了一下。
  3. 写代码首先把大致的模块(用到的函数)写出来,再去补充和完善各个函数
  4. 其实中途有点瓶颈,但后来发现我绕过去了。就是ListComponents_DFS与DFS,一开始以为主函数用DFS,但后面发现“进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。”这个要求很难弄,但我没死磕,反而接着写连通的函数ListComponents_DFS,发现原来主函数用ListComponents_DFS即可。
  5. 那个Visited[]数组,需要设两个。因为DFS和BFS分别都要,如果只设一个的话,DFS用完,整个数组是全置了1的,除非DFS用完后置0,或者BFS判断时反向判断,都行~
  6. Graph=(MGraph)malloc(sizeof(struct GNode));与E=(Edge)malloc(sizeof(struct ENode)); 需要加这两个,不然在dev上运行会出错,原因未知...
  7. 如果BFS中也用递归会如何?答:(结果如图)那队列没有存在的意义了,for循环也没有。因为队列只会存一个元素,存了一个,出来一个,又存一个。所以,会和DFS一样

 

/*DFS*/
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex V) ){
    Vertex W;
    Visited_DFS[V] = 1;
    Visit(V);
    for(W = 0; W < Graph->Nv ; W++)
    {
        if(Graph->G[V][W] ==1 && !Visited_DFS[W])
        {
           DFS(Graph, W, Visit);
        }
    }
    return;
}
/*BFS*/
void BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ){
    Queue Q;
    Vertex V,W;
    Q=CreateQueue();//MaxSize
    Visit(S);
    Visited_BFS[S]=1;
    AddQ(Q,S);

    while(Q->front!=NULL){
        V=DeleteQ(Q);
        for(W=0; W<Graph->Nv; W++){
            if(Graph->G[V][W]==1 && !Visited_BFS[W]){
            	BFS(Graph, W, Visit); //若将下面三行换成此行,会如何?
//                Visit(W);
//                Visited_BFS[W]=1;
//                AddQ(Q,W);
            }
        }
    }
}

 


邻接矩阵源码:

/*邻接矩阵实现*/
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 10
#define MaxSize 10

typedef int WeightType;
typedef int Vertex;
typedef int ElementType;
int Visited_DFS[MaxVertexNum]; /* 顶点的访问标记 */
int Visited_BFS[MaxVertexNum];

struct GNode{
    int Nv;
    int Ne;
    WeightType G[MaxVertexNum][MaxVertexNum];
};
typedef struct GNode *PtrToGNode;
typedef PtrToGNode MGraph;

struct ENode{
    Vertex V1,V2;
    WeightType Weight;
};
typedef struct ENode *PtrToENode;
typedef PtrToENode Edge;

struct Node{
    ElementType Data;
    struct Node *Next;
};

struct QNode{
    struct Node *front;
    struct Node *rear;
};
typedef struct QNode *Queue;

MGraph CreateGraph(int VertexNum);
void InsertEdge(MGraph,Edge E);
MGraph BuildGraph();
void Visit( Vertex V );
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex) );
void BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) );
void ListComponents_DFS(MGraph Graph);
void ListComponents_BFS(MGraph Graph);
Queue CreateQueue();
void AddQ(Queue Q,Vertex S);
ElementType DeleteQ(Queue Q);

int main(void){
    MGraph Graph;
    Vertex V;
    Graph=BuildGraph();
    // DFS(Graph,V,Visit);
    ListComponents_DFS(Graph);
    // BFS(Graph,V,Visit);
    ListComponents_BFS(Graph);
    return 0;
}
/*MGraph初始化*/
MGraph CreateGraph(int VertexNum){
    Vertex V,W;
    MGraph Graph;
    //需加(MGraph)
    Graph=(MGraph)malloc(sizeof(struct GNode));
    Graph->Nv=VertexNum;
    Graph->Ne=0;

    for(V=0;V<Graph->Nv;V++)
     for(W=0;W<Graph->Nv;W++)
        Graph->G[V][W]=0;   
    
    return Graph;
}
/*MGraph插入边*/
void InsertEdge(MGraph Graph,Edge E){
    Graph->G[E->V1][E->V2]=E->Weight;
    Graph->G[E->V2][E->V1]=E->Weight;
}
/*完整建立MGraph*/
MGraph BuildGraph(){
    MGraph Graph;
    Edge E; Vertex V; int Nv, i;
    scanf("%d",&Nv);
    Graph=CreateGraph(Nv);
    scanf("%d",&(Graph->Ne));
    if(Graph->Ne != 0){
        //需加(Edge)
        E=(Edge)malloc(sizeof(struct ENode));
        for(i=0; i<Graph->Ne; i++){
            scanf("%d %d",&E->V1, &E->V2);
            E->Weight=1;
            InsertEdge(Graph, E);
        }
    }
    for(V=0; V<Graph->Nv; V++){
        Visited_BFS[V]=0;
        Visited_DFS[V]=0;
    }
    return Graph;
}
/*访问节点*/
void Visit( Vertex V ){
    printf(" %d", V); 
}
/*DFS连通*/
void ListComponents_DFS(MGraph Graph){
    Vertex i;
    for(i=0; i<Graph->Nv; i++){
        if(!Visited_DFS[i]){//节点i未被访问过
            printf("{");
            DFS(Graph, i, Visit);
            printf(" }");
            printf("\n");
        }
    }
}
/*BFS连通*/
void ListComponents_BFS(MGraph Graph){
    Vertex i;
    for(i=0; i<Graph->Nv; i++){
        if(!Visited_BFS[i]){
            printf("{");
            BFS(Graph, i, Visit);
            printf(" }");
            printf("\n");
        }
    }
//    printf("\n");
}
/*
进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。How??
按照"{v1v2...vk}"的格式,每行输出一个连通集。
*/
/*DFS*/
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex V) ){
    Vertex W;
    Visited_DFS[V] = 1;
    // printf("{");
    Visit(V);
    for(W = 0; W < Graph->Nv ; W++)
    {
        if(Graph->G[V][W] ==1 && !Visited_DFS[W])
        {
           DFS(Graph, W, Visit);
        }
    }
    // printf("}");
    return;
}
/*BFS*/
void BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ){
    Queue Q;
    Vertex V,W;
    Q=CreateQueue();//MaxSize
    Visit(S);
    Visited_BFS[S]=1;
    AddQ(Q,S);

    while(Q->front!=NULL){
        V=DeleteQ(Q);
        for(W=0; W<Graph->Nv; W++){
            if(Graph->G[V][W]==1 && !Visited_BFS[W]){
                Visit(W);
                Visited_BFS[W]=1;
                AddQ(Q,W);
            }
        }
    }
}
/*建队列,假定为空*/
Queue CreateQueue(){
    Queue Q;
    Q=(Queue)malloc(sizeof(struct QNode));
    Q->front=Q->rear=NULL;
    return Q;
}
/*进队列*/
void AddQ(Queue Q,Vertex S){
    struct Node *temp;
    temp=(struct Node*)malloc(sizeof(struct Node));
    temp->Data=S;
    temp->Next=NULL;
    if(Q->front==NULL){
        Q->front=temp;
        Q->rear=temp;
    }
    else{
        Q->rear->Next=temp;
        Q->rear=temp;
    }
    // return Q;
}
/*出队列*/
ElementType DeleteQ(Queue Q){
    struct Node *FrontCell;
    ElementType FrontElem;
    if(Q->front==NULL){
        return -1;
    }
    FrontCell=Q->front;
    if(Q->front==Q->rear)
        Q->front=Q->rear=NULL;
    else Q->front=Q->front->Next;
    FrontElem=FrontCell->Data;
    free(FrontCell);
    return FrontElem;
}

邻接表

实现思路:

  1. 要搞清楚Vertex 与 PtrToAdjVNode中AdjV的关系。
  2. 用邻接表写BFS时,遇到一个问题。题目要求:“进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。”从编号最小的顶点出发好说,在连通函数里用for循环遍历一遍就好了。但是后者,按递增顺序访问临界点,对邻接表来讲并不友好。因为邻接表的插入是利用G[V].FirstEdge的,而且与输入数据的顺序有关,所以插入之后,结点是随便排序的。本来想着能不能在BFS函数中进行调整,但是我没想到比较好的方法。简单粗暴就用遍历,但....太傻了,而且实现起来不是一般的麻烦,完全没必要。所以我觉得题目要求:“进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。”这个要求可能就已经限制我们要用邻接矩阵了。
  3. 邻接表的实现,我打算BFS那里是随机的,可以从编号最小出发,但不是按递增的顺序访问临界点(此处比个小心心~)

作业有点多,我先做作业了。代码写的差不多。先贴上来叭

/*用邻接表实现*/
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 10

typedef int WeightType;
typedef int Vertex;
typedef int ElementType;
int Visited_DFS[MaxVertexNum]; /* 顶点的访问标记 */
int Visited_BFS[MaxVertexNum];

typedef struct AdjVNode *PtrToAdjVNode;/*PtrToAdjVNode需要在上面,可能是因为要先声明*/
struct AdjVNode{
    Vertex AdjV;
    WeightType Weight;
    PtrToAdjVNode Next;//或者用struct AdjVNode *Next?
};


typedef struct VNode{
    PtrToAdjVNode FirstEdge;
}AdjList[MaxVertexNum];

struct GNode{
    int Nv;
    int Ne;
    AdjList G;
};
typedef struct GNode *PtrToGNode;
typedef PtrToGNode LGraph;

struct ENode{
    Vertex V1,V2;
    WeightType Weight;
};
typedef struct ENode *PtrToENode;
typedef PtrToENode Edge;

LGraph CreateGraph(int VertexNum);
void InsertEdge(LGraph Graph, Edge E);
LGraph BuildGraph();
void Visit( Vertex V );
void DFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) );
void BFS( LGraph Graph, Vertex S, void (*Visit)(Vertex) );

int main(void){
    LGraph Graph;
    Graph=BuildGraph();

    return 0;
}
/*建空图*/
LGraph CreateGraph(int VertexNum){
    Vertex V;
    LGraph Graph;
    Graph=(LGraph)malloc(sizeof(struct GNode));
    Graph->Ne=0;
    Graph->Nv=VertexNum;
    for(V=0; V<Graph->Nv; V++)
        Graph->G[V].FirstEdge=NULL;
    return Graph;
}
/*插入边*/
void InsertEdge(LGraph Graph, Edge E){
    PtrToAdjVNode NewNode1,NewNode2;
    /*插入边<V1,V2>*/
    NewNode1=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode1->AdjV=E->V2;
    NewNode1->Weight=E->Weight;
    NewNode1->Next=Graph->G[E->V1].FirstEdge;
    Graph->G[E->V1].FirstEdge=NewNode1;
    /*插入边<V2,V1>*/
    NewNode2=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode2->AdjV=E->V1;
    NewNode2->Weight=E->Weight;
    NewNode2->Next=Graph->G[E->V2].FirstEdge;
    Graph->G[E->V2].FirstEdge=NewNode2;
}
/*完整建立一个图*/
LGraph BuildGraph(){
    LGraph Graph;
    Edge E; Vertex V; int Nv, i;
    scanf("%d",&Nv);
    Graph=CreateGraph(Nv);
    scanf("%d",&Graph->Ne);
    if(Graph->Ne){
        E=(Edge)malloc(sizeof(struct ENode));
        for(i=0; i<Graph->Ne; i++){
            scanf("%d %d",&E->V1,&E->V2);
            E->Weight=1;
            InsertEdge(Graph,E);
        }
    }
    for(V=0; V<Graph->Nv; V++){
        Visited_BFS[V]=0;
        Visited_DFS[V]=0;
    }
    return Graph;
}
/*打印*/
void Visit( Vertex V ){
    printf(" %d",V);
}
/*DFS*/
void DFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) ){
    PtrToAdjVNode W;
    Visit(V);
    Visited_DFS[V]=1;
    for(W=Graph->G[V].FirstEdge; W; W=W->Next){
        if(!Visited_DFS[W->AdjV])
            DFS(Graph, W->AdjV, Visit);
    }
}
/*BFS 需重看,很巧妙的一个方法。没有用队列,数组就可以了。算拓展思维吧*/
void BFS( LGraph Graph, Vertex S, void (*Visit)(Vertex) ){
    int queue[1010];
    int l=0,r=0;
    queue[r++]=S;
    (*Visit)(S);
    Visited[S]=true;
    PtrToAdjVNode tmp;
    while(l!=r)
    {
        tmp=Graph->G[queue[l++]].FirstEdge;
        while(tmp)
        {
            Vertex pos=tmp->AdjV;
            if(!Visited[pos])
            {
                Visit(pos);
                Visited[pos]=true;
                queue[r++]=pos;
            }
            tmp=tmp->Next;
        }
    }
}

 

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值