PTA 列出连通集 思路分析及代码解析(邻接表)

一、前导

0. 写在前面

  1. 如果从应试角度考虑,本题不建议使用 邻接表:代码量较邻接矩阵大的多、理解起来也不容易
  2. 可能是自己太菜,感觉邻接表存储实在是太麻烦了。搞了好几天才算基本掌握
  3. 本文主要介绍了与 PTA 列出连通集(邻接矩阵) 思路分析 不同的部分,重点介绍了邻接表的数据结构
  4. 收获:通过邻接表存储,锻炼了自己对图的进一步理解、提升了一点编码能力

1. 需要掌握的知识

  1. 图的邻接表存储、BFS、DFS

2. 题目信息

题目来源:PTA / 拼题A
题目地址:列出连通集

二、解题思路分析

1. 题意理解

  1. 存储图并通过深度优先和广度优先分别进行遍历

2. 思路分析

  1. 通过链表存储图
  2. 图顶点从0开始编号

三、具体实现

1. 弯路和bug

  1. 使用指针时,赋值前先通过malloc( )申请对应的内存空间,然后再使用。指针的本质就是内存中的地址

2. 代码框架

2.1 邻接表

  1. 使用邻接表存储,相关数据结构包括:图、图头结点、邻接点 以及 边
  2. 图、图头结点、邻接点 之间 环环相扣,很容易把人搞晕,一开始不熟悉的时候,只能多看几遍了,熟能生巧吧
  3. 本题其实比较简单,并没有包含边的权值、图顶点数据
#define max 10  //题目锁定了顶点最大值为10
typedef int vertex;

struct GraphEdge  //定义边
{
	vertex V1;
	vertex V2;
};
typedef struct GraphEdge *PtrEdge;


typedef struct AdjNode *PtrAdjNode; 
struct AdjNode //定义邻接点:邻接点编号、头结点的下一个邻接点
{
	vertex VertexIndex;
	PtrAdjNode Next;	
};


struct HeadNode  //定义图头结点
{  //举个栗子,如:1->2->NULL,头结点对应HeadVertex[1]中的数组下标,
//邻接点是 HeadVertex[1].AdjacentVertex->VertexIndex=2;
	PtrAdjNode AdjacentVertex;
};
typedef struct HeadNode HeadVertex[max];


struct Graph //定义图结构:顶点数、边数、头结点数组
{
	int VertexNumber;
	int EdgeNumber;
	HeadVertex head;
} ;
typedef struct Graph *PtrGraph;

2.2 程序主体框架

               程序伪码描述
int main()
{	
	1.依据输入数据,通过邻接表存储图;
	2.深度优先搜索并打印;
	3.广度优先搜索并打印;
}

2.3 各分支函数

  1. CreateGraph( ) 创建创建一个 VertexNumber个顶点、无边的图,被Build函数调用
PtrGraph CreateGraph(int VertexNumber)
{
	PtrGraph theGraph;
	
	theGraph=(PtrGraph)malloc(sizeof(struct Graph));
	theGraph->VertexNumber=VertexNumber;
	theGraph->EdgeNumber=0;
	for(int i=0;i<max;i++) //初始化图头结点,创建时无边,所以赋值为NULL 
		theGraph->head[i].AdjacentVertex=NULL; 
	return  theGraph;
} 
  1. insert( ) 将边加入图中,被Build函数调用。需要注意,代码中邻接点链表的形成与录入顺序相反,举例:边的录入顺序是 0 7, 0 1 , 0 2 ,头结点0的邻接点链表形成顺序是: 结点0 : NULL, 7-NULL, 1-7-NULL, 2-1-7-NULL ,这里建议参考代码仔细理解。 另外,由于题目是无向图,因此边需要存两次
