数学建模社区-数学中国

标题: [数据结构与算法]16 什么,图这种数据结构把你难住了?! [打印本页]

作者: 杨利霞    时间: 2020-4-26 15:17
标题: [数据结构与算法]16 什么,图这种数据结构把你难住了?!
[数据结构与算法]16 什么,图这种数据结构把你难住了?!
% x( P+ g; ?- ]你是不是和我一样,在学习数据结构与算法时,了解了一下图这种数据结构之后,根本不知道它的用武之地在哪里?
+ b4 s7 Q( t2 `4 X在我查了资料之后,现在我可以跟你讲讲,图可以这么用!* D0 x9 z0 D& r2 R" I" ^' `
2 u5 |0 |/ Z. G
概念介绍
% K- f2 q  C# x- w' N& }
- L% Y- p9 p5 M4 z先来了解一下什么是图.; Y6 n$ p1 J% b# V/ D
图,是一种非线性表数据结构.
+ j$ O7 x' _) c那么你可能会问了,什么是线性表结构哇,我怎么区别一种数据结构是线性的,还是非线性的呢.
0 s1 j. z! v8 q& B  I/ `哈哈,还好我机智,在这篇文章之前就写了一篇文章来介绍,如果还有疑问,楼上雅座请: [数据结构与算法]14 搞不懂线性结构,非线性结构?
; ?& T7 R& x8 n5 G  W& v7 F; f% c* h
在图中元素叫做顶点( vertex ),图中的一个顶点可以和其他任意顶点建立连接关系,这种建立的关系叫做边( edge )
1 u8 d; S' Q, R2 p+ _6 ^& H! y( D. K  n
无向图8 s( o. r7 b) i7 w: }9 q. i
1.jpg
9 M: O* G1 ^1 F
) _5 K3 h" \5 l上面给出的是无向图.看到这里你可能就觉得比较疑惑,这个无向图看起来没啥呀,怎么会有这种数据结构呢?
) E6 X% T& O" Y& S' F/ O5 J& Y# c不知道你是怎么想的,我刚开始学的时候就有这种疑问,这是什么神仙数据结构哇,还会有应用场景?
  r. i1 Z# h/ ^& B: m5 R0 C( Q4 J& R# i' d+ U4 x' ?! j5 C$ W
既然有疑惑,那就给个应用场景:7 i9 F- E* ?: s  t. G% ^
假设,现在你和我是微信好友,那是不是应该你的好友列表里面有我,我的好友里面有你,这样咱们才是好友对不对~
, h( S% b' D" ?+ X' v% I那在数据库中如何表示呢?吼~这个时候无向图就登场了/ D( C0 F2 N: I! c3 t3 e: z, t
你和我是微信好友,那就在咱俩之间来条线,表示咱俩之间有关系,一条线就解决了问题,真是完美至极啊
) E9 o5 L2 k+ N; h# ?假设,(怎么又是假设,哈哈哈)上图中表示的就是 A,B,C,D,E,F 之间的关系,那你可能就发现问题了,有的顶点线比较多,比如 D 有四条线,有的就相对少一些,比如 B 有两条线.这些线就表示顶点的度( degree ).这个概念有啥用?; H% c  k6 ^$ y, A) A+ R0 b* v; P
能一眼看出来谁的好友多!那这个功能有啥用?(好吧,这个功能好像是有点儿鸡肋,不过也算是一个应用场景6 c9 u+ G/ u! [* X" N1 X* k

8 h/ J! j9 r4 L/ P$ w4 Y. [有向图
( s( D' [' ^) L) D, c# n! ~ 2.jpg - D& Z9 b4 M6 f1 o1 U: J% F
看到上面的无向图,基础不错的小伙伴肯定会说了,我还知道有向图呢!! {) N/ Z% ~9 Z1 ^0 v3 S
呦呵,不错,有向图就是下面这个样子:" a! ]+ A' d( f' Y1 n8 ^) Y
- ^, i2 \8 k* U, b, y( G
在无向图中,咱们知道一个顶点有多少条边,就说它的度为多少.
; T0 m2 }4 c3 p+ n# p在有向图中呢,有指向顶点的,也有从顶点指出去的,基于无向图的概念,咱们把从这个顶点指出去的边称为出度,指向该顶点的边称为入度.0 \3 L5 {9 J' g& a
那么有向图会应用在哪些场景呢?微信好友这个场景是不太可以了1 _& ?; w6 Q  Y1 s
那么微博呢?
+ k* y; d8 ^5 g微博和微信有什么不一样呢?微信是你和我是好友,那么咱们的好友列表里一定是要有彼此的,拉黑或者删除彼此了,那就不能互相发送消息了.4 N9 k9 i! d0 \4 q) z
但是微博呢?你关注了我,并不代表我就要关注你对吧?看到这里有没有一种豁然开朗的感觉~! T+ X# y) i2 S) s. B
那么我关注了多少人就是出度,多少人关注了我就是入度.
4 r$ x- e. ^4 t6 u* a这样带入理解是不是会比较好一点儿?(我可真是个天才,哈哈哈
. s' ~: |* g- W6 G  L# n2 R! s+ S, P% r7 q8 u
带权图" A4 H( z) L5 V3 A# B7 F/ p7 I
3.jpg . M: G' m; Y: v
看完了无向图,有向图,相信就有人说,我还见过带权图!(陈独秀给我坐下!3 g: j0 i7 K' T7 `' W1 @
带权图长啥样呢?就下面这个样子:6 F7 i5 v: v1 t

: s( f/ h4 w. M: s懵逼了,这每条边上的数字是个什么鬼呦
/ \' L- r( V  L: n别急,咱们来个场景:大家都玩 QQ 嘛?(别跟我说不玩,配合一下嘛…
' L3 w9 B2 Y2 K. @" U9 ~  ]玩 QQ 的话,一定知道有 QQ 空间,然后空间里面有个「谁在意我」「我在意谁」的功能,就是下图:: W# [  q8 u' q
: x# {- g0 ~  z3 F& u* l
那么有没有好奇过呢? QQ 怎么知道我在意谁,谁在意我呢?5 I4 O' O! S- Z) t
就是通过带权图哇
& C0 d1 J& P$ K# x2 g/ u  q你访问了一个人的空间,这条边的权重就增加一点儿;别人访问了你的空间,那这条边的权重就增加一点儿;这段时间你们两个人聊天聊得比较频繁,来个小火花,顺便在你们两者之间的边权重增加一点儿.然后根据这些边的权重从大到小排序就得出了「谁在意我」「我在意谁」# V6 {/ Y! i8 o! c7 i
- _" Q, R4 c( X& j* o4 {' @6 i
到这里,上面的一切理解都还 OK ?$ c- m0 t* Y1 Z, e+ v; ^- S
那咱们继续.图是怎么表示的呢?* U& W& e* H, |; u5 E) G% Q
图这种数据结构,再怎么画顶点,画边,到最后在物理结构上是怎么存储的呢?7 C+ C- r; E! j2 v; Q5 W
别急,你所疑惑的,我都帮你想到了1 U2 v9 ~- T5 M* K* C! }
* C* \$ k- p$ m0 a" k
图的存储方法; z! y3 o: J  z  F. J. \& h( r

7 W$ s# I1 |( H/ G3 G图的存储方法主要有以下两种:$ J; s$ P# d" U/ Z* R6 z
: n1 j! E  n" R8 `' `& [( w
邻接矩阵/ `' X7 u* t' T  i" {! }) ?

" f  m+ I0 M- S. h$ t9 r邻接矩阵的底层依赖一个二维数组.对于无向图来说,如果 i 与 j 之间有边,那就将 A[j] 和 A[j] 标记为 1 ;对于无向图来说,如果 i 指向 j ,那么 A[j] 值为 1 ,如果 j 指向 i ,那么 A[j] 值为 1 ;对于带权图来说, A[j] 存储的值就不是 1 了,而是对应的权重值.所以这是图最直观的一种存储方法." u- v# E; Y7 B  n7 L/ R  \. i
啥,你跟我说这还不直观?该不会是没有看下图吧:  x; B% p" F$ M+ q: h" F* _5 |- Z
4.jpg ! L% w9 Z) t1 n6 N
但是你发现问题了嘛,这样看起来确实是直观了很多,但是很浪费空间有没有!比如无向图,如果 A[j] 为 1 ,那么 A[j] 肯定也是 1 ,多存储 A[j] 根本没啥必要.就像买东西,明明一块钱能买到的东西,为啥非要花两块钱?
) H- U0 j: e( Q! {+ ~+ h所以如果使用邻接矩阵来表示的话,一定要清楚它的缺点.
7 `8 P9 @# x; T5 l1 M: v4 |* Y7 f& a; v
但这并不是说,使用邻接矩阵来存储就没啥优点.这天底下哪儿有那么绝对的事情呢.
1 e) g/ @1 |& F5 ~7 o# c首先,邻接矩阵的存储方式简单,直接,所以当我们需要获取两个顶点之间的关系时,相信我没有比这种存储结构更高效的了.' Y% U: e. v7 V, U/ |
还有就是使用邻接矩阵存储图的另外一个优点就是方便计算,因为可以将很多图的运算转换成矩阵之间的运算., K+ g9 Z* `, C' ~  @

& D+ t$ V0 \9 C- R& h' v邻接表
  n  m& U% @+ z- z6 ^  J3 ~) g& Q( b9 P3 Y. y  j, ^  \
先来看图:: H+ [/ e% G) p' {. y* p, P& L
5.jpg 0 S1 g' h. k% R: q
, v9 a, L+ ]3 v; K. q7 D
乍一看,这不是散列表嘛!每个顶点对应一条链表,链表中存储的是与这个顶点相连接的其他顶点.
7 g+ f+ h7 g  q4 I# _- f) K嘿嘿,直觉超棒!
1 R6 }/ d0 Q3 G' O/ a
, I' y" t6 I4 J, W' R+ ?$ X如果你对散列表熟悉的话,应该知道,在散列表中,如果链太长了,会导致冲突概率增大,复杂度也蹭的一下升高.而且吧,链表的存储方式你也知道,不是连续的,所以相对于数组来说, CPU 读取就会慢一些,相对于邻接矩阵的存储方式,在邻接表中查询两个顶点之间的关系就没那么高效了., ^8 v3 x8 O  w
所以在实际开发中要注意遇到这种情况该如何处理,或者在刚开始的时候就直接设计好实现方式.比如可以将邻接表中的链表改为平衡二叉树,或者红黑树.
2 I( v5 D/ L3 Y) l( {
0 Y. S6 _0 c) D4 e/ P! P" u2 R我觉得对于数据结构来说,没有最好的,只有最合适的~) D7 `! B& {* G; t3 w
9 o2 H# |7 \! i+ ^, |# W$ f
参考
" K5 g; d7 g' ^
2 r$ u3 w5 O" Y/ {( Z0 G极客时间—<数据结构与算法之美>
/ c& t! f' T# h————————————————
7 g; A. X/ J/ n) w3 W8 a版权声明:本文为CSDN博主「郑璐璐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 |7 `4 I6 J/ B( \5 L6 k原文链接:https://blog.csdn.net/zll_0405/article/details/105209800
/ X+ X1 J8 e1 P  o5 @4 t5 K5 V% [/ I# A9 ^* ~

6 I, i2 u; q- Q4 T




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5