您的位置:首页 > 理论基础 > 数据结构算法

【数据结构】图的存储方式:邻接矩阵和邻接表

2016-12-02 14:19 323 查看
本文主要简单介绍了[b]“[b][b]数据结构[/b]”[/b]中,图的存储方式,包括邻接矩阵的存储方式和邻接表的存储方式,并使用C++进一步实现。[/b]
-- 说明:本博文属于基础篇,适合于初学或者尚未学且对数据结构感兴趣的同学,核心内容如下:

  1. 图的存储结构理论简介,以及创建图的算法;

  2. 无向邻接矩阵的应用,主要介绍了通过文件流读取城市路径(旅行商问题)的相关数据,将值赋给图的数据结构相关的成员;

  3. 关于旅行商问题的数据说明与处理;

-- 此外,在本文的基础上,后续还会有6篇左右的文章基于图的存储结构,解决TSP旅行商问题,分别包括如下标题:

-------------------------------------------------------------------------------------------------
(1)TSP_旅行商问题- 蛮力法( 深度遍历优先算法DFS )
(2)TSP_旅行商问题- 动态规划
(3)TSP_旅行商问题- 模拟退火算法
(4)TSP_旅行商问题- 遗传算法
(5)TSP_旅行商问题- 粒子群算法

(6)TSP_旅行商问题- 神经网络

-------------------------------------------------------------------------------------------------

一、图的存储结构

1. 邻接矩阵表示法 - 以无向图为例:

  1)邻接矩阵简介

-- 逻辑结构分为两部分:Vexs[](存储顶点)和Arcs[][](邻接矩阵)集合。因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。

-- 邻接矩阵又分为有向图邻接矩阵和无向图邻接矩阵,此处以无向图为例。

  2)无向图邻接矩阵的特点:

-- 对无向图而言,邻接矩阵一定是对称的,而且主对角线一定为零(在此仅讨论无向简单图),副对角线不一定为0,有向图则不一定如此;

-- 无向图邻接矩阵是一个对称矩阵;

-- 顶点Vi的度等于第i行非零元个数,或第i列非零元个数;(有向图没有此特点)

    


-- 矩阵非零元总数等于边数的2倍;

-- 用邻接矩阵表示图,很容易确定图中任意两个顶点是否有边相连;

  3)完整的数据结构表示如下:

/* 1. 图 - 邻接矩阵表示法 */
/* ---------------------------------------------------------------- */
/* 较完善的数据结构 */
#define VRType int
#define InfoType int
#define VertexType char
#define max_n 20
typedef enum{DG, DN, AG, AN} GraphKind;

//  弧结点与矩阵的类型
typedef struct {
VRType    adj;          //VRType为弧的类型。图--0,1;网--权值
InfoType  *Info;        //与弧相关的信息的指针,可省略
}ArcCell, AdjMatrix[max_n][max_n];

//  图的类型
typedef struct{
VertexType vexs[max_n];     // 顶点向量
AdjMatrix  arcs;            // 邻接矩阵
int        vexnum, arcnum;  // 顶点数,边数
GraphKind  kind;            // 图类型
}MGraph;

/* ---------------------------------------------------------------- */


  4)【具体问题具体分析】根据”TSP“旅行商问题的需求,将该无向图的邻接矩阵简化如下:

/* 简化的数据结构 */

#define max_vexNum 26    // 最大城市个数
#define MAX_PATH_LENGTH 9999999
typedef struct{
int vex_num, arc_num;                   // 顶点数 边数
char vexs[max_vexNum];                  // 顶点向量
double arcs[max_vexNum][max_vexNum];    // 邻接矩阵
}Graph;


  5)基于邻接矩阵创建图 - CreateGraph()

输入图的类型Kind,0--有向图,2--无向图;
输入顶点数G.vexnum和边数G.arcnum;
输入n个顶点,填入顶点数组G.vexs;
输入边的偶对(vi,vj)或<vi,vj>, 填写邻接矩阵G.arcs


  6)无向图的邻接矩阵图例



2. 邻接表表示法 - 将每个顶点的邻接点串成一个单链表

  1)邻接表的数据结构

#define  max_n   20                //最大顶点数
typedef struct ArcNode{            //边结点
int            adjvex;        //邻接点的下标
struct ArcNode    *nextarc;     //后继链指针
}ArcNode;

typedef struct VNode{            //顶点结点
VertexType       data;        //顶点数据
ArcNode           *firstarc;  //边链头指针
}VNode, AdjList[max_n];

typedef struct{
AdjList        vertices;       //邻接表
int        vexnum,arcnum;     //顶点数和边数
GraphType    kind;          //图种类标志
}ALGraph;


  2)无向图的邻接表 - 实例:



  3)基于邻接表创建图 - CreateGraph()

输入图的类型Kind,0--有向图,2--无向图;
输入顶点数G.vexnum和边数G.arcnum;
输入n个顶点,填入顶点数组G.vextices;
输入边的偶对(vi,vj)或<vi,vj>, 用头插法生成边链


二、C++实现图的邻接矩阵表示法

1. 数据结构

/* 简化的数据结构 */

#define max_vexNum 26    // 最大城市个数
#define MAX_PATH_LENGTH 9999999
typedef struct{
int vex_num, arc_num;                  // 顶点数 边数
char vexs[max_vexNum];                 // 顶点向量
double arcs[max_vexNum][max_vexNum];   // 邻接矩阵
}Graph;


2. 文件读取的方式,本程序采用文件流的方式进行数据的读写,相关代码如下:

#include <fstream>

ifstream read_in;
read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt");
if (!read_in.is_open())
{
cout<<"文件读取失败."<<endl;
return;
}


3. TSP(旅行商问题)的实验数据:文件名( city_10.txt )

10
A B C D E F G H I J
0 2538.94 2873.8 2575.27 2318.1 2158.71 2216.58 3174.04 3371.13 3540.24
2538.94 0 1073.54 111.288 266.835 395.032 410.118 637.
d323
942 853.554 1055
2873.8 1073.54 0 964.495 988.636 1094.32 1382.73 1240.15 1460.25 1687
2575.27 111.288 964.495 0 262.053 416.707 503.563 624.725 854.916 1068.42
2318.1 266.835 988.636 262.053 0 163.355 395.14 885 1110.86 1318.19
2158.71 395.032 1094.32 416.707 163.355 0 338.634 1030.34 1248.58 1447.69
2216.58 410.118 1382.73 503.563 395.14 338.634 0 984.068 1160.26 1323.7
3174.04 637.942 1240.15 624.725 885 1030.34 984.068 0 243.417 473.768
3371.13 853.554 1460.25 854.916 1110.86 1248.58 1160.26 243.417 0 232.112
3540.24 1055 1687 1068.42 1318.19 1447.69 1323.7 473.768 232.112 0


4. 创建TSP城市的邻接矩阵

void CreateGraph(Graph &G){
ifstream read_in;
read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt");
if (!read_in.is_open())
{
cout<<"文件读取失败."<<endl;
return;
}

read_in >> G.vex_num;
// read_in >> G.arc_num;
G.arc_num = 0;
for (int i = 0;i < G.vex_num; i++)
{
read_in >> G.vexs[i];
}
G.vexs[G.vex_num] = '\0';    // char的结束符.

for (int i = 0; i < G.vex_num;i++)
{
for (int j = 0; j < G.vex_num; j++)
{
read_in >> G.arcs[i][j];

// calculate the arc_num
if (G.arcs[i][j] > 0)
{
G.arc_num++;
}
}
}

// display
cout<<"无向图创建完毕,相关信息如下:"<<endl;
cout<<"【顶点数】 G.vex_num = "<<G.vex_num<<endl;
cout<<"【边数】 G.arc_num = "<<G.arc_num<<endl;
cout<<"【顶点向量】 vexs[max_vexNum] = ";
for (int i = 0; i < G.vex_num; i++)
{
cout<<G.vexs[i]<<" ";
}
cout<<endl<<"【邻接矩阵】 arcs[max_vexNum][max_vexNum] 如下:"<<endl;
for (int i = 0; i < G.vex_num;i++)
{
for (int j = 0; j < G.vex_num; j++)
{
cout << std::right<<setw(10) << G.arcs[i][j]<<" ";
}
cout<<endl;
}
}


5. 实验结果



 

 

 

 

 

 

 

 

 

 

 

 

6. 旅行商问题数据的说明与处理

  1)旅行商问题的数据说明:

-- 目前网上有很多我国城市相关的数据,以坐标点的方式表示,而本程序以及后续的程序都是以邻接矩阵的方式体现,所以现在需要将数据进行转换。

-- 【城市数据】  参考网址:http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/tsp/

-- 【TSP最优解】参考网址:http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/STSP.html

  2)转换程序如下:

// 城市数据格式转化

#define MAX_CITYNUM 150
void CityDataTranslate(){
ifstream read_in;
read_in.open("L:\\Coding\\TSP_SA模拟退火算法\\TSP_SA模拟退火算法\\ch150.txt");        // 待转换数据,数据可从上述链接【城市数据】下载
if (!read_in.is_open())
{
cout<<"文件读取失败."<<endl;
return;
}

ofstream fout("L:\\Coding\\TSP_SA模拟退火算法\\TSP_SA模拟退火算法\\city_150.txt");        // 转换后的数据存入文档 city_150.txt

double city_table[MAX_CITYNUM][MAX_CITYNUM];
int city_No[MAX_CITYNUM];
double city_x[MAX_CITYNUM];
double city_y[MAX_CITYNUM];

int vex_num;
read_in >> vex_num;
fout << vex_num << endl;

for (int i = 0; i < vex_num; i++)
{
read_in >> city_No[i] >> city_x[i] >> city_y[i];

fout << i + 1 <<" ";
}
fout<<endl;

for (int i = 0; i < vex_num; i++)
{
city_table[i][i] = 0;
for (int j = 0; j < vex_num; j++)
{
double temp = (city_x[i] - city_x[j])*(city_x[i] - city_x[j]) + (city_y[i] - city_y[j])*(city_y[i] - city_y[j]);
city_table[i][j] = sqrt(temp);
fout << city_table[i][j]<<" ";
}
fout<<endl;
}
}


--------------------------------------------------------------

【注】博文由本文经过实践进一步整理,如有问题,还望指出,本人会及时纠正!谢谢^^

--------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息