void insert(PtrGraph Graph,PtrEdge Edge) 
{
	PtrAdjNode Node;
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNode));  
	Node->VertexIndex=Edge->V2;   
	Node->Next = Graph->head[Edge->V1].AdjacentVertex;
	Graph->head[Edge->V1].AdjacentVertex=Node;

	Node=(PtrAdjNode)malloc(sizeof(struct AdjNode));  
	Node->VertexIndex=Edge->V1;
	Node->Next = Graph->head[Edge->V2].AdjacentVertex;
	Graph->head[Edge->V2].AdjacentVertex=Node;
	 
	return;	
}
  1. BuildGraph( ) 创建VertexNumber个顶点、EdgeNumber边的图,需要用到create和insert子函数
PtrGraph BuildGraph()
{
	PtrGraph theGraph;
	PtrEdge Edge;
	int VertexNumber, EdgeNumber;
	
	cin>>VertexNumber;
	theGraph=CreateGraph(VertexNumber);
	cin>>theGraph->EdgeNumber;
	
	for(int i=0;i<theGraph->EdgeNumber;i++)
	{
		Edge=(PtrEdge)malloc(sizeof(struct GraphEdge)); 
		//所有的指针型数据 均需要 申请内存空间 ,指针指向一段内存空间 
		cin>>Edge->V1>>Edge->V2; 
		insert(theGraph,Edge);
	}
	return theGraph;
} 
  1. isEdge( ) 判定两个顶点是否存在边:邻接矩阵就是一行语句,邻接表就麻烦多了
bool isEdge(vertex V1,vertex V2)
{
	bool result=false;
	PtrAdjNode AdjNode;
	
	AdjNode=theGraph->head[V1].AdjacentVertex;
	
	if(!AdjNode)
		return false;
	else
	{
		while(AdjNode)
		{
			if(V2==AdjNode->VertexIndex)
				return true;
			else 
				AdjNode=AdjNode->Next;
		}
		return false;
	}
}
  1. BFS, DFS 与 列出连通集(邻接矩阵) 思路分析 相同,这里不再重复了

3. 完整AC代码

#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;

#define max 10  //题目锁定了顶点最大值为10
typedef int vertex;

struct GraphEdge  //定义边
{
	vertex V1;
	vertex V2;
};
typedef struct GraphEdge *PtrEdge;


typedef struct AdjNode *PtrAdjNode; 
struct AdjNode //定义邻接点:邻接点编号、头结点的下一个邻接点
{
	vertex VertexIndex;
	PtrAdjNode Next;	
};


struct HeadNode  //定义图头结点
{  //举个栗子,如:1->2->NULL,头结点对应HeadVertex[1]中的数组下标,
//邻接点是 HeadVertex[1].AdjacentVertex->VertexIndex=2;
	PtrAdjNode AdjacentVertex;
};
typedef struct HeadNode HeadVertex[max];


struct Graph //定义图结构:顶点数、边数、头结点数组
{
	int VertexNumber;
	int EdgeNumber;
	HeadVertex head;
} ;
typedef struct Graph *PtrGraph;

PtrGraph CreateGraph(int VertexNumber); // create:创建一个 VertexNumber个顶点、无边的图
void insert(PtrGraph Graph,PtrEdge Edge); //insert:将边加入图中 
PtrGraph BuildGraph(); //build: 将边加入图中 
void PrintGraph();
void DeepFirstSearch(vertex Node);
void BreadthFirstSearch(vertex Node);
bool isEdge(vertex V1,vertex V2);

bool visited[max]={false};
PtrGraph theGraph;

int main()
{	
	
	theGraph=BuildGraph();
	
	for(int i=0;i<theGraph->VertexNumber;i++)
	{
		if(!visited[i])
		{
			cout<<"{ ";
			DeepFirstSearch(i);
			cout<<"}"<<endl;
		}	
	}
	
	for(int i=0;i<max;i++)
		visited[i]=false;
	
	for(int i=0;i<theGraph->VertexNumber;i++)
	{
		if(!visited[i])
		{
			cout<<"{ ";
			BreadthFirstSearch(i);
			cout<<"}"<<endl;
		}	
	}
	
	return 0;
} 

