数据结构 C 图的存储结构和两种遍历

图的存储结构

邻接矩阵

typedef struct graph{
    int** connection;//邻接矩阵(实质是二维数组) 
    int numNodes;//矩阵阶数
}*graphPtr; 

优点

  • 便于判断两个顶点之间是否有边,即根据A[i][j]=0或1来判断。
  • 便于计算各个顶点的度。对于无向图,邻接矩阵第i行元素之和就是顶点i的度;对于有向图,第i行元素之和就是顶点i的出度,第1列元素之和就是顶点i的入度。

缺点

  • 不便于增加和删除顶点。
  • 不便于统计边的数目,需要扫描邻接矩阵所有元素才能统计完毕,时间复杂度为O(n^2)。
  • 空间复杂度高。如果是有向图,n个顶点需要n^2个单元存储边。如果是无向图,因其邻接矩阵是对称的,所以对规模较大的邻接矩阵可以采用压缩存储的方法,仅存储下三角(或上三角)的元素,这样需要n(n- 1)/2个单元即可。但无论以何种方式存储,邻接矩阵表示法的空间复杂度均为O(n^2),这对于稀疏图而言尤其浪费空间。                                                                                                                                                                                               

邻接表

邻接表:实质上是邻接矩阵的压缩存储

typedef struct AdjacencyNode{//邻接结点边表 
    int subscript;//邻接点域
    struct AdjacencyNode* next;//链域 
}AdjacencyNode,*AdjacentNodePtr;
typedef struct AdjacencyList{//邻接表顶点表 
    int numNodes;//结点数 
    struct AdjacencyNode* header;//一维数组
}AdjacencyList,*AdjacencyListPtr;

优点

  • 便于增加和删除顶点。
  • 便于统计边的数目,按顶点表顺序扫描所有边表可得到边的数目,时间复杂度为O(n+ e)。
  • 空间效率高。对于一个具有n个顶点e条边的图G,若G是无向图,则在其邻接表表示中有n个顶点表结点和2e个边表结点;若G是有向图,则在它的邻接表表示或逆邻接表表示中均有n个顶点表结点和e个边表结点。因此,邻接表或逆邻接表表示的空间复杂度为O(n+e),适合表示稀疏图。对于稠密图,考虑到邻接表中要附加链域,因此常采取邻接矩阵表示法。

缺点

  • 不便于判断顶点之间是否有边,要判定vi和yj之间是否有边,就需扫描第i个边表,最坏情况下要耗费O(n)时间。
  • 不便于计算有向图各个顶点的度。对于无向图,在邻接表表示中顶点vi的度是第i个边表中的结点个数。在有向图的邻接表中,第i个边表上的结点个数是顶点vi的出度,但求vi的入度较困难,需遍历各顶点的边表。若有向图采用逆邻接表表示,则与邻接表表示相反,求顶点的入度容易,而求顶点的出度较难。 

图的遍历

图的两种遍历:深度优先搜索(DFS)和广度优先搜索(WFS),两种遍历算法的实现都需要使用访问标志数组visitedPtr[i]来标记当前结点i是否已经被访问

深度优先搜索(DFS)

DFS:使用了访问标志数组递归

void DFS(graphPtr paraGraphPtr, int paraNode/*结点标号*/){
    int i;
    visitedPtr[paraNode]=1;//将paraNode标记为1,表示该结点已经访问过 
    printf("%d\t",paraNode);
    for(i=0;i<paraGraphPtr->numNodes;i++){
        if(!visitedPtr[i]){//如果有未被访问的结点 
            if(paraGraphPtr->connection[paraNode][i]){//如果有paraNode的邻接点i(即[paraNode][i]为1) 
                DFS(paraGraphPtr,i);//对paraNode的尚未被访问的邻接点i递归调用DFS搜索 
            }//of if
        }//of if
    }//of for
}//of DFS

广度优先搜索(WFS) 

WFS: 使用了访问标志数组队列

