PTA 列出连通集(通过邻接表存储图) 思路分析及代码解析v1.0
一、前导
0. 写在前面
- 如果从应试角度考虑,本题不建议使用 邻接表:代码量较邻接矩阵大的多、理解起来也不容易
- 可能是自己太菜,感觉邻接表存储实在是太麻烦了。搞了好几天才算基本掌握
- 本文主要介绍了与 PTA 列出连通集(邻接矩阵) 思路分析 不同的部分,重点介绍了邻接表的数据结构
- 收获:通过邻接表存储,锻炼了自己对图的进一步理解、提升了一点编码能力
1. 需要掌握的知识
- 图的邻接表存储、BFS、DFS
2. 题目信息
题目来源:PTA / 拼题A
题目地址:列出连通集
二、解题思路分析
1. 题意理解
- 存储图并通过深度优先和广度优先分别进行遍历
2. 思路分析
- 通过链表存储图
- 图顶点从0开始编号
三、具体实现
1. 弯路和bug
- 使用指针时,赋值前先通过malloc( )申请对应的内存空间,然后再使用。指针的本质就是内存中的地址
2. 代码框架
2.1 邻接表
- 使用邻接表存储,相关数据结构包括:图、图头结点、邻接点 以及 边
- 图、图头结点、邻接点 之间 环环相扣,很容易把人搞晕,一开始不熟悉的时候,只能多看几遍了,熟能生巧吧
- 本题其实比较简单,并没有包含边的权值、图顶点数据
#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 各分支函数
- 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;
}
- 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;
}
- 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;
}
- 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;
}
}
- 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;
}