第四章——图(c语言描述)

无向图邻接矩阵以及邻接表的构建,深度优先搜索以及广度优先搜索。

如图所示,左为邻接表,右边为图的表示。

 先写邻接矩阵,相对邻接表没那么复杂.

int G[MAXSIZE][MAXSIZE];//全局变量初始化就都是0
int N;//结点数
int M;//边数

直接定义为全局变量就行,用起来方便.

最简单的邻接矩阵就是这样的二维矩阵了,如果想要给图的每个结点添加数据信息,那么就改成三维矩阵,第三个维度存放结点的数据信息.

如果两结点之间有边的话,例如1结点和2结点直接有边(无向图),那么代码层面这样写:

//无向图就两个都要设置为1
G[1][2]=G[2][1]=1;
//如果是有向图,那么只要一个
//例如1->2
G[1][2]=1;

通常图的遍历是深度优先搜索和广度优先搜索,所以这里再添加一个数组,表示是否过访问结点

int visit[MAXSIZE];

不管是邻接表还是邻接矩阵,深搜广搜的思想都是一样的。

深度优先搜索重点理解它的走法:不撞南墙不回头.

 所以,访问顺序是:0 1 2 3 4

广度优先搜索则是看它周围是否有边,周围有边就走,将它的辐射范围走完就对下一个结点的辐射范围进行访问.

 所以,访问顺序是:0 1 3 2 4

 深度优先搜索:递归

//N是该图的结点数
void dfs(int n){
	//输出该邻接矩阵 深度优先搜索
    if(visit[n]==0){
        //如果该点未走过
        visit[n]=1;//标记
    	printf("%d\n",n);
    }
    int j;
    for(j=0;j<N;j++){
    	if(visit[j]==0&&G[n][j]==1){
        	//如果该点没访问过并且相邻之间有边,可以访问
            dfs(j);//递归
        }
    }
}

广度优先搜索:利用队列,将结点辐射范围的边入队.

void bfs(int n){
	ListQueue Q;
    LoadListQueue(&Q);
    PushListQueue(&Q,n);//起点入队
    while(isEmpty(&Q)!=1){
    	//队列不为空
        int v=popListQueue(&Q),i;
        if(visit[v]==0)
        printf("%d\n",v);
        visit[v]=1;//标记
        for(i=0;i<N;i++){
        	if(visit[i]==0&&G[v][i]==1){
            	PushListQueue(&Q,i);//如果是辐射范围的结点并且没访问过则入队
            }
        }
    }
}

 

 邻接矩阵:(队列的结构细节可以忽略,只是起到辅助作用,如果是用其他语言如c++/java可以直接调用stl库或者集合)

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 256
int G[MAXSIZE][MAXSIZE];//邻接矩阵无向图
int N;//结点数
int M;//边数
int visit[MAXSIZE];
typedef struct{
	int data[MAXSIZE];    
    int head;//头结点
    int tail;//尾巴结点  
}ListQueue;
void LoadListQueue(ListQueue *q){
 	q->head=q->tail=0;
}
void PushListQueue(ListQueue *q,int value){
    int t=q->tail;
    q->tail=(q->tail+1)%MAXSIZE;//为了循环
 	q->data[t]=value;      
}
int popListQueue(ListQueue *q){
    int t=q->head;
    q->head=(q->head+1)%MAXSIZE;

    return q->data[t];   
}
int isEmpty(ListQueue *q){
    if(q->head==q->tail){
    return 1;
    }
    return 0;
}
void LoadGraph(){
	//创建一个无向图
    int x1,x2,i;
    printf("请分别输入图的顶点数以及边数\n");
    scanf("%d %d",&N,&M);
    printf("请输入图的边\n");
    for(i=0;i<M;i++){
    	scanf("%d%d",&x1,&x2);
    	G[x1][x2]=G[x2][x1]=1;//给边赋值1	
    }
    printf("初始化成功!\n");
}
void dfs(int n){
	//输出该邻接矩阵 深度优先搜索
    if(visit[n]==0){
        visit[n]=1;//标记
    	printf("%d\n",n);
    }
    int j;
    for(j=0;j<N;j++){
    	if(visit[j]==0&&G[n][j]==1){
        	//如果该点没访问过并且相邻之间有边,可以访问
            dfs(j);
        }
    }
}
void bfs(int n){
	ListQueue Q;
    LoadListQueue(&Q);
    PushListQueue(&Q,n);//起点入队
    while(isEmpty(&Q)!=1){
    	//队列不为空,BFS
        int v=popListQueue(&Q),i;
        if(visit[v]==0)
        printf("%d\n",v);
        visit[v]=1;//标记
        for(i=0;i<N;i++){
        	if(visit[i]==0&&G[v][i]==1){
            	PushListQueue(&Q,i);
            }
        }
    }
}
/*
输入实例 
5 6
0 1
0 3
1 4
1 2
2 4
2 3
*/
int main(){   
	LoadGraph();//创建无向图 矩阵
	printf("邻接矩阵\n");
	printf("深度优先搜索\n");
    dfs(0);//从顶点0开始深度优先搜索
    int i;//重设一下visit 以免影响广度优先搜索 
	for(i=0;i<N;i++){
		visit[i]=0;
	}
    printf("广度优先搜索:\n");
	bfs(0);

	return 0;
}

输出结果: 

 

 

邻接表的结构相对邻接矩阵复杂多了,主要分为两大块:表头链表和边表

 

邻接表结构:

typedef struct Anode{
	int aindex;//表示当前顶点的位置
	struct Anode *next;//指向下一边的指针	 
}AdjNode;//表头链表,主要作用储存与它相连的边 

typedef struct Vnode{
	int data;//结点存储的信息 
	AdjNode *head;//它的边链表 
}AdjList;//边表,将表头链表收进来 