void WFS(graphPtr paraGraphPtr, int paraStart/**/){
    //初始化辅助队列
    //printf("Begin to initialize queue.\r\n");
    QueuePtr tempQueue=initQueue();
    enqueue(tempQueue,paraStart);

    //printf("paraStart enqueued.\r\n");
    visitedPtr[paraStart]=1;
    printf("%d\t",paraStart);
    
    int i,j,tempNode;
    i=0;//i计数 
    while(!isQueueEmpty(tempQueue)){//队列不为空 
        tempNode=dequeue(tempQueue);//tempNode接收出队元素 
        //printf("tempNode=%d\r\n",tempNode);
        visitedPtr[tempNode]=1;//标记tempNode已经被访问 
        i++;//计数 
        for(j=0;j<paraGraphPtr->numNodes;j++){//遍历图寻找没有被访问且与tempNode有边的结点 
            if( visitedPtr[j]==0 && paraGraphPtr->connection[tempNode][j]==1){
                printf("%d\t",j);
                visitedPtr[j]=1;
                enqueue(tempQueue,j);
            }
        } 
        //printf("i=%d",i);
    }
    //printf("i=%d",i);

使用邻接矩阵的两种图遍历算法的完整代码: 

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h> 
#define QUEUE_SIZE 10//队列空间大小 
int* visitedPtr;//记录结点是否已被访问 
typedef struct graphNodeQueue{
	int* node;
	int front;
	int rear; 
}graphNodeQueue,*QueuePtr;
/*初始化队列*/ 
QueuePtr initQueue(){
	QueuePtr resultPtr=(QueuePtr)malloc(sizeof(struct graphNodeQueue));
	resultPtr->node=(int*)malloc(QUEUE_SIZE*sizeof(int));
	resultPtr->front=0;
	resultPtr->rear=1;
	return resultPtr;
}
/*判断队列是否为空*/
bool isQueueEmpty(QueuePtr paraQueue){
	if((paraQueue->front+1)%QUEUE_SIZE==paraQueue->rear){
		return true;
	}
	return false;
}
/*结点入队*/
void enqueue(QueuePtr paraQueue,int paraNode){
	//判断队满
	if((paraQueue->rear+1)%QUEUE_SIZE==paraQueue->front%QUEUE_SIZE){//注意paraQueue->front也要%QUEUE_SIZE
		printf("Error,tring to enqueue %d,but queue full.\r\n");
		return;
	} 
	//让结点入队(入尾指针处) 
	paraQueue->node[paraQueue->rear]=paraNode;
	//更新队列尾指针 
	paraQueue->rear= (paraQueue->rear+1)%QUEUE_SIZE;
}
/*结点出队*/
int dequeue(QueuePtr paraQueue){
	//判断队空 
	if(isQueueEmpty(paraQueue)){
		printf("Error,empty queue.\r\n");
		return -1;
	}
	paraQueue->front=(paraQueue->front+1)%QUEUE_SIZE;
	//printf("dequeue %d ends.\r\n", paraQueue->node[paraQueue->front]);
	return paraQueue->node[paraQueue->front];
}
/*图的存储表示*/
typedef struct graph{
	int** connection;//邻接矩阵 
	int numNodes;//矩阵阶数 
}*graphPtr; 	
/*初始化图*/
graphPtr initGraph(int paraSize,int** paraData){
	int i,j;
	graphPtr resultPtr=(graphPtr)malloc(sizeof(struct graph));
	resultPtr->connection=(int**)malloc(paraSize*sizeof(int*));
	resultPtr->numNodes=paraSize; 
	for(i=0;i<paraSize;i++){
		resultPtr->connection[i]=(int*)malloc(paraSize*sizeof(int));
		for(j=0;j<paraSize;j++){
			//printf("i=%d,j=%d.\r\n",i,j);
			resultPtr->connection[i][j]=paraData[i][j];
			//printf("i=%d,j=%d,connection[i][j]=%d.\r\n",i,j,resultPtr->connection[i][j]);
		}
	}
	return resultPtr;
}
/*初始化visitedPtr数组为0*/
void initVisitedPtr(graphPtr paraGraphPtr){
	visitedPtr=(int*)malloc(paraGraphPtr->numNodes*sizeof(int));
	int i;
	for(i=0;i<paraGraphPtr->numNodes;i++){
		visitedPtr[i]=0;
		//printf("visitedPtr[%d]=%d\r\n",i,visitedPtr[i]);
	}
}
/*深度优先搜索*/ 
void DFS(graphPtr paraGraphPtr, int paraNode/*结点标号*/){
	int i;
	visitedPtr[paraNode]=1;//将paraNode标记为1,表示该结点已经访问过 
	printf("%d\t",paraNode);
	for(i=0;i<paraGraphPtr->numNodes;i++){
		if(!visitedPtr[i]){//如果有未被访问的结点 
			if(paraGraphPtr->connection[paraNode][i]){//如果有paraNode的邻接点i(即[paraNode][i]为1) 
				DFS(paraGraphPtr,i);//递归调用DFS搜索邻接点i 
			}
		}
	}
}
/*广度优先搜索*/
void WFS(graphPtr paraGraphPtr, int paraStart/**/){
	//初始化辅助队列
	//printf("Begin to initialize queue.\r\n");
	QueuePtr tempQueue=initQueue();
	enqueue(tempQueue,paraStart);
	//printf("paraStart enqueued.\r\n");
	visitedPtr[paraStart]=1;
	printf("%d\t",paraStart);
	
	int i,j,tempNode;
	i=0;//i计数 
	while(!isQueueEmpty(tempQueue)){//队列不为空 
		tempNode=dequeue(tempQueue);//tempNode接收出队元素 
		//printf("tempNode=%d\r\n",tempNode);
		visitedPtr[tempNode]=1;//标记tempNode已经被访问 
		i++;//计数 
		for(j=0;j<paraGraphPtr->numNodes;j++){//遍历图寻找没有被访问且与tempNode有边的结点 
			if( visitedPtr[j]==0 && paraGraphPtr->connection[tempNode][j]==1){
				printf("%d\t",j);
				visitedPtr[j]=1;
				enqueue(tempQueue,j);
			}
		} 
		//printf("i=%d",i);
	}
	//printf("i=%d",i);
}
/*测试函数*/ 
void graphTranverseTest(){
	//初始化邻接矩阵	
	int myGraph[5][5] = { 
		{0, 1, 0, 1, 0},
		{1, 0, 1, 0, 1}, 
		{0, 1, 0, 1, 1}, 
		{1, 0, 1, 0, 0}, 
		{0, 1, 1, 0, 0}};
	printf("Preparing data\r\n");
	int i, j;
	int** tempPtr= (int**)malloc(5*sizeof(int*));
	for(i=0;i<5;i++){
		tempPtr[i]=(int*)malloc(5*sizeof(int));
		for(j=0;j<5;j++){
			tempPtr[i][j]=myGraph[i][j];
		}
	}
	printf("Data ready.\r\n");
	//初始化图
	graphPtr tempGraph=initGraph(5,tempPtr);
	printf("Graph initialized.\r\n");
	//DFS
	printf("DFS:\r\n");
	initVisitedPtr(tempGraph);//先将VisitedPtr数组初始化为0 
	DFS(tempGraph,4);
	//WFS
	printf("\r\nWFS:\r\n");
	initVisitedPtr(tempGraph);//将VisitedPtr数组初始化为0 
	WFS(tempGraph,4);
}
int main(){
	graphTranverseTest();
	return 0;
}

运行结果:

 使用邻接表的广度优先遍历算法:

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h> 
#define QUEUE_SIZE 10//队列空间大小 
int* visitedPtr;//记录结点是否已被访问 
typedef struct graphNodeQueue{
	int* node;
	int front;
	int rear; 
}graphNodeQueue,*QueuePtr;
/*初始化队列*/ 
QueuePtr initQueue(){
	QueuePtr resultPtr=(QueuePtr)malloc(sizeof(struct graphNodeQueue));
	resultPtr->node=(int*)malloc(QUEUE_SIZE*sizeof(int));
	resultPtr->front=0;
	resultPtr->rear=1;
	return resultPtr;
}
/*判断队列是否为空*/
bool isQueueEmpty(QueuePtr paraQueue){
	if((paraQueue->front+1)%QUEUE_SIZE==paraQueue->rear){
		return true;
	}
	return false;
}
/*结点入队*/
void enqueue(QueuePtr paraQueue,int paraNode){
	//判断队满
	if((paraQueue->rear+1)%QUEUE_SIZE==paraQueue->front%QUEUE_SIZE){//注意paraQueue->front也要%QUEUE_SIZE
		printf("Error,tring to enqueue %d,but queue full.\r\n");
		return;
	} 
	//让结点入队(入尾指针处) 
	paraQueue->node[paraQueue->rear]=paraNode;
	//更新队列尾指针 
	paraQueue->rear= (paraQueue->rear+1)%QUEUE_SIZE;
}
/*结点出队*/
int dequeue(QueuePtr paraQueue){
	//判断队空 
	if(isQueueEmpty(paraQueue)){
		printf("Error,empty queue.\r\n");
		return -1;
	}
	paraQueue->front=(paraQueue->front+1)%QUEUE_SIZE;
	//printf("dequeue %d ends.\r\n", paraQueue->node[paraQueue->front]);
	return paraQueue->node[paraQueue->front];
}
/*图的存储表示*/
typedef struct graph{
	int** connection;//邻接矩阵 
	int numNodes;//矩阵阶数 
}*graphPtr; 	
/*初始化图*/
graphPtr initGraph(int paraSize,int** paraData){
	int i,j;
	graphPtr resultPtr=(graphPtr)malloc(sizeof(struct graph));
	resultPtr->connection=(int**)malloc(paraSize*sizeof(int*));
	resultPtr->numNodes=paraSize; 
	for(i=0;i<paraSize;i++){
		resultPtr->connection[i]=(int*)malloc(paraSize*sizeof(int));
		for(j=0;j<paraSize;j++){
			resultPtr->connection[i][j]=paraData[i][j];
			//printf("i=%d,j=%d,connection[i][j]=%d.\r\n",i,j,resultPtr->connection[i][j]);
		}
	}
	return resultPtr;
}
/*图的存储结构*/
typedef struct AdjacencyNode{//邻接结点边表 
	int subscript;//邻接点域
	struct AdjacencyNode* next;//链域 
}AdjacencyNode,*AdjacentNodePtr;
typedef struct AdjacencyList{//邻接表顶点表 
	int numNodes;//结点数 
	struct AdjacencyNode* header;//一维数组
}AdjacencyList,*AdjacencyListPtr;
/*将图转换为邻接表*/
AdjacencyListPtr graphToAdjacentList(graphPtr paraGraph){
	//为邻接表分配空间 
	AdjacencyListPtr tempAdjList=(AdjacencyListPtr)malloc(sizeof(struct AdjacencyList));
	tempAdjList->numNodes=paraGraph->numNodes;
	tempAdjList->header=(AdjacentNodePtr)malloc(tempAdjList->numNodes*sizeof(struct AdjacencyNode));
	//创建邻接表
	int i,j;
	AdjacentNodePtr p, q;//p用于指示结点,q用于创建结点 
	for(i=0;i<paraGraph->numNodes;i++){
		//初始化边表 
		p=&(tempAdjList->header[i]);//p指向边表的头结点 
		p->subscript=-1;
		p->next=NULL;
		for(j=0;j<paraGraph->numNodes;j++){
			if(paraGraph->connection[i][j]==1){
				//创建新结点
				q=(AdjacentNodePtr)malloc(sizeof(struct AdjacencyNode));
				q->subscript=j;
				q->next=NULL;
				//链接
				p->next=q;
				p=q;//更新指针p,让它指向最后一个结点,便于下一次链接新结点 
			}//Of if 
		} //Of for
	} //Of for
	return tempAdjList;
}
/*打印邻接表*/
void printAdjList(AdjacencyListPtr paraAdjList){
	printf("The graph is:\r\n");
	int i;
	AdjacentNodePtr p; 
	for(i=0;i<paraAdjList->numNodes;i++){
		p=paraAdjList->header[i].next;
		while(p!=NULL){
			printf("%d\t",p->subscript);
			p=p->next;
		}//Of while
		printf("\r\n");
	}//Of for
} 
/*广度优先搜索邻接表*/
void WFS(AdjacencyListPtr paraAdjList,int paraStart){
	//初始化访问标志数组
	int i;
	visitedPtr=(int*)malloc(paraAdjList->numNodes*sizeof(int));
	for(i=0;i<paraAdjList->numNodes;i++){
		visitedPtr[i]=0;
	} 
	//队列
	QueuePtr tempQueue=initQueue();
	printf("%d\t",paraStart);
	visitedPtr[paraStart]=1;
	enqueue(tempQueue,paraStart);//让已访问的元素入队 
	int tempNode;//接收出队元素 
	AdjacentNodePtr p;//指向每一个边表的头结点 ,遍历边表
	while(!isQueueEmpty(tempQueue)){//队列不为空 
		tempNode=dequeue(tempQueue);//出队元素 
		for(p=&(paraAdjList->header[tempNode]);p!=NULL;p=p->next){
			if(visitedPtr[p->subscript]==0){
				printf("%d\t",p->subscript);
				visitedPtr[p->subscript]=1;
				enqueue(tempQueue,p->subscript); 
			}//Of if
		}//Of for
	}//Of while
} 
void WFSofAdjacencyListTest(){
	int i, j;
	int myGraph[5][5] = { 
		{0, 1, 0, 1, 0},
		{1, 0, 1, 0, 1}, 
		{0, 1, 0, 1, 1}, 
		{1, 0, 1, 0, 0}, 
		{0, 1, 1, 0, 0}};
	int** tempPtr;
	printf("Preparing data\r\n");
		
	tempPtr = (int**)malloc(5 * sizeof(int*));
	for (i = 0; i < 5; i ++) {
		tempPtr[i] = (int*)malloc(5 * sizeof(int));
		for (j = 0; j < 5; j ++) {
			tempPtr[i][j] = myGraph[i][j];
			//printf("i = %d, j = %d, %d\r\n", i, j, tempPtr[i][j]);
		}//Of for j
	}//Of for i
 
	printf("Data ready\r\n");
	
	graphPtr tempGraphPtr = initGraph(5, tempPtr);
	AdjacencyListPtr tempListPtr = graphToAdjacentList(tempGraphPtr);

	printAdjList(tempListPtr);
	printf("The WFS:\r\n");
	WFS(tempListPtr, 4);
}
int main(){
	WFSofAdjacencyListTest();
	return 0;
}

运行结果:  

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值