Skip to content

Commit fdab90a

Browse files
committed
graph
1 parent b511e72 commit fdab90a

File tree

7 files changed

+488
-0
lines changed

7 files changed

+488
-0
lines changed

data-structures/graph/README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
##
2+
3+
[图的概念](http://data.biancheng.net/view/36.html)
4+
5+
[图的存储](https://blog.csdn.net/qq_22238021/article/details/78281939)
6+
7+
####
8+
9+
带权的图就是网
10+
11+
### 图的存储结构
12+
13+
#### 邻接矩阵(顺序存储)
14+
15+
1. 顶点数组:存储顶点信息
16+
2. 边数组:存储顶点数组中2个顶点关系和权
17+
3. 图{顶点数组,边数组,顶点数,边数,图类型}
18+
19+
不适合:查找顶点的度,需要扫描整张边数组,效率低,对顶点的相关操作
20+
适合:对边依次进行处理的操作,
21+
22+
#### 邻接表(链式存储)
23+
24+
1. 表头结点:存储顶点信息(data)和第一个邻接点地址(firstarc),一般顺序存储在**一个数组中**
25+
2. 表中结点:存储表头结点在数组中的位置和下一个结点的指针,可存储权值
26+
3. 图{表头结点数组,顶点数,边数,图类型}
27+
28+
特点:
29+
30+
1. 出度为各自链表中的结点数,入度需要遍历整个表的结点,还有一种方法,求入度,建立一个逆邻接表
31+
2. 稀疏图存储时,比使用邻接矩阵节省空间
32+
33+
#### 十字链表(链式存储)
34+
35+
36+
1. 顶点结点:存储顶点信息(data) 一个弧头结点指针(firstin) 一个弧尾结点指针(firstout)
37+
2. 弧结点:tailvex 和 headvex 分别存储的是弧尾和弧头对应的顶点在数组中的位置下标; hlink 和 tlink 为指针域,分别指向弧头相同的下一个弧和弧尾相同的下一个弧; info 为指针域,存储的是该弧具有的相关信息,例如权值等
38+
3. 图{顶点结点数组,弧数,顶点数}
39+
40+
特点:
41+
1. 存储的是有向图或者有向网
42+
2. 求入度出度方便,入度为弧头的数量,出度为弧尾的数量
43+
3. 程序中构建链表对于每个新初始化的结点采用头插法进行插入
44+
45+
46+
#### 邻接多重表(链式存储)
47+
48+
邻接多重表可以看做是邻接表和十字链表的结合体
49+
50+
使用邻接表解决在无向图中删除某两个结点之间的边的操作时,由于表示边的结点分别处在两个顶点为头结点的链表中,所以需要都找到并删除,操作比较麻烦。处理类似这种操作,使用邻接多重表会更合适。
51+
52+
1. 表结点构成:
53+
54+
mark 为标志域,作用是标记某结点是否已经被操作过,例如在遍历各结点时, mark 域为 0 表示还未遍历;mark 域为 1 表示该结点遍历过;
55+
ivex 和 jvex 分别表示该结点表示的边两端的顶点在数组中的位置下标; ilink 指向下一条与 ivex 相关的边;
56+
jlink 指向下一条与 jvex 相关的边;
57+
info 指向与该边相关的信息。
58+
59+
2. 顶点结点构成:
60+
61+
data 为该顶点的数据域;
62+
firstedge 为指向第一条跟该顶点有关系的边。
63+
64+
#### 总结
65+
66+
1. 邻接表适用于所有的图结构,无论是有向图(网)还是无向图(网),存储结构较为简单,但是在存储一些问题时,例如计算某顶点的度,需要通过遍历的方式自己求得
67+
2. 十字链表适用于有向图(网)的存储,使用该方式存储的有向图,可以很容易计算出顶点的出度和入度,只需要知道对应链表中的结点个数即可
68+
3. 邻接多重表适用于无向图(网)的存储,该方式避免了使用邻接表存储无向图时出现的存储空间浪费的现象,同时相比邻接表存储无向图,更方便了某些边操作(遍历、删除等)的实现
69+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package graph
2+
3+
type DirGraph struct {
4+
graph
5+
}
6+
7+
//有向图
8+
func NewDirected() *DirGraph {
9+
return &DirGraph{
10+
graph{
11+
edgesCount: 0,
12+
edges: make(map[VertexId]map[VertexId]int),
13+
isDirected: true,
14+
},
15+
}
16+
}
17+
18+
func (g *graph) GetPredecessors(vertex VertexId) VerticesIterable {
19+
iterator := func() <-chan VertexId {
20+
ch := make(chan VertexId)
21+
go func() {
22+
if connected, ok := g.edges[vertex]; ok {
23+
for VertexId, _ := range connected {
24+
if g.CheckEdge(VertexId, vertex) {
25+
ch <- VertexId
26+
}
27+
}
28+
}
29+
close(ch)
30+
}()
31+
return ch
32+
}
33+
34+
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
35+
}
36+
37+
func (g *graph) GetSuccessors(vertex VertexId) VerticesIterable {
38+
iterator := func() <-chan VertexId {
39+
ch := make(chan VertexId)
40+
go func() {
41+
if connected, ok := g.edges[vertex]; ok {
42+
for VertexId, _ := range connected {
43+
if g.CheckEdge(vertex, VertexId) {
44+
ch <- VertexId
45+
}
46+
}
47+
}
48+
close(ch)
49+
}()
50+
return ch
51+
}
52+
53+
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
54+
}
55+
56+
func (g *DirGraph) Reverse() *DirGraph {
57+
r := NewDirected()
58+
59+
vertices := g.VerticesIter()
60+
for vertex := range vertices {
61+
r.AddVertex(vertex)
62+
}
63+
64+
edges := g.EdgesIter()
65+
for edge := range edges {
66+
r.AddEdge(edge.To, edge.From, 1)
67+
}
68+
69+
return r
70+
}

data-structures/graph/graph.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package graph
2+
3+
import "errors"
4+
5+
const Infinity int = 65535 //无穷大
6+
7+
//非线程安全
8+
type VertexId uint
9+
10+
type Vertices []VertexId
11+
12+
type Edge struct {
13+
From VertexId
14+
To VertexId
15+
}
16+
17+
type graph struct {
18+
edges map[VertexId]map[VertexId]int
19+
edgesCount int
20+
isDirected bool //定向图
21+
}
22+
23+
type EdgesIterable interface {
24+
EdgesIter() <-chan Edge
25+
}
26+
27+
type VerticesIterable interface {
28+
VerticesIter() <-chan VertexId
29+
}
30+
31+
//边
32+
func (g *graph) EdgesIter() <-chan Edge {
33+
ch := make(chan Edge)
34+
go func() {
35+
for from, connectedVertices := range g.edges {
36+
for to, _ := range connectedVertices {
37+
if g.isDirected { //有向边
38+
ch <- Edge{from, to}
39+
} else {
40+
if from < to { //无向边,只输出一次
41+
ch <- Edge{from, to}
42+
}
43+
}
44+
}
45+
}
46+
close(ch)
47+
}()
48+
return ch
49+
}
50+
51+
//顶点
52+
func (g *graph) VerticesIter() <-chan VertexId {
53+
ch := make(chan VertexId)
54+
go func() {
55+
for vertex, _ := range g.edges {
56+
ch <- vertex
57+
}
58+
close(ch)
59+
}()
60+
return ch
61+
}
62+
63+
//检查顶点是否存在
64+
func (g *graph) CheckVertex(vertex VertexId) bool {
65+
_, exists := g.edges[vertex]
66+
return exists
67+
}
68+
69+
//增加顶点
70+
func (g *graph) AddVertex(vertex VertexId) error {
71+
if !g.CheckVertex(vertex) {
72+
g.edges[vertex] = make(map[VertexId]int)
73+
return nil
74+
} else {
75+
return errors.New("Vertex already exists")
76+
}
77+
}
78+
79+
//删除顶点
80+
func (g *graph) RemoveVertex(vertex VertexId) error {
81+
if !g.CheckVertex(vertex) {
82+
return errors.New("unknow vertex")
83+
}
84+
delete(g.edges, vertex)
85+
for _, connectedVertices := range g.edges {
86+
delete(connectedVertices, vertex)
87+
}
88+
return nil
89+
}
90+
91+
//统计顶点
92+
func (g *graph) VerticesCount() int {
93+
return len(g.edges)
94+
}
95+
96+
//判断边是否存在
97+
func (g *graph) CheckEdge(from, to VertexId) bool {
98+
if _, ok := g.edges[from][to]; ok {
99+
return true
100+
}
101+
return false
102+
}
103+
104+
//增加边,存在就修改权
105+
func (g *graph) AddEdge(from, to VertexId, weight int) error {
106+
//自身循环
107+
if from == to {
108+
return errors.New("cannot add self loop")
109+
}
110+
//不存在边
111+
if !g.CheckVertex(from) || !g.CheckVertex(to) {
112+
return errors.New("vertices not exist")
113+
}
114+
g.edges[from][to] = weight
115+
if !g.isDirected {
116+
g.edges[to][from] = weight
117+
}
118+
g.edgesCount++
119+
return nil
120+
}
121+
122+
//删除边
123+
func (g *graph) RemoveEdge(from, to VertexId) error {
124+
//判断边是否存在
125+
if !g.CheckEdge(from, to) {
126+
return errors.New("edge not exist")
127+
}
128+
//删除
129+
delete(g.edges[from], to)
130+
if !g.isDirected {
131+
delete(g.edges[to], from)
132+
}
133+
g.edgesCount--
134+
return nil
135+
}
136+
137+
//统计边
138+
func (g *graph) EdgesCount() int {
139+
return g.edgesCount
140+
}
141+
142+
//获取边权
143+
func (g *graph) GetEdgeWeight(from, to VertexId) int {
144+
if !g.CheckEdge(from, to) {
145+
return Infinity
146+
}
147+
return g.edges[from][to]
148+
}
149+
150+
//获取邻结点和权
151+
func (g *graph) GetNeighbours(vertex VertexId) VerticesIterable {
152+
iterator := func() <-chan VertexId {
153+
ch := make(chan VertexId)
154+
go func() {
155+
if connected, ok := g.edges[vertex]; ok {
156+
for vid, _ := range connected {
157+
ch <- vid
158+
}
159+
}
160+
close(ch)
161+
}()
162+
return ch
163+
}
164+
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
165+
}
166+
167+
//帮助获取
168+
type VerticesIterableHelp struct {
169+
iter func() <-chan VertexId
170+
}
171+
172+
func (v *VerticesIterableHelp) VerticesIter() <-chan VertexId {
173+
return v.iter()
174+
}

data-structures/graph/graph_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package graph
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package graph
2+
3+
//无向图
4+
5+
type UnGraph struct {
6+
graph
7+
}
8+
9+
func NewUndirected() *UnGraph {
10+
return &UnGraph{
11+
graph{
12+
edgesCount: 0,
13+
edges: make(map[VertexId]map[VertexId]int),
14+
isDirected: false,
15+
},
16+
}
17+
}

0 commit comments

Comments
 (0)