typedef struct {
	AdjList Graph[MAXSIZE];
	int vexnum;//图的当前结点数
	int arcnum;//图的当前边数 
}AGraph;//边表数组同时增加结点数和边数

邻接表的结构只要搞定了深搜和广搜都是与邻接矩阵一样的。

一个小改变的地方是判断该点与某点是否有边的方式改了.

例如判断结点1和结点2是否有边:
 

int isLink(AdjList L,int n){
	//判断L结点与n结点是否有边 
	AdjNode *t=L.head;
	int flag=0;
	while(t!=NULL){
		if(t->aindex==n){
			flag=1;
			break;
		}
		t=t->next;
	}
	return flag;
}

完整代码:(队列的结构细节可以忽略,只是起到辅助作用,如果是用其他语言如c++/java可以直接调用stl库或者集合)

//邻接表 
//采用邻接表储存,实现无向图的深度优先遍历
//采用邻接表储存,实现无向图的广度优先遍历
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 256
int visit[MAXSIZE];
typedef struct{
	int data[MAXSIZE];    
    int head;//头结点
    int tail;//尾巴结点
    
}ListQueue;
void LoadListQueue(ListQueue *q){
 	q->head=q->tail=0;
}
void PushListQueue(ListQueue *q,int value){
    int t=q->tail;
    q->tail=(q->tail+1)%MAXSIZE;//为了循环
 	q->data[t]=value;      
}
int popListQueue(ListQueue *q){
    int t=q->head;
    q->head=(q->head+1)%MAXSIZE;

    return q->data[t];   
}
int isEmpty(ListQueue *q){
    if(q->head==q->tail){
    return 1;
    }
    return 0;
}
typedef struct Anode{
	int aindex;//表示当前顶点的位置
	struct Anode *next;//指向下一边的指针	 
}AdjNode;//表头,主要作用储存与它相连的边 
typedef struct Vnode{
	int data;//结点存储的信息 
	AdjNode *head;//它的边链表 
}AdjList;//边表 
typedef struct {
	AdjList Graph[MAXSIZE];
	int vexnum;//图的当前结点数
	int arcnum;//图的当前边数 
}AGraph;
AdjNode* CreateL(int index){
	//创建边链表,输入-1表示结束
	AdjNode *t,*tail,*head;
	head=tail=NULL;	
	int x=0;
	printf("请输入%d结点的边: 输入-1结束\n",index);
	 while(x!=-1){
	 	//尾插法 	 	
	 	scanf("%d",&x);
	 	if(x==-1)break;
	 	t=(AdjNode*)malloc(sizeof(AdjNode));
	 	t->aindex=x;
	 	t->next=NULL;
	 	if(head==NULL){
	 		head=t;
		 }
		 else {		 
		 	tail->next=t;
		 }		 
		 tail=t;
	 }
	 return head;
}
void CreateG(AGraph &G){
	int i,N,M;
	//输入总结点数以及总边数
	printf("输入总结点数以及总边数\n");
	scanf("%d %d",&N,&M);
	G.vexnum=N;
	G.arcnum=M;
	//输入顶点之间的关系,下标从0开始  
	for(i=0;i<N;i++){
		//输入顶点存储的数据
		printf("输入顶点%d存储的数据\n",i);
		scanf("%d",&G.Graph[i].data); 
		G.Graph[i].head=CreateL(i);//建立链表		-----类型是指针类型吗? 
	}
}
int isLink(AdjList L,int n){
	//判断L结点与n结点是否有边 
	AdjNode *t=L.head;
	int flag=0;
	while(t!=NULL){
		if(t->aindex==n){
			flag=1;
			break;
		}
		t=t->next;
	}
	return flag;
}
void printG(AGraph G){
	int Nlen=G.vexnum;//结点数
	int Mlen=G.arcnum;//边数
	int i,j;
	for(i=0;i<Nlen;i++){
		printf("该节点数据是:%d 它的边有: ",G.Graph[i].data);
		AdjNode *t=G.Graph[i].head;
		while(t!=NULL){
			printf("%d ",t->aindex);
			t=t->next;
		}
		printf("\n");
	} 
}
void dfs(int n,AGraph G){
	//输出该邻接矩阵 深度优先搜索
    if(visit[n]==0){
        visit[n]=1;//标记
    	printf("%d\n",n);
    }
    int j;
    for(j=0;j<G.vexnum;j++){
    	if(visit[j]==0&&isLink(G.Graph[j],n)){
        	//如果该点没访问过并且相邻之间有边,可以访问
            dfs(j,G);
        }
    }
}
void bfs(int n,AGraph G){
	//利用队列完成广度优先搜索 
	ListQueue Q;
    LoadListQueue(&Q);
    PushListQueue(&Q,n);//起点入队
    while(isEmpty(&Q)!=1){
    	//队列不为空,BFS
        int v=popListQueue(&Q),i;
        if(visit[v]==0)
        printf("%d\n",v);
        visit[v]=1;//标记
        for(i=0;i<G.vexnum;i++){
        	if(visit[i]==0&&isLink(G.Graph[i],v)==1)
			{	//当i点没有访问过并且与v点有边 
            	PushListQueue(&Q,i);
            }
        }
    }
}
/*
输入实例 
5 6
0 3 1 -1
1 4 2 0 -1
2 4 3 1 -1
3 2 0 -1
4 2 1 -1
*/
int main(){
	AGraph G;
	CreateG(G);
	printG(G);
	printf("邻接表\n");
	printf("深度优先搜索:\n");
	dfs(0,G);
	int i;//重设一下visit 以免影响广度优先搜索 
	for(i=0;i<G.vexnum;i++){
		visit[i]=0;
	}
	printf("广度优先搜索:\n");
	bfs(0,G);
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值