1、数组(邻接矩阵)
无向图的邻接矩阵是对称的
顶点i的度=第i行(列)中1的个数
特别:完全图的邻接矩阵中,对角元素为0,其余1。
#define MVNum 100 //最大顶点数
typedef string VerTexType; //假设顶点的数据类型为字符串
typedef int ArcType; //假设边的权值类型为整型
//------------图的邻接矩阵------------------
typedef struct {
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum; //图的当前点数和边数
} Graph;
//得到顶点i的数据
VerTexType Vertexdata(const Graph& g, int i)
{
return g.vexs[i];
}
int LocateVex(const Graph& g, VerTexType v)
{
//确定点v在G中的位置
for (int i = 0; i < g.vexnum; ++i)
if (g.vexs[i] == v)
return i;
return -1;
}//LocateVex
int FirstAdjVex(const Graph& g, int v)
{
//返回v的第一个邻接点编号,没有返回-1
for (int i = 0; i < g.vexnum; i++)if (g.arcs[v][i])return i;
return -1;
}//FirstAdjVex
int NextAdjVex(const Graph& g, int v, int w)
{
//返回v相对于w的下一个邻接点,没有返回-1
for (int i = w + 1; i < g.vexnum; i++) if (g.arcs[v][i])return i;
return -1;
}//NextAdjVex
void CreateUDG(Graph& g)
{
//采用邻接矩阵表示法,创建无向图G
cin >> g.vexnum >> g.arcnum;
for (int i = 0; i < g.vexnum; i++)cin >> g.vexs[i];
for (int i = 0; i < g.vexnum; i++)
for (int j = 0; j < g.vexnum; j++) {
g.arcs[i][j] = 0;
}
for (int k = 0; k < g.arcnum; k++)
{
string s1, s2;
cin >> s1 >> s2;
int m = LocateVex(g, s1);
int n = LocateVex(g, s2);
if (m != -1 && n != -1)g.arcs[m][n] = g.arcs[n][m] = 1;
}
}//CreateUDN
void DestroyUDG(Graph& g)
{
//you should do this
memset(g.arcs, 0, sizeof( g.arcs ));
}
//输出邻接矩阵
void PrintUDG(const Graph& g)
{
int i, j;
cout << " ";
for (i = 0; i < g.vexnum; i++) {
cout << setw(4) << g.vexs[i];
}
cout << endl;
for (i = 0; i < g.vexnum; i++) {
cout << setw(4) << g.vexs[i];
for (j = 0; j < g.vexnum; j++) {
cout << setw(4) << g.arcs[i][j];
}
cout << endl;
}
}
有向图的邻接矩阵可能是不对称的。
行为出度,列为入度
顶点的出度=第i行元素之和顶点的入度=第i列元素之和
顶点的度=第i行元素之和+第i列元素之和
#define MAXlnt 32999 // 极大值,无穷
#define MVNum 100 //最大顶点数
typedef string VerTexType; //假设顶点的数据类型为字符串
typedef int ArcType; //假设边的权值类型为整型
//------------图的邻接矩阵------------------
typedef struct {
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum; //图的当前点数和边数
} AMGraph;
int LocateVex(AMGraph G, VerTexType u)
{
// 图G中查找顶点u,存在则返回顶点表中的下标;否则返回-1
int i;
for (i = 0; i < G.vexnum; ++i)
if (u == G.vexs[i])
return i;
return -1;
}
Status CreateUDN(AMGraph &G)
{
// 采用邻接矩阵表示法,创建无向网G
int i,k,j,w;
VerTexType v1,v2;
G.vexnum >> G.arcnum; // 输入总顶点数,总边数
for(i = 0; i<G.vexnum; ++i)
cin >> G.vexs[i];
// 依次输入点的信息
for (i = 0; i < G.vexnum; ++i) // 初始化邻接矩阵
for(j = 0; j<G.vexnum; ++j)
G.arcs[i][j] = MAXlnt; // 边的权值均置为极大值
for (k = 0; k < G.arcnum; ++k)
{ // 构造邻接矩阵
cin >> v1 >> v2 >> w; // 输入—条边所依附的顶点及边的权值
i = LocateVex(G, v1);
j = LocateVex(G, v2); // 确定v1和v2在G中的位置
G.arcs[i][j] = w; // 边<v1,v2>的权值置为w
G.arcs[j][i] = G.arcs[i][j]; // 置<v1, v2>的对称边<v2, v1>的权值为w
}//for
return 1;
} // CreateUDN
2、邻接表
#define MVNum 100 //最大顶点数
#define OK 1
typedef string VerTexType; //顶点信息
typedef int OtherInfo; //和边相关的信息
//- - - - -图的邻接表存储表示- - - - -
typedef struct ArcNode { //边结点
int adjvex; //该边所指向的顶点的位置
struct ArcNode* nextarc; //指向下一条边的指针
OtherInfo info; //和边相关的信息
} ArcNode;
typedef struct VNode {
VerTexType data; //顶点信息
ArcNode* firstarc; //指向第一条依附该顶点的边的指针
} VNode, AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct {
AdjList vertices; //邻接表
int vexnum, arcnum; //图的当前顶点数和边数
} ALGraph;
采用邻接表表示法创建无向网
【算法思想】
(1)输入总顶点数和总边数。
(2)建立顶点表
依次输入点的信息存入顶点表中
使每个表头结点的指针域初始化为NULL
(3)创建邻接表
依次输入每条边依附的两个顶点确定两个顶点的序号i和j,建立边结点
将此边结点分别插入到v,和v;对应的两个边链表的头部
int CreateUDG(Graph& g)
{
//采用邻接表表示法,创建无向图G
int i, j, k;
ArcNode* p1, * p2;
cin >> g.vexnum >> g.arcnum;
k = g.vexnum;
for (i = 0; i < k; ++i)
{
cin >> g.vertices[i].data;
g.vertices[i].firstarc = NULL;
}
k = g.arcnum;
while (k--)
{
VerTexType v1, v2;
cin >> v1;
cin >> v2;
i = LocateVex(g, v1);
j = LocateVex(g, v2);
p1 = new ArcNode;
p2 = new ArcNode;
p1->adjvex = j;
p1->nextarc = g.vertices[i].firstarc;
g.vertices[i].firstarc = p1;
p2->adjvex = i;
p2->nextarc = g.vertices[j].firstarc;
g.vertices[j].firstarc = p2;
}
for (int i = 0; i < g.vexnum; ++i) {
sort(g.vertices[i].firstarc); 保证有序,不依赖输入次序
}//for
return OK;
}//CreateUDG
1.联系:邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数。
3、图的遍历
1)深度优先搜索(DFS)
连通图的深度优先遍历类似手树的先根遍历
void DFS(AMGraph G, int v){
//图G为邻接矩阵类型
cout<<v; visited[v] = true;//访问第v个顶点
for(w = 0; w< G.vexnum; w++)//依次检查邻接矩阵v所在的行
if((G.arcs[v][w]!=0)&& (!visited[w]))
DFS(G,w);
//w是v的邻接点,如果w未访问,则递归调用DFS
}
void DFS_AL(AMGraph G, int v){
//图G为邻接表类型
cout<<v; visited[v] = true;//访问第v个顶点
p=G.vertices[v].firstarc //p指向v边链表的第一个边结点
while(!p)
{
w=p->adjvex; //w是v的邻接点
if(!visited[w])
DFS(G,w);
p=p->nextarc; //指向下一边结点
//w是v的邻接点,如果w未访问,则递归调用DFS
}
}
2)广度优先搜索(BFS)
类似于树的按层次遍历
void BFS(Graph G, int v){
//按广度优先非递归遍历连通图G
cout<<v; visited[v] = true;//访问第v个顶点
lnitQueue(Q);
//辅助队列Q初始化,置空
EnQueue(Q, v);
//v进队
while(!QueueEmpty(Q)){
//队列非空
DeQueue(Q, u);
//队头元素出队并置为u
for(w = FirstAdjVex(G, u); w> =0; w = NextAdjVex(G, u, w))
if(!visited[w]){
//w为u的尚未访问的邻接顶点
cout<<w; visited[w] = true; EnQueue(Q, w);//w进队
}//if
}//while
}//BFS
DFS和BFS比较
·空间复杂度相同,都是O(n)(借用了堆栈或队列) ;
·时间复杂度只与存储结构,邻接矩阵O(n^2)或邻接表O(e+n)有关,而与搜索路径无关。