给定一个已连接的无向图,请查找它是否包含任何循环。
例如,以下图形包含一个循环2–5–10–6–2
:
推荐阅读:
1.使用BFS
当我们从无v
向图中的任何顶点进行广度优先搜索(BFS)时,我们可能会遇到一个交叉边缘,该边缘指向先前发现的顶点,该顶点既不是当前顶点的祖先也不是其后代。每个“交叉边缘”在无向图中定义一个循环。如果交叉边缘x —> y
,则因为y
已经发现,我们从路径v
到y
(或来自y
于v
,因为是无向图),其中v
是BFS的起始顶点。因此,可以说我们有一条v ~~ x ~ y ~~ v
形成循环的路径。(此处,~~
表示路径中的另一个边,并~
表示直接边)。
以下是演示它的C ++,Java和Python程序:
- C ++
- 爪哇
- Python
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
import java.util.*;
// A class to store a graph edge
class Edge
{
int source, dest;
public Edge(int source, int dest)
{
this.source = source;
this.dest = dest;
}
}
// A class to represent a graph object
class Graph
{
// A list of lists to represent an adjacency list
List<List<Integer>> adjList = null;
// Constructor
Graph(List<Edge> edges, int N)
{
adjList = new ArrayList<>();
for (int i = 0; i < N; i++) {
adjList.add(new ArrayList<>());
}
// add edges to the undirected graph
for (Edge edge: edges)
{
int src = edge.source;
int dest = edge.dest;
adjList.get(src).add(dest);
adjList.get(dest).add(src);
}
}
}
// Node to store vertex and its parent info in BFS
class Node
{
int v, parent;
Node(int v, int parent)
{
this.v = v;
this.parent = parent;
}
}
class Main
{
// Perform BFS on the graph starting from vertex `src` and
// return true if a cycle is found in the graph
public static boolean BFS(Graph graph, int src, int N)
{
// to keep track of whether a vertex is discovered or not
boolean[] discovered = new boolean[N];
// mark the source vertex as discovered
discovered[src] = true;
// create a queue for doing BFS and
// enqueue source vertex
Queue<Node> q = new ArrayDeque<>();
q.add(new Node(src, -1));
// loop till queue is empty
while (!q.isEmpty())
{
// dequeue front node and print it
Node node = q.poll();
// do for every edge `v —> u`
for (int u: graph.adjList.get(node.v))
{
if (!discovered[u])
{
// mark it as discovered
discovered[u] = true;
// construct the queue node containing info
// about vertex and enqueue it
q.add(new Node(u, node.v));
}
// `u` is discovered, and `u` is not a parent
else if (u != node.parent)
{
// we found a cross-edge, i.e., the cycle is found
return true;
}
}
}
// no cross-edges were found in the graph
return false;
}
public static void main(String[] args)
{
// List of graph edges as per the above diagram
List<Edge> edges = Arrays.asList(
new Edge(1, 2), new Edge(1, 3), new Edge(1, 4),
new Edge(2, 5), new Edge(2, 6), new Edge(5, 9),
new Edge(5, 10), new Edge(4, 7), new Edge(4, 8),
new Edge(7, 11), new Edge(7, 12), new Edge(6, 10)
// edge `6 —> 10` introduces a cycle in the graph
);
// total number of nodes in the graph
final int N = 13;
// build a graph from the given edges
Graph graph = new Graph(edges, N);
// Perform BFS traversal in connected components of a graph
if (BFS(graph, 1, N)) {
System.out.println("The graph contains a cycle");
}
else {
System.out.println("The graph doesn't contain any cycle");
}
}
}
|
输出:
图形包含一个循环
2.使用DFS
下图包含一个周期8—9—11—12—8
:
当我们从无向图中的任何顶点进行深度优先搜索(DFS)时v
,我们可能会遇到一个后缘,该后缘指向v
DFS树中当前顶点的祖先之一。每个“后沿”在无向图中定义一个循环。如果后边缘是x —> y
,则既然y
是node的祖先x
,我们有一条从y
到的路径x
。因此,可以说我们有一条y ~~ x ~ y
形成循环的路径。(此处,~~
表示路径中的另一个边,并~
表示直接边)。
以下是基于上述思想的C ++,Java和Python实现:
- C ++
- 爪哇
- Python
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
|
import java.util.*;
// A class to store a graph edge
class Edge
{
int source, dest;
public Edge(int source, int dest)
{
this.source = source;
this.dest = dest;
}
}
// A class to represent a graph object
class Graph
{
// A list of lists to represent an adjacency list
List<List<Integer>> adjList = null;
// Constructor
Graph(List<Edge> edges, int N)
{
adjList = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
adjList.add(i, new ArrayList<>());
}
// add edges to the undirected graph
for (Edge edge: edges)
{
int src = edge.source;
int dest = edge.dest;
adjList.get(src).add(dest);
adjList.get(dest).add(src);
}
}
}
class Main
{
// Function to perform DFS traversal on the graph on a graph
public static boolean DFS(Graph graph, int v,
boolean[] discovered, int parent)
{
// mark the current node as discovered
discovered[v] = true;
// do for every edge `v —> w`
for (int w: graph.adjList.get(v))
{
// if `w` is not discovered
if (!discovered[w])
{
if (DFS(graph, w, discovered, v)) {
return true;
}
}
// if `w` is discovered, and `w` is not a parent
else if (w != parent)
{
// we found a back-edge (cycle)
return true;
}
}
// No back-edges were found in the graph
return false;
}
public static void main(String[] args)
{
// List of graph edges as per the above diagram
List<Edge> edges = Arrays.asList(
new Edge(1, 2), new Edge(1, 7),
new Edge(1, 8), new Edge(2, 3),
new Edge(2, 6), new Edge(3, 4),
new Edge(3, 5), new Edge(8, 9),
new Edge(8, 12), new Edge(9, 10),
new Edge(9, 11), new Edge(11, 12)
// edge `11 —> 12` introduces a cycle in the graph
);
// total number of nodes in the graph
final int N = 13;
// build a graph from the given edges
Graph graph = new Graph(edges, N);
// to keep track of whether a vertex is discovered or not
boolean[] discovered = new boolean[N];
// Perform DFS traversal from the first vertex
if (DFS(graph, 1, discovered, -1)) {
System.out.println("The graph contains a cycle");
}
else {
System.out.println("The graph doesn't contain any cycle");
}
}
}
|
输出:
图形包含一个循环
上述解决方案的时间复杂度为O(V + E),其中V
和和E
分别是图形中顶点和边缘的总数。