检查仅 S 和 T 重复的无向图中节点 S 和 T 之间是否存在循环 |套装 – 2
给定一个有N个节点和两个顶点S & T的无向图,任务是检查这两个顶点之间是否存在循环(并返回它),使得除了 S和T之外的其他节点不会出现超过一次循环。
例子:
Input: N = 7, edges[][] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 2}, {2, 6}, {6, 0}}, S = 0, T = 4
Output: No simple cycle from S to T exists
Explanation: No simple cycle from S to T exists,
because node 2 appears two times, in the only cycle that exists between 0 & 4
Input: N = 7, edges[][] = {{0, 1}, {1, 2}, {1, 6}, {2, 3}, {3, 4}, {4, 5}, {5, 2}, {2, 6}, {6, 0}},, S = 0, T = 4
Output: 0->1->3->4->5->2->6->0
Explanation: The cycle doesn’t repeat any node (except 0)
朴素方法:该问题的朴素方法在该问题的Set-1中进行了讨论。
有效的方法:在幼稚的方法中,检查所有可能的路径。这种方法的想法类似于使用Edmonds-Karp实现的 Ford Fulkerson 算法,但只有2 个 BFS 。请按照以下步骤解决问题
- 首先,通过复制接收者和发送者中的每个节点(除了S和T )来制作有向图:
- 如果原始图有边:{a, b},则新图将有 {sender_a, receiver_b}
- 唯一指向发送者的节点是他的接收者,所以以 sender_v 结尾的唯一边是: { receiver_v, sender_v }
- 接收者只指向他的发送者,所以receiver_v的邻接表为:[sender_v]
- 运行 BFS以找到从 S 到 T 的路径,并记住返回的路径(使用前驱数组)。
- 反转找到的路径中的边,这一步类似于福特富尔克森中增广路径的更新。
- 在反转时记住路径中从一个节点到另一个节点的流
- 所以,如果cur的前一个节点是pred,那么flow[cur] = pred
- 最后,记住最后一个节点(在节点t之前),我们称之为:first_node(因为它是flow_path中t之后的第一个节点),first_node = flow[t]
- 再次运行 BFS以找到第二条路径,并记住返回的路径(使用前驱数组)。
- 再次记忆第二条路径的流程:
- 只有在没有相反方向的先前流时才标记流,这样两个相反的流将被丢弃。因此,如果 flow[pred] == cur 不要这样做: flow[cur] = pred
- 如果 cur 的前一个节点在路径中 pred,则 flow[cur] = pred
- 最后,加入路径:
- 通过流遍历两条路径,一条从 first_node 开始,另一条从 flow[t]
- 由于我们有 2 条从 t 到 s 的路径,通过还原其中一条,我们将有一条从 s 到 t 的路径,以及另一条从 t 到 s 的路径。
- 从 s 到 t 遍历一条路径,从 t 到 s 遍历另一条路径。
所有这些复制图和注册流程的工作都是为了确保不会遍历同一个节点两次。
下面是上述方法的实现:
Python3
# Python program for the above approach
# Auxiliary data struct for the BFS:
class Node:
def __init__(self, val):
self.val = val
self.next = None
class queue:
def __init__(self):
self.head = None
self.tail = None
def empty(self):
return self.head == None
def push(self, val):
if self.head is None:
self.head = Node(val)
self.tail = self.head
else:
self.tail.next = Node(val)
self.tail = self.tail.next
def pop(self):
returned = self.head.val
self.head = self.head.next
return returned
# BFS to find the paths
def bfs(graph, s, t):
# Number of nodes in original graph
N = len(graph)//2
Q = queue()
Q.push(s)
predecessor = list(-1 for _ in range(2 * N))
predecessor[s] = s
while not Q.empty():
cur = Q.pop()
# Add neighbors to the queue
for neighbour in graph[cur]:
# If we reach node we found the path
if neighbour == t or neighbour == t + N:
predecessor[t] = cur
predecessor[t + N] = cur
return predecessor
# Not seen
if predecessor[neighbour] == -1:
Q.push(neighbour)
predecessor[neighbour] = cur
return None
# Invert the path and register flow
def invert_path(graph, predecessor, flow, s, t):
N = len(graph)//2
cur = t
while cur != s:
pred = predecessor[cur]
if flow[pred] != cur:
flow[cur] = pred
# Reverse edge
graph[cur].append(pred)
graph[pred].remove(cur)
cur = pred
# Node S and T are not duplicated
# so we don't reverse the edge s->(s + N)
# because it shouldn't exist
graph[s].append(s + N)
return flow
# Return the path by the flow
def flow_path(flow, first_node, s):
path = []
cur = first_node
while cur != s:
path.append(cur)
cur = flow[cur]
return path
# Function to get the cyclle with 2 nodes
def cycleWith2Nodes(graph, s = 0, t = 1):
# Number of nodes in the graph
N = len(graph)
# Duplicate nodes:
# Adjacency list of sender nodes
graph += list(graph[node] for node in range(N))
# Adjacency list of receiver nodes
graph[:N] = list([node + N] for node in range(N))
# print('duplicated graph:', graph, '\n')
# Find a path from s to t
predecessor = bfs(graph, s, t)
if predecessor is not None:
# List to memorize the flow
# flow from node v is:
# flow[v], which gives the node who
# receives the flow
flow = list(-1 for _ in range(2 * N))
flow = invert_path(graph, predecessor, flow, s, t)
first_node = flow[t]
else:
print("No cycle")
return
# Find second path
predecessor = bfs(graph, s, t)
if predecessor is not None:
flow = invert_path(graph, predecessor, flow, s, t)
# Combine both paths:
# From T to S
path1 = flow_path(flow, first_node, s)
path2 = flow_path(flow, flow[t], s)
# Reverse the second path
# so we will have another path
# but from s to t
path2.reverse()
simpleCycle = [s]+path2+[t]+path1+[s]
print(simpleCycle[::2])
else:
print("No cycle")
# Driver Code
if __name__ == "__main__":
graph = [
[1, 6], # 0
[0, 2, 3], # 1
[1, 3, 5, 6], # 2
[1, 2, 4], # 3
[3, 5], # 4
[2, 4], # 5
[0, 2], # 6
]
cycleWith2Nodes(graph, s = 0, t = 4)
[0, 6, 2, 5, 4, 3, 1, 0]
复杂度分析:下面给出每个函数的复杂度
- 创建重复图:O(V+E)
- 2 BFS: O(V+E)
- 2 次反转(注册流程):O(路径大小)<= O(V+E)
- 从流数组重新创建路径:O(path size) <= O(V+E)
- 反向一条路径:O(path size) <= O(V+E)
时间复杂度:O(V+E)
辅助空间:O(N*N),其中 N 是图中的顶点数。