-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
Copy pathcycle.go
112 lines (92 loc) · 2.74 KB
/
cycle.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// cycle.go
// this file handle algorithm that related to cycle in graph
// time complexity: O(V+E) where V is the number of vertices and E is the number of edges in the graph
// space complexity: O(V) where V is the number of vertices in the graph
// reference: https://en.wikipedia.org/wiki/Cycle_(graph_theory)
// [kiarash hajian](https://github.com/kiarash8112)
package graph
func (g *Graph) HasCycle() bool {
//this implimetation referred as 3-color too
all := map[int]struct{}{}
visiting := map[int]struct{}{}
visited := map[int]struct{}{}
for v := range g.edges {
all[v] = struct{}{}
}
for current := range all {
if g.hasCycleHelper(current, all, visiting, visited) {
return true
}
}
return false
}
func (g Graph) hasCycleHelper(v int, all, visiting, visited map[int]struct{}) bool {
delete(all, v)
visiting[v] = struct{}{}
neighbors := g.edges[v]
for v := range neighbors {
if _, ok := visited[v]; ok {
continue
} else if _, ok := visiting[v]; ok {
return true
} else if g.hasCycleHelper(v, all, visiting, visited) {
return true
}
}
delete(visiting, v)
visited[v] = struct{}{}
return false
}
// this function can do HasCycle() job but it is slower
func (g *Graph) FindAllCycles() []Graph {
all := map[int]struct{}{}
visiting := map[int]struct{}{}
visited := map[int]struct{}{}
allCycles := []Graph{}
for v := range g.edges {
all[v] = struct{}{}
}
for current := range all {
foundCycle, parents := g.findAllCyclesHelper(current, all, visiting, visited)
if foundCycle {
foundCycleFromCurrent := false
//this loop remove additional vertex from detected cycle
//using foundCycleFromCurrent bool to make sure after removing vertex we still have cycle
for i := len(parents) - 1; i > 0; i-- {
if parents[i][1] == parents[0][0] {
parents = parents[:i+1]
foundCycleFromCurrent = true
}
}
if foundCycleFromCurrent {
graph := Graph{Directed: true}
for _, edges := range parents {
graph.AddEdge(edges[1], edges[0])
}
allCycles = append(allCycles, graph)
}
}
}
return allCycles
}
func (g Graph) findAllCyclesHelper(current int, all, visiting, visited map[int]struct{}) (bool, [][]int) {
parents := [][]int{}
delete(all, current)
visiting[current] = struct{}{}
neighbors := g.edges[current]
for v := range neighbors {
if _, ok := visited[v]; ok {
continue
} else if _, ok := visiting[v]; ok {
parents = append(parents, []int{v, current})
return true, parents
} else if ok, savedParents := g.findAllCyclesHelper(v, all, visiting, visited); ok {
parents = append(parents, savedParents...)
parents = append(parents, []int{v, current})
return true, parents
}
}
delete(visiting, current)
visited[current] = struct{}{}
return false, parents
}