博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图基本算法 图的表示方法 邻接矩阵 邻接表
阅读量:4221 次
发布时间:2019-05-26

本文共 5893 字,大约阅读时间需要 19 分钟。

要表示一个图G=(V,E),有两种标准的表示方法,即邻接表和邻接矩阵。这两种表示法既可用于有向图,也可用于无向图。通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑。但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示。

  图G=<V,E>的邻接表表示是由一个包含|V|个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点。对于每一个u∈V,邻接表Adj[u]包含所有满足条件(u,v)∈E的顶点v。亦即,Adj[u]包含图G中所有和顶点u相邻的顶点。每个邻接表中的顶点一般以任意顺序存储。

  如果G是一个有向图,则所有邻接表的长度之和为|E|,这是因为一条形如(u,v)的边是通过让v出现在Adj[u]中来表示的。如果G是一个无向图,则所有邻接表的长度之和为2|E|,因为如果(u,v)是一条无向边,那么u会出现在v的邻接表中,反之亦然。邻接表需要的存储空间为O(V+E)。

  邻接表稍作变动,即可用来表示加权图,即每条边都有着相应权值的图,权值通常由加权函数w:E→R给出。例如,设G=<V,E>是一个加权函数为w的加权图。对每一条边(u,v)∈E,权值w(u,v)和顶点v一起存储在u的邻接表中。

邻接表C++实现:

 

复制代码

1 #include 
2 #include
3 using namespace std; 4 5 #define maxn 100 //最大顶点个数 6 int n, m; //顶点数,边数 7 8 struct arcnode //边结点 9 { 10 int vertex; //与表头结点相邻的顶点编号 11 int weight = 0; //连接两顶点的边的权值 12 arcnode * next; //指向下一相邻接点 13 arcnode() {} 14 arcnode(int v,int w):vertex(v),weight(w),next(NULL) {} 15 arcnode(int v):vertex(v),next(NULL) {} 16 }; 17 18 struct vernode //顶点结点,为每一条邻接表的表头结点 19 { 20 int vex; //当前定点编号 21 arcnode * firarc; //与该顶点相连的第一个顶点组成的边 22 }Ver[maxn]; 23 24 void Init() //建立图的邻接表需要先初始化,建立顶点结点 25 { 26 for(int i = 1; i <= n; i++) 27 { 28 Ver[i].vex = i; 29 Ver[i].firarc = NULL; 30 } 31 } 32 33 void Insert(int a, int b, int w) //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边 34 { 35 arcnode * q = new arcnode(b, w); 36 if(Ver[a].firarc == NULL) 37 Ver[a].firarc = q; 38 else 39 { 40 arcnode * p = Ver[a].firarc; 41 if(p->vertex == b) //如果不要去重边,去掉这一段 42 { 43 if(p->weight < w) 44 p->weight = w; 45 return ; 46 } 47 while(p->next != NULL) 48 { 49 if(p->next->vertex == b) //如果不要去重边,去掉这一段 50 { 51 if(p->next->weight < w); 52 p->next->weight = w; 53 return ; 54 } 55 p = p->next; 56 } 57 p->next = q; 58 } 59 } 60 void Insert2(int a, int b, int w) //头插法,效率更高,但不能去重边 61 { 62 arcnode * q = new arcnode(b, w); 63 if(Ver[a].firarc == NULL) 64 Ver[a].firarc = q; 65 else 66 { 67 arcnode * p = Ver[a].firarc; 68 q->next = p; 69 Ver[a].firarc = q; 70 } 71 } 72 73 void Insert(int a, int b) //尾插法,插入以a为起点,b为终点,无权的边,效率不如头插,但是可以去重边 74 { 75 arcnode * q = new arcnode(b); 76 if(Ver[a].firarc == NULL) 77 Ver[a].firarc = q; 78 else 79 { 80 arcnode * p = Ver[a].firarc; 81 if(p->vertex == b) return; //去重边,如果不要去重边,去掉这一句 82 while(p->next != NULL) 83 { 84 if(p->next->vertex == b) //去重边,如果不要去重边,去掉这一句 85 return; 86 p = p->next; 87 } 88 p->next = q; 89 } 90 } 91 void Insert2(int a, int b) //头插法,效率跟高,但不能去重边 92 { 93 arcnode * q = new arcnode(b); 94 if(Ver[a].firarc == NULL) 95 Ver[a].firarc = q; 96 else 97 { 98 arcnode * p = Ver[a].firarc; 99 q->next = p;100 Ver[a].firarc = q;101 }102 }103 void Delete(int a, int b) //删除以a为起点,b为终点的边104 {105 arcnode * p = Ver[a].firarc;106 if(p->vertex == b)107 {108 Ver[a].firarc = p->next;109 delete p;110 return ;111 }112 while(p->next != NULL)113 if(p->next->vertex == b)114 {115 p->next = p->next->next;116 delete p->next;117 return ;118 }119 }120 121 void Show() //打印图的邻接表(有权值)122 {123 for(int i = 1; i <= n; i++)124 {125 cout << Ver[i].vex;126 arcnode * p = Ver[i].firarc;127 while(p != NULL)128 {129 cout << "->(" << p->vertex << "," << p->weight << ")";130 p = p->next;131 }132 cout << "->NULL" << endl;133 }134 }135 136 void Show2() //打印图的邻接表(无权值)137 {138 for(int i = 1; i <= n; i++)139 {140 cout << Ver[i].vex;141 arcnode * p = Ver[i].firarc;142 while(p != NULL)143 {144 cout << "->" << p->vertex;145 p = p->next;146 }147 cout << "->NULL" << endl;148 }149 }150 int main()151 {152 int a, b, w;153 cout << "Enter n and m:";154 cin >> n >> m;155 Init();156 while(m--)157 {158 cin >> a >> b >> w; //输入起点、终点159 Insert(a, b, w); //插入操作160 Insert(b, a, w); //如果是无向图还需要反向插入161 }162 Show();163 return 0;164 }