bool isEdge(vertex V1,vertex V2)
{
	bool result=false;
	PtrAdjNode AdjNode;
	
	AdjNode=theGraph->head[V1].AdjacentVertex;
	
	if(!AdjNode)
		return false;
	else
	{
		while(AdjNode)
		{
			if(V2==AdjNode->VertexIndex)
				return true;
			else 
				AdjNode=AdjNode->Next;
		}
		return false;
	}
}

void BreadthFirstSearch(vertex Node)
{
	queue<vertex> GraphQueue;
	vertex frontNode;
	
	GraphQueue.push(Node);
	visited[Node]=true;
	
	while(!GraphQueue.empty()) 
	{
		frontNode=GraphQueue.front();
		GraphQueue.pop();
		cout<<frontNode<<" ";
		for(int i=0;i<theGraph->VertexNumber;i++)
		{
			if(isEdge(frontNode,i) && !visited[i])
			{
				GraphQueue.push(i);
				visited[i]=true;
			}
		}
	}
	
	return;
}

void DeepFirstSearch(vertex Node)
{
	visited[Node]=true;
	cout<<Node<<' ';
	for(int i=0;i<theGraph->VertexNumber;i++)
	{
		if(isEdge(Node,i) && !visited[i])
			DeepFirstSearch(i);
	}
	return ;
}

void PrintGraph()
{
	PtrAdjNode AdjNode;
	
	for(int i=0; i<theGraph->VertexNumber;i++) //打印邻接点 
	{
		cout<<"Node "<<i<<": ";
		AdjNode=theGraph->head[i].AdjacentVertex;
		
		if(!AdjNode) cout<<"NULL"<<endl;
		else
		{
			while(AdjNode)
			{
				cout<<AdjNode->VertexIndex<<" ";
				AdjNode=AdjNode->Next;
			}
			cout<<endl;	
		}	
	}
	return;
}


PtrGraph BuildGraph()
{
	PtrGraph theGraph;
	PtrEdge Edge;
	int VertexNumber, EdgeNumber;
	
	cin>>VertexNumber;
	theGraph=CreateGraph(VertexNumber);
	cin>>theGraph->EdgeNumber;
	
	for(int i=0;i<theGraph->EdgeNumber;i++)
	{
		Edge=(PtrEdge)malloc(sizeof(struct GraphEdge)); //所有的指针型数据 均需要 申请内存空间 ,指针指向一段内存空间 
		cin>>Edge->V1>>Edge->V2; 
		insert(theGraph,Edge);
	}
	return theGraph;
} 


PtrGraph CreateGraph(int VertexNumber)
{
	PtrGraph theGraph;
	
	theGraph=(PtrGraph)malloc(sizeof(struct Graph));
	theGraph->VertexNumber=VertexNumber;
	theGraph->EdgeNumber=0;
	for(int i=0;i<max;i++) //初始化图头结点,无边图所以赋值为NULL 
		theGraph->head[i].AdjacentVertex=NULL; 
	return  theGraph;
} 

void insert(PtrGraph Graph,PtrEdge Edge) 
{
	PtrAdjNode Node;
	Node=(PtrAdjNode)malloc(sizeof(struct AdjNode));  
	Node->VertexIndex=Edge->V2;   
	Node->Next = Graph->head[Edge->V1].AdjacentVertex;
	Graph->head[Edge->V1].AdjacentVertex=Node;

	Node=(PtrAdjNode)malloc(sizeof(struct AdjNode));  
	Node->VertexIndex=Edge->V1;
	Node->Next = Graph->head[Edge->V2].AdjacentVertex;
	Graph->head[Edge->V2].AdjacentVertex=Node;
	 
	return;	
}


四、参考

  1. 浙江大学 陈越、何钦铭老师主讲的数据结构
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值