图的遍历和邻接表

本文探讨了图的数据结构,包括无向图和有向图,并详细介绍了图的两种遍历方法:深度优先遍历和广度优先遍历。同时,讲解了邻接表的概念,通过老师的代码示例解释了邻接表的创建、打印和遍历过程,强调了邻接表在存储无向图时可能出现的数据冗余问题。
摘要由CSDN通过智能技术生成

一.图及相关操作

1.老师的代码(重点关注图相关代码)

结构体


typedef struct Graph{
	int** connections;
	int numNodes;
}*GraphPtr;

初始化


GraphPtr initGraph(int paraSize,int** paraData){
	int i,j;
	GraphPtr resultPtr=(GraphPtr)malloc(sizeof(struct Graph));
	resultPtr->numNodes=paraSize;
	resultPtr->connections=(int**)malloc(paraSize*sizeof(int*));
	
	for (i=0;i<paraSize;i++){
		resultPtr->connections[i]=(int*)malloc(paraSize*sizeof(int));
		for (j=0;j<paraSize;j++){
			resultPtr->connections[i][j]=paraData[i][j];
		}
	}
	
	return resultPtr;
}

初始化记录数组及深度优先遍历


void initTranverse(GraphPtr paraGraphPtr){
	int i;
	visitedPtr=(int*)malloc(paraGraphPtr->numNodes*sizeof(int));
	
	for (i=0;i<paraGraphPtr->numNodes;i++){
		visitedPtr[i]=0;
	}
}

void depthFirstTranverse(GraphPtr paraGraphPtr,int paraNode) {
	int i;
	
	visitedPtr[paraNode] = 1;
	printf("%d\t",paraNode);
	
	for (i=0;i<paraGraphPtr->numNodes;i++){
		if (!visitedPtr[i]){ 
			if (paraGraphPtr->connections[paraNode][i]){
				depthFirstTranverse(paraGraphPtr,i);
			}
		}
	}
}

广度优先遍历


void widthFirstTranverse(GraphPtr paraGraphPtr,int paraStart){
	int i,j,tempNode;
	i=0;
	QueuePtr tempQueuePtr=initQueue();
	printf("%d\t",paraStart);
	visitedPtr[paraStart]=1;
	enqueue(tempQueuePtr,paraStart);
	while(!isQueueEmpty(tempQueuePtr)){
		tempNode=dequeue(tempQueuePtr);
		visitedPtr[tempNode]=1;
		
		i++;

		for (j=0;j<paraGraphPtr->numNodes;j++){
			if (visitedPtr[j]) 
				continue;

			if (paraGraphPtr->connections[tempNode][j]==0)
				continue;
			
			printf("%d\t",j);
			visitedPtr[j]=1;
			enqueue(tempQueuePtr,j);
		}
	}
}

运行结果

Preparing data
Data ready
num nodes = 5
Graph initialized
Depth first visit:
4       1       0       3       2
Width first visit:
4       1       2       0       3       Press any key to continue

2.自己的代码

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>

#define QUEUE_SIZE 10

int* visited;

typedef struct GraphNQ{
	int* nodes;
	int front;
	int rear;
}*Queueptr;

Queueptr initQueue()
{
	Queueptr result=(Queueptr)malloc(sizeof(struct GraphNQ));
	
	result->nodes=(int*)malloc(QUEUE_SIZE*sizeof(int));
	result->front=0;
	result->rear=1;
	
	return result;
}

bool cheak(Queueptr p)
{
	if ((p->front+1)%QUEUE_SIZE==p->rear)
		return true;

	return false;
}

void enqueue(Queueptr p,int paraN)
{
	if ((p->rear+1)%QUEUE_SIZE==p->front%QUEUE_SIZE){
		printf("Error Queue full\r\n");
		return;
	}
	
	p->nodes[p->rear]=paraN;
	p->rear=(p->rear+1)%QUEUE_SIZE;
}

int dequeue(Queueptr p)
{
	if (cheak(p)){
		printf("Error empty\r\n");
		return -1;
	}

	p->front=(p->front+1)%QUEUE_SIZE;

	return p->nodes[p->front];
}

typedef struct Graph{
	int** connections;
	int num;
}*GraphPtr;

GraphPtr initGraph(int paraS,int** paraD)
{
	GraphPtr resultPtr=(GraphPtr)malloc(sizeof(struct Graph));
	resultPtr->num=paraS;
	resultPtr->connections=(int**)malloc(paraS*sizeof(int*));
	
	for (int i=0;i<paraS;i++){
		resultPtr->connections[i]=(int*)malloc(paraS*sizeof(int));
		for (int j=0;j<paraS;j++)
			resultPtr->connections[i][j]=paraD[i][j];
	}
	
	return resultPtr;
}

void initT(GraphPtr paraGraph)
{
	visited=(int*)malloc(paraGraph->num*sizeof(int));
	
	for (int i=0;i<paraGraph->num;i++)
		visited[i]=0;
}

void depth(GraphPtr pGraph,int paraN) 
{
	visited[paraN] = 1;
	printf("%d\t",paraN);
	
	for (int i=0;i<pGraph->num;i++){
		if (!visited[i]){ 
			if (pGraph->connections[paraN][i])
				depth(pGraph,i);
		}
	}
}

void width(GraphPtr pGraph,int pStart)
{
	int tNode;

	Queueptr tQueue=initQueue();
	printf("%d\t",pStart);
	visited[pStart]=1;
	enqueue(tQueue,pStart);
	
	int i=0;
	while(!cheak(tQueue)){
		tNode=dequeue(tQueue);
		visited[tNode]=1;
		
		i++;

		for (int j=0;j<pGraph->num;j++){
			if (visited[j]) 
				continue;

			if (pGraph->connections[tNode][j]==0)
				continue;
			
			printf("%d\t",j);
			visited[j]=1;
			enqueue(tQueue,j);
		}
	}
}

void test() 
{
	int graph[5][5]={ 
		{0,1,1,1,0},
		{1,0,1,0,1}, 
		{1,1,0,1,1}, 
		{1,0,1,0,0}, 
		{0,1,1,0,0}
	};
	int** tempPtr;
		
	tempPtr=(int**)malloc(5*sizeof(int*));
	for (int i=0;i<5;i++) {
		tempPtr[i]=(int*)malloc(5*sizeof(int));
	}
	 
	for (int i=0;i<5;i++) {
		for (int j=0;j<5;j++) 
			tempPtr[i][j]=graph[i][j];
	}
	
	GraphPtr tempGraphPtr=initGraph(5,tempPtr);
	printf("num nodes = %d \r\n",tempGraphPtr->num);

	printf("Depth:\r\n");
	initT(tempGraphPtr);
	depth(tempGraphPtr,4);

	printf("\r\nWidth:\r\n");
	initT(tempGraphPtr);
	width(tempGraphPtr,4);
}

void main(){
	test();
}

运行结果

1.图是由顶点集合以及顶点间的关系集合组成的一种数据结构。

2.图是按照无方向和有方向分为无向图和有向图。图的遍历方式分为两种:深度优先遍历与广度优先遍历,二者的时间复杂度都是O(n2).

二.邻接表及相关操作

老师的代码(重点理解邻接表相关)

结构体


typedef struct AdjacencyNode {
	int column;
	struct AdjacencyNode* next;
}AdjacencyNode, *AdjacentNodePtr;

typedef struct AdjacencyList {
	int num;
	struct AdjacencyNode* headers;
}AdjacencyList, *AdjacencyListPtr;

创建邻接表


AdjacencyListPtr graphToAdjacentList(GraphPtr paraPtr){
	int i,j,tempNum;
	AdjacentNodePtr p,q;
	tempNum=paraPtr->num;
	AdjacencyListPtr resultPtr=(AdjacencyListPtr)malloc(sizeof(struct AdjacencyList));
	resultPtr->num=tempNum;
	resultPtr->headers=(AdjacencyNode*)malloc(sizeof(struct AdjacencyNode)*tempNum);
	
	for(i=0;i<tempNum;i++){
		p=&(resultPtr->headers[i]);
		p->column=-1;
		p->next=NULL;
		
		for(j=0;j<tempNum;j++){
			if(paraPtr->connections[i][j]>0){
				q=(AdjacentNodePtr)malloc(sizeof(struct AdjacencyNode));
				q->column=j;
				q->next=NULL;
				
				p->next=q;
				p=q;
			}
		}
	}
	
	return resultPtr;
}

打印邻接表


void printAdjacentList(AdjacencyListPtr paraPtr){
	int i;
	AdjacentNodePtr p;
	int tempNum=paraPtr->num;

	printf("This is the graph:\r\n");
	for (i=0;i<tempNum;i ++){
		p=paraPtr->headers[i].next;
		while (p!=NULL){
			printf("%d, ",p->column);
			p=p->next;
		}
		printf("\r\n");
	}
}

遍历


void width(AdjacencyListPtr paraListPtr,int pStart){
	printf("width first \r\n");
	int i,j,tNode;
	AdjacentNodePtr p;
	i=0;

	visited=(int*)malloc(paraListPtr->num*sizeof(int));
	
	for(i=0;i<paraListPtr->num;i++){
		visited[i]=0;
	}

	QuenePtr tQueue=initQuene();
	printf("%d\t",pStart);
	visited[pStart]=1;
	enquene(tQueue,pStart);
	
	while (!cheak(tQueue)){
		tNode=dequene(tQueue);

		for(p=&(paraListPtr->headers[tNode]);p!=NULL;p=p->next){
			j=p->column;
			if(visited[j]) 
				continue;

			printf("%d\t",j);
			visited[j]=1;
			enquene(tQueue,j);
		}
	}
	printf("\r\n");
}

运行结果

图的邻接表存储方法跟树的链式表示法相类似。

这是一种顺序和链式相结合的存储结构。如头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于头结点所指向的单向链表中。表结点存放的是邻接顶点在数组中的索引。对于无向图来说,使用邻接表进行存储也会出现数据冗余。表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值