复制代码

 

  

  邻接表表示法也有潜在的不足之处,即如果要确定图中边(u,v)是否存在,只能在顶点u邻接表Adj[u]中搜索v,除此之外没有其他更快的办法。这一不足可通过图的邻接矩阵表示法来弥补,但要(在渐进意义下)以占用更多的存储空间为代价。

  在图G=(V,E)的临界矩阵表示法中,假定各顶点按某种任意的方式编号为1,2,···,|V|,那么G的邻接矩阵为一个|V|*|V|的矩阵A=(a[i][j]),它满足:

  观察无向图的邻接矩阵会发现,它是沿主对角线对称的。在一个无向图中,(u,v)和(v,u)表示同一条边,故无向图的邻接矩阵A的转置矩阵就是它本真。在某些应用中,可以只存储邻接矩阵的对角线以及对角线以上的部分,这样一来,图所占用的存储空间几乎可以减少一半。

  邻接矩阵也可以用来表示加权图。例如,如果G=<V,E>是一个加权图,其权值函数为w,对于边(u,v)∈E,其权值w(u,v)就可以简单地存储在邻接矩阵的第u行第v列的元素中。如果边不存在,则可以在矩阵的相应元素中存一个NIL值,在很多问题中,对这样的元素赋0或∞会更为方便些。

邻接矩阵C++实现:

复制代码

1 #include 
2 #include
3 using namespace std; 4 5 #define maxn 100 6 #define INF 1xffffff //预定于的最大值 7 int n, m; //顶点数、边数 8 int g[maxn][maxn]; //邻接矩阵表示 9 10 void Init()11 {12 for(int i = 1; i <= n; i++)13 for(int j = 1; j <= n; j++)14 g[i][j] = 0; //讲所有顶点度数置零,若为带权图,则置为INF15 }16 void Show() //打印邻接矩阵17 {18 for(int i = 1; i <= n; i++)19 {20 for(int j = 1; j <= n; j++)21 cout << g[i][j] << " ";22 cout << endl;23 }24 }25 int main()26 {27 int a, b;28 cout << "Enter n and m:";29 cin >> n >> m;30 while(m--)31 {32 cin >> a >> b; //输入为边的始点、终点,若有权,还需输入权w33 g[a][b] = 1; //a、b间存在边,将g[a][b]置1,若有权,则将其置为权值34 g[b][a] = 1; //对于无向图,还要插入边(b,a)35 }36 Show();37 return 0;38 }

复制代码

(文章以及相关代码参考算法导论编写)

转载地址:http://zoemi.baihongyu.com/

你可能感兴趣的文章
视频 | 我想跟AI打一架,用人类的方式
查看>>
18岁华裔少年颠覆量子加速优势,推动量子算法经典化
查看>>
加拿大首个人工智能本科专业来了,落户于多伦多大学
查看>>
为什么说“开源”已经失败:让穷人越来越穷,富人越来越富!
查看>>
技术讲解概率机器学习——深度学习革命之后 AI 道路
查看>>
年薪30~60万,机器学习算法工程师必备能力项
查看>>
2018全球人工智能突破性技术TOP10
查看>>
找一份高薪的AI工作有多难?
查看>>
AI又成中国名片!杭州8分钟展示阿里无人车,马云压轴广发英雄帖
查看>>
从统计到概率,入门者都能用Python试验的机器学习基础
查看>>
手慢无 | 第二届全国高校大数据人工智能师资实战免费培训班(2期)火热报名中!...
查看>>
马云老师,你好!
查看>>
关于人工智能,看看青年AI科学家们最关心什么?
查看>>
目前最流行的15个机器学习框架,你知道几个?
查看>>
2018诺贝尔经济学奖得主,一名62岁的Python教徒
查看>>
从代码恐惧到开发大牛:开发者“10倍提升”宝典
查看>>
吴恩达机器学习课程:完全用Python完成,可以的!(附代码)
查看>>
狂破11项记录,谷歌年度最强NLP论文到底强在哪里?
查看>>
AI“重造”麻省理工学院!今宣布投资10亿美元成立全新计算学院,近70年来最大结构调整...
查看>>
波音公司探索飞行AI,模拟人类大脑制造芯片!
查看>>