निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान
अनिर्देशित ग्राफ में अभिभावक रिकॉर्ड रखकर और निर्देशित ग्राफ में DFS रंग-चिह्नन से चक्र पहचानिए; देखी गई तीन अवस्थाएँ सफ़ेद, धूसर और काली हैं।
निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान, CoddyKit पर DSA Interview Prep का एक निःशुल्क पाठ है। यह 4 में से 4वाँ पाठ है। इस अध्ययन पथ के 3 तक कोई भी पाठ पूरा पढ़ना निःशुल्क है — इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ व्यावहारिक अभ्यास भी उपलब्ध कराता है। यह DSA Interview Prep सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। DSA Interview Prep पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
चक्र की पहचान क्यों महत्वपूर्ण है
ग्राफ़ में चक्र ऐसा पथ है जो एक ही नोड से शुरू होकर उसी नोड पर समाप्त होता है। अनेक एल्गोरिदम में चक्र की पहचान महत्वपूर्ण है: चक्रीय ग्राफ़ पर टोपोलॉजिकल क्रम विफल हो जाता है, निर्भरता समाधान को परिपत्र निर्भरताओं की पहचान करनी होती है, और OS शेड्यूलिंग में गतिरोध की पहचान के लिए संसाधन-वितरण ग्राफ़ में चक्र ढूँढ़ने पड़ते हैं। अनिर्देशित और निर्देशित ग्राफ़ के लिए तरीका अलग होता है — इनके लिए मूल रूप से अलग एल्गोरिदम आवश्यक हैं।
from collections import defaultdict
# Undirected cycle: A-B-C-A (triangle)
undirected = defaultdict(list)
for u, v in [('A','B'),('B','C'),('C','A')]:
undirected[u].append(v)
undirected[v].append(u)
# Directed cycle: A->B->C->A
directed = defaultdict(list)
for u, v in [('A','B'),('B','C'),('C','A')]:
directed[u].append(v) # one direction only
# Key difference:
# Undirected: edge A-B appears as both A->B and B->A
# Must track parent to distinguish cycle from back-edge to parent
print('Undirected and directed cycles need different detection')DFS द्वारा अनिर्देशित चक्र की पहचान
अनिर्देशित ग्राफ़ में चक्र तब मौजूद होता है जब DFS किसी ऐसे नोड पर पहुँचता है जो केवल देखे गए नोड्स में ही नहीं, बल्कि वर्तमान पथ में भी मौजूद है। चुनौती यह है कि प्रत्येक किनारा दोनों दिशाओं में दिखाई देता है, इसलिए जब हम किसी बाल नोड पर पहुँचते हैं, तो उसकी पड़ोसी सूची में हमारा वर्तमान नोड (मूल नोड) भी शामिल होता है। मूल नोड की ओर लौटने वाले किनारे को गलती से चक्र मानने से बचने के लिए हमें प्रत्येक नोड के मूल नोड का अभिलेख रखना होगा। यदि हमें ऐसा देखा हुआ नोड मिलता है जो हमारा मूल नोड नहीं है, तो चक्र मिल गया है।
def has_cycle_undirected(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
visited = set()
def dfs(node, parent):
visited.add(node)
for nb in graph[node]:
if nb not in visited:
if dfs(nb, node): # recurse with current as parent
return True
elif nb != parent: # visited and not parent = CYCLE
return True
return False
for node in range(n):
if node not in visited:
if dfs(node, -1): # -1 = no parent for root
return True
return False
print(has_cycle_undirected(4, [(0,1),(1,2),(2,3),(3,1)])) # True
print(has_cycle_undirected(3, [(0,1),(1,2)])) # FalseBFS द्वारा अनिर्देशित चक्र
अनिर्देशित ग्राफ़ में BFS द्वारा चक्र की पहचान करते समय भी प्रत्येक देखे गए नोड के मूल नोड का अभिलेख रखा जाता है। किसी नोड के पड़ोसियों को संसाधित करते समय, यदि कोई पड़ोसी पहले से देखा जा चुका है और वर्तमान नोड का मूल नोड नहीं है, तो चक्र मौजूद है। मूल नोड्स को संग्रहीत करने के लिए शब्दकोश इस्तेमाल करें। यह O(V + E) तरीका पुनरावृत्ति सीमा की चिंता से बचाता है और बड़े ग्राफ़ के लिए पसंदीदा पुनरावृत्त विकल्प है।
from collections import deque, defaultdict
def has_cycle_bfs_undirected(n, edges):
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
visited = set()
for start in range(n):
if start in visited:
continue
visited.add(start)
parent = {start: -1}
queue = deque([start])
while queue:
node = queue.popleft()
for nb in graph[node]:
if nb not in visited:
visited.add(nb)
parent[nb] = node
queue.append(nb)
elif parent[node] != nb: # visited and not parent = CYCLE
return True
return False
print(has_cycle_bfs_undirected(4, [(0,1),(1,2),(2,0)])) # Trueनिर्देशित चक्र: मूल नोड का अभिलेख रखना क्यों विफल होता है
निर्देशित ग्राफ़ में केवल मूल नोड का अभिलेख रखना पर्याप्त नहीं है। A→C और B→C पर विचार करें: नोड C के दो 'मूल नोड' हैं, लेकिन कोई चक्र नहीं है। सही तरीका तीन-अवस्था वाले रंग-निर्धारण का उपयोग करता है: सफेद (नहीं देखा गया), धूसर (वर्तमान DFS पथ/स्टैक में), काला (पूरी तरह संसाधित)। यदि DFS के दौरान कभी किसी धूसर नोड पर पहुँचते हैं, तो चक्र मौजूद है — इसका अर्थ है कि वर्तमान पथ में किसी पूर्वज की ओर लौटने वाला किनारा मिल गया है।
# Three-state DFS coloring:
# WHITE (0): not yet visited
# GRAY (1): currently being visited (in DFS stack)
# BLACK (2): fully visited (all descendants processed)
# Why parent fails for directed graphs:
# A -> C (no cycle)
# B -> C (no cycle)
# If we DFS from A, mark C gray
# Then DFS from B finds C is gray -- but this is NOT a cycle!
# C is gray from A's path, not B's path.
# Parent tracking only works when the back-edge goes to the IMMEDIATE parent.
print('Directed graph: use 3-state coloring (white/gray/black)')3-अवस्था वाले DFS द्वारा निर्देशित चक्र की पहचान
मान 0 (सफेद/नहीं देखा गया), 1 (धूसर/स्टैक में) और 2 (काला/पूरा) रखने वाली state[] सरणी इस्तेमाल करें। DFS शुरू करते समय प्रवेश पर नोड को धूसर और बाहर निकलते समय काला चिह्नित करें। यदि DFS कभी धूसर नोड तक पहुँचता है, तो पीछे लौटने वाला किनारा मिला — अर्थात चक्र मौजूद है। यदि वह काले नोड तक पहुँचता है, तो उस पथ की पूरी जाँच हो चुकी है और उसमें कोई चक्र नहीं है, इसलिए उसे छोड़ दें।
def has_cycle_directed(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
state = [0] * n # 0=white, 1=gray, 2=black
def dfs(node):
state[node] = 1 # mark gray (in stack)
for nb in graph[node]:
if state[nb] == 1: # gray = back edge = CYCLE
return True
if state[nb] == 0: # white = unvisited
if dfs(nb):
return True
state[node] = 2 # mark black (fully processed)
return False
for node in range(n):
if state[node] == 0:
if dfs(node):
return True
return False
print(has_cycle_directed(4, [(0,1),(1,2),(2,0),(2,3)])) # True (0->1->2->0)
print(has_cycle_directed(3, [(0,1),(1,2)])) # Falseपाठ्यक्रम अनुसूची: DAG में चक्र
पाठ्यक्रम अनुसूची (LeetCode #207) में पूछा जाता है कि क्या दी गई पूर्वापेक्षाओं के आधार पर सभी पाठ्यक्रम पूरे किए जा सकते हैं। पाठ्यक्रमों को नोड्स और पूर्वापेक्षाओं को निर्देशित किनारों के रूप में मॉडल करें। सभी पाठ्यक्रम तभी पूरे किए जा सकते हैं जब ग्राफ़ एक DAG हो (जिसमें कोई चक्र न हो)। 3-अवस्था वाले DFS से चक्र की पहचान करें — यदि चक्र मिले, तो असत्य लौटाएँ; अन्यथा सत्य लौटाएँ।
from collections import defaultdict
def can_finish(num_courses, prerequisites):
graph = defaultdict(list)
for a, b in prerequisites:
graph[b].append(a) # b is prerequisite for a: b -> a
state = [0] * num_courses
def dfs(course):
if state[course] == 1: return False # cycle!
if state[course] == 2: return True # already verified
state[course] = 1 # mark as in-progress
for next_course in graph[course]:
if not dfs(next_course):
return False
state[course] = 2 # mark as done
return True
return all(dfs(i) for i in range(num_courses) if state[i] == 0)
print(can_finish(2, [[1,0]])) # True: take 0 then 1
print(can_finish(2, [[1,0],[0,1]])) # False: circular dependencyकान के एल्गोरिदम (BFS) से चक्र की पहचान
निर्देशित ग्राफ़ में चक्र की पहचान का एक वैकल्पिक तरीका कान का BFS टोपोलॉजिकल क्रम इस्तेमाल करता है। सभी नोड्स की आने वाली धाराओं की संख्या गिनें। जिन नोड्स की आने वाली धाराओं की संख्या 0 है, उन्हें एक कतार में डालें। प्रत्येक को संसाधित करें: पड़ोसी नोड्स की आने वाली धाराओं की संख्या घटाएँ और जिनकी संख्या 0 हो जाए, उन्हें कतार में डालें। यदि संसाधित नोड्स की संख्या V के बराबर है, तो कोई चक्र नहीं है; अन्यथा चक्र मौजूद है (असंसाधित नोड्स चक्र बनाते हैं)। यह O(V + E) तरीका 3-अवस्था वाले DFS की तुलना में अधिक सहज और याद रखने में आसान है।
from collections import defaultdict, deque
def has_cycle_kahn(n, edges):
graph = defaultdict(list)
in_degree = [0] * n
for u, v in edges:
graph[u].append(v)
in_degree[v] += 1
# Start with all zero in-degree nodes
queue = deque(i for i in range(n) if in_degree[i] == 0)
processed = 0
while queue:
node = queue.popleft()
processed += 1
for nb in graph[node]:
in_degree[nb] -= 1
if in_degree[nb] == 0:
queue.append(nb)
return processed != n # if not all processed, cycle exists
print(has_cycle_kahn(4, [(0,1),(1,2),(2,0),(2,3)])) # True
print(has_cycle_kahn(3, [(0,1),(1,2)])) # Falseचक्र ढूँढ़ना: चक्र वाले नोड्स एकत्र करना
कभी-कभी आपको केवल चक्र की मौजूदगी की पहचान नहीं करनी होती, बल्कि यह भी पता लगाना होता है कि कौन-से नोड चक्र का हिस्सा हैं। 3-अवस्था वाले DFS के दौरान जब पीछे लौटने वाला किनारा मिले, तो कॉल स्टैक (या पथ स्टैक) में पीछे जाकर पूर्वज और वर्तमान नोड के बीच के सभी नोड्स एकत्र करें। अवस्था वाली सरणी के साथ रखा गया पथ स्टैक वर्तमान DFS पथ को दर्ज करता है और O(चक्र_की_लंबाई) में चक्र का पुनर्निर्माण संभव बनाता है।
def find_cycle_nodes(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
state = [0] * n
path = [] # current DFS path
cycle = []
def dfs(node):
state[node] = 1
path.append(node)
for nb in graph[node]:
if state[nb] == 1: # back edge -> found cycle
start = path.index(nb)
cycle.extend(path[start:])
return True
if state[nb] == 0 and dfs(nb):
return True
path.pop()
state[node] = 2
return False
for i in range(n):
if state[i] == 0 and dfs(i):
break
return cycle
print(find_cycle_nodes(4, [(0,1),(1,2),(2,0),(2,3)])) # [0, 1, 2]अंततः सुरक्षित अवस्थाएँ ढूँढ़ना
अंततः सुरक्षित अवस्थाएँ ढूँढ़ना (LeetCode #802) में पूछा जाता है कि कौन-से नोड अंततः किसी अंतिम नोड (जिससे कोई बाहर जाने वाला किनारा न हो) तक पहुँचते हैं और चक्र में फँसते नहीं हैं। कोई नोड 'सुरक्षित' तब है जब उससे निकलने वाले सभी पथ अंतिम नोड्स तक पहुँचते हैं। 3-अवस्था वाला DFS इस्तेमाल करें: काले नोड (जिनकी पूरी जाँच चक्र पाए बिना हो चुकी है) सुरक्षित होते हैं। जो नोड चक्र का हिस्सा हैं या चक्र की ओर ले जाते हैं, वे सुरक्षित नहीं हैं।
def eventual_safe_nodes(graph):
n = len(graph)
state = [0] * n # 0=unvisited, 1=visiting, 2=safe
def dfs(node):
if state[node] == 1: # currently visiting = cycle
return False
if state[node] == 2: # already verified safe
return True
state[node] = 1 # mark as visiting
for nb in graph[node]:
if not dfs(nb):
return False # leads to cycle, not safe
state[node] = 2 # mark as safe
return True
return [i for i in range(n) if dfs(i)]
# [[1,2],[2,3],[5],[0],[5],[],[]] means:
# 0->[1,2], 1->[2,3], 2->[5], 3->[0] (cycle!), 4->[5], 5->[], 6->[]
print(eventual_safe_nodes([[1,2],[2,3],[5],[0],[5],[],[]]))
# [2, 4, 5, 6]अनिर्देशित ग्राफ़ में अतिरिक्त किनारा
अतिरिक्त किनारा (LeetCode #684) उस किनारे को ढूँढ़ता है जो पहले से चक्र-रहित अनिर्देशित ग्राफ़ में जोड़ने पर चक्र बनाता है। इसे DFS द्वारा चक्र की पहचान से हल किया जा सकता है, लेकिन सबसे साफ़ समाधान यूनियन-फ़ाइंड (DSU) का उपयोग करता है: किनारों को एक-एक करके संसाधित करें; यदि दोनों अंतिम बिंदु पहले से जुड़े हुए हैं (एक ही घटक में हैं), तो वर्तमान किनारा चक्र बनाता है और यही उत्तर है। DSU प्रत्येक प्रक्रिया के लिए O(alpha(n)) देता है — व्यवहार में O(1)।
def find_redundant_connection(edges):
n = len(edges)
parent = list(range(n + 1))
rank = [0] * (n + 1)
def find(x):
if parent[x] != x:
parent[x] = find(parent[x]) # path compression
return parent[x]
def union(x, y):
px, py = find(x), find(y)
if px == py:
return False # already connected = cycle!
if rank[px] < rank[py]: px, py = py, px
parent[py] = px
if rank[px] == rank[py]: rank[px] += 1
return True
for u, v in edges:
if not union(u, v):
return [u, v] # this edge creates the cycle
return []
print(find_redundant_connection([[1,2],[1,3],[2,3]])) # [2,3]
print(find_redundant_connection([[1,2],[2,3],[3,4],[1,4],[1,5]])) # [1,4]सारांश: चक्र की पहचान की रणनीतियाँ
चक्र की पहचान के साधनों का सारांश: अनिर्देशित ग्राफ़ के लिए मूल नोड का अभिलेख रखने वाला DFS या यूनियन-फ़ाइंड इस्तेमाल करें। निर्देशित ग्राफ़ के लिए 3-अवस्था वाला DFS (सफेद/धूसर/काला) या कान का BFS टोपोलॉजिकल क्रम इस्तेमाल करें। जब आप एक-एक करके किनारे जोड़ रहे हों (ऑनलाइन), तब यूनियन-फ़ाइंड चुनें। जब आपको टोपोलॉजिकल क्रम भी चाहिए हो, तब कान का एल्गोरिदम चुनें। जब आपको चक्र में शामिल विशिष्ट नोड्स पहचानने हों, तब 3-अवस्था वाला DFS चुनें। साक्षात्कार में चक्र की पहचान पर चर्चा करते समय हमेशा निर्देशित और अनिर्देशित ग्राफ़ों के बीच का अंतर स्पष्ट करें।
# Cycle detection summary:
# Graph type | Algorithm | Complexity
# ------------|----------------------|-----------
# Undirected | DFS + parent track | O(V + E)
# Undirected | Union-Find (DSU) | O(E * alpha(V))
# Directed | DFS 3-state (W/G/B) | O(V + E)
# Directed | Kahn's BFS topo sort | O(V + E)
# When to choose:
# Online (edges added one at a time): Union-Find
# Need topological order too: Kahn's BFS
# Need cycle nodes identified: 3-state DFS with path stack
# Simple existence check: any of the above
print('Always clarify directed vs undirected before coding')त्वरित जाँच
इस पाठ में सिखाई गई डेटा संरचनाएँ और एल्गोरिदम — कोडिंग साक्षात्कार की तैयारी — से संबंधित अवधारणाओं की अपनी समझ जाँचें।
पाठ का पुनरावलोकन
इस पाठ में आपने सीखा: मूल नोड का अभिलेख रखने वाले DFS से अनिर्देशित चक्र की पहचान, 3-अवस्था वाले सफेद/धूसर/काले रंग-निर्धारण से निर्देशित चक्र की पहचान, निर्देशित ग्राफ़ के लिए कान का वैकल्पिक BFS, और पाठ्यक्रम अनुसूची, अतिरिक्त किनारा तथा अंततः सुरक्षित अवस्थाओं जैसे अनुप्रयोग। अब हम डायनेमिक प्रोग्रामिंग की बुनियादी बातों में गहराई से जाएँगे।
एआई शिक्षक के साथ Python सीखें — निःशुल्क
अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।
- पाठ्यक्रम
- 30
- पाठ
- 120
अक्सर पूछे जाने वाले प्रश्न
क्या “निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान” पाठ निःशुल्क है?
हाँ — DSA Interview Prep अध्ययन पथ के 3 तक कोई भी पाठ, जिसमें “निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान” भी शामिल है, यहाँ वेब पर पूरा पढ़ना निःशुल्क है। इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ इंटरैक्टिव अभ्यास भी उपलब्ध कराता है। DSA Interview Prep पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
“निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान” में मैं क्या सीखूँगा?
अनिर्देशित ग्राफ में अभिभावक रिकॉर्ड रखकर और निर्देशित ग्राफ में DFS रंग-चिह्नन से चक्र पहचानिए; देखी गई तीन अवस्थाएँ सफ़ेद, धूसर और काली हैं। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ DSA Interview Prep का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।
क्या DSA Interview Prep शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?
पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर DSA Interview Prep शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 4वाँ पाठ है।
“निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान” पाठ पूरा करने में कितना समय लगता है?
CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।
क्या मैं इस DSA Interview Prep पाठ में कोड लिख और चला सकता हूँ?
हाँ। हर DSA Interview Prep पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।
इस पाठ्यक्रम के सभी पाठ
- ग्राफ निरूपण और भ्रमण की तैयारी
- BFS: सबसे छोटा पथ और स्तर-भ्रमण
- DFS: जुड़े घटक और Flood Fill
- निर्देशित और अनिर्देशित ग्राफ में चक्र पहचान