प्रॉम्प्ट और दायरा
0 से n - 1 तक लेबल किए गए n नोड्स हैं। प्रारंभ में, प्रत्येक नोड अपना स्वयं का कनेक्टेड कॉम्पोनेंट है। इन ऑपरेशन्स के साथ UnionFind को लागू करें:
union(a, b)उन कॉम्पोनेंट्स को मर्ज करता है जिनमेंaऔरbशामिल हैं। यह केवल तभीTrueलौटाता है जब दो
पहले से अलग कॉम्पोनेंट्स वास्तव में मर्ज होते हैं।
connected(a, b)रिपोर्ट करता है कि क्या दोनों नोड्स वर्तमान में एक ही कॉम्पोनेंट से संबंधित हैं।count()कनेक्टेड कॉम्पोनेंट्स की वर्तमान संख्या लौटाता है।
यह संस्करण n = 0 की अनुमति देता है, लेकिन कोई भी ऑपरेशन तर्क एक मान्य लेबल होना चाहिए या IndexError उठाना चाहिए। मूल समस्या केवल कनेक्शन्स जोड़ती है; यह एजेस को नहीं हटाती है, और कॉल्स एक ही थ्रेड से आते हैं। उदाहरण के लिए, n = 6 से शुरू करने और (0, 1), (1, 2), और (3, 4) को मर्ज करने के बाद, तीन कॉम्पोनेंट्स {0, 1, 2}, {3, 4}, और {5} हैं। (2, 4) को मर्ज करने पर दो कॉम्पोनेंट्स बचते हैं। बाद में किया गया union(0, 3) काउंट को फिर से घटाए बिना False लौटाना चाहिए।
यह एक सामान्य सॉफ़्टवेयर-इंजीनियरिंग डेटा-स्ट्रक्चर इंटरव्यू समस्या है। इसका मुख्य उपयोग मामला कई कनेक्टिविटी और कॉम्पोनेंट-काउंट क्वेरीज़ के साथ इंटरलीव्ड कनेक्शन्स की एक इंक्रीमेंटल स्ट्रीम है। यदि सभी एजेस एक साथ आती हैं और कॉलर को एक कॉम्पोनेंट काउंट की आवश्यकता होती है, तो DFS या BFS अक्सर अधिक प्रत्यक्ष होता है; उस अंतर को पहचानना एक मजबूत उत्तर का हिस्सा है।
इंटरव्यूअर क्या मूल्यांकन करता है
पहला संकेत स्टेट का चयन है। Union-Find पूरे ग्राफ़ को बनाए नहीं रखता है। यह प्रत्येक सेट को एक पैरेंट-पॉइंटर ट्री के रूप में दर्शाता है जिसका रूट सेट का प्रतिनिधि होता है और खुद को इंगित करता है। नतीजतन, connected(a, b) हर संग्रहीत एज को ट्रैवर्स करने के बजाय दो रूट्स की तुलना कर सकता है।
दूसरा संकेत यह है कि क्या union केवल रूट्स को जोड़ता है। सीधे parent[a] = b लिखने से एक आंतरिक नोड दूसरे नोड के नीचे जा सकता है और इसके मूल कॉम्पोनेंट के निरूपण को दूषित कर सकता है। सही क्रम root_a और root_b को ढूंढता है, पुष्टि करता है कि वे भिन्न हैं, और छोटे ट्री के रूट को बड़े ट्री के रूट से जोड़ता है। size केवल रूट्स पर ही अर्थपूर्ण होता है और ट्री की वृद्धि को नियंत्रित करता है।
तीसरा संकेत रटे-रटाए टेम्पलेट के बजाय पाथ कम्प्रेशन की व्याख्या है। यह कार्यान्वयन पाथ हाफ़िंग (path halving) का उपयोग करता है: जैसे ही find ऊपर की ओर बढ़ता है, यह वर्तमान नोड के पैरेंट को उसके ग्रैंडपैरेंट में बदल देता है। वह नया पैरेंट अभी भी उसी ट्री में है, इसलिए भविष्य के रास्ते छोटे होते हुए भी कनेक्टिविटी अपरिवर्तित रहती है। इटरेटिव रूप एक लंबे रास्ते पर रिकर्सन-डेप्थ विफलता से भी बचाता है।
अंत में, इंटरव्यूअर काउंट इनवेरिएंट, जटिलता शब्दावली और सत्यापन की जाँच करता है। components, n से शुरू होता है और दो अलग-अलग रूट्स के मर्ज होने के बाद ही घटता है। डुप्लिकेट यूनियन्स और सेल्फ़-यूनियन्स इसे नहीं बदल सकते। union by size और पाथ कम्प्रेशन के साथ मिलकर, ऑपरेशन्स एक क्रम में एमॉर्टाइज़्ड O(α(n)) समय लेते हैं—प्रत्येक कॉल के लिए स्ट्रिक्ट वर्स्ट-केस O(1) नहीं।
उत्तर देने से पहले स्पष्ट करने वाले प्रश्न
- क्या कनेक्शन्स केवल जोड़े जाते हैं, या उन्हें हटाया भी जा सकता है? मानक Union-Find केवल जोड़ने को संभालता है।
मनमाने ढंग से एज डिलीशन के बाद, पैरेंट फ़ॉरेस्ट यह नहीं बता सकता कि क्या अन्य एजेस अभी भी एंडपॉइंट्स को जोड़ती हैं; इसके लिए एक ऑफ़लाइन विधि या अधिक उन्नत डायनामिक-कनेक्टिविटी संरचना की आवश्यकता होती है।
- क्या क्वेरीज़ ऑनलाइन इंटरलीव्ड हैं, या सभी एजेस पहले से ही प्रदान की गई हैं? इंटरलीव्ड union और
कनेक्टिविटी क्वेरीज़ Union-Find के पक्ष में हैं। स्टैटिक ग्राफ़ में एक कॉम्पोनेंट काउंट के लिए, एडजेंसी-लिस्ट DFS/BFS अधिक पारदर्शी है और वास्तविक एजेस को बनाए रखता है।
unionको क्या लौटाना चाहिए? यहाँ यह रिपोर्ट करता है कि क्या कोई मर्ज हुआ था। वह बूलियन सीधे साइकिल
डिटेक्शन का समर्थन करता है और यह सुनिश्चित करता है कि कॉम्पोनेंट काउंट ठीक एक बार बदले।
- अमान्य नोड्स को कैसा व्यवहार करना चाहिए? यह संस्करण
IndexErrorउठाता है। एक कॉन्टेस्ट समाधान गारंटीकृत-मान्य
अनुबंध के तहत वैलिडेशन छोड़ सकता है, लेकिन सार्वजनिक कार्यान्वयन में पायथन के नकारात्मक सूचकांकों (negative indices) को चुपचाप ऐरे के अंत को संदर्भित नहीं करना चाहिए।
- क्या API को कॉम्पोनेंट का आकार रिपोर्ट करना चाहिए या सदस्यों की गणना करनी चाहिए? रूट
sizeलगभग
स्थिर एमॉर्टाइज़्ड समय में आकार का उत्तर दे सकता है। सदस्यों की गणना करने में अभी भी कम से कम आउटपुट आकार की लागत आती है, और यह आधार संरचना सदस्यता सूचियों को बनाए नहीं रखती है।
- क्या कॉल्स समवर्ती (concurrent) हो सकते हैं? बेस कार्यान्वयन
findके अंदरparentको म्यूटेट करता है, इसलिए एक
कनेक्टिविटी क्वेरी भी केवल-पठनीय (read-only) या थ्रेड-सुरक्षित नहीं है। कॉनक्रेन्सी के लिए एक लॉकिंग अनुबंध या एक विशिष्ट समवर्ती Union-Find एल्गोरिदम की आवश्यकता होती है।
30-सेकंड उत्तर फ़्रेमवर्क
"मैं n लंबाई के दो ऐरे रखूँगा: parent[x] पैरेंट को इंगित करता है, और size[root] रूट के ट्री साइज़ को संग्रहीत करता है। प्रारंभ में प्रत्येक नोड अपना स्वयं का पैरेंट होता है और कॉम्पोनेंट काउंट n होता है। find रूट की ओर बढ़ता है और विज़िट किए गए प्रत्येक नोड को उसके ग्रैंडपैरेंट पर इंगित करके पाथ हाफ़िंग करता है। union दोनों रूट्स ढूंढता है; यदि वे बराबर हैं, तो यह False लौटाता है। अन्यथा यह छोटे ट्री के रूट को बड़े ट्री के रूट से जोड़ता है, उनके आकारों को जोड़ता है, कॉम्पोनेंट काउंट घटाता है, और True लौटाता है। दो रूट्स को जोड़ने से साइकिल नहीं बन सकती, और पाथ हाफ़िंग केवल एक सेट के अंदर पॉइंटर्स को बदलता है, इसलिए कनेक्टिविटी सही रहती है। इनिशियलाइज़ेशन O(n) है; बाद के ऑपरेशन्स O(n) स्पेस के साथ एमॉर्टाइज़्ड O(α(n)) हैं।"
चरण-दर-चरण गहन विश्लेषण
एक प्रत्यक्ष प्रतिनिधित्व प्रत्येक नोड को एक कॉम्पोनेंट लेबल प्रदान करता है। connected एक लेबल तुलना है, लेकिन दो कॉम्पोनेंट्स को मर्ज करने के लिए पूरे ऐरे को स्कैन करना और प्रत्येक पुराने लेबल को बदलना आवश्यक होता है, जिससे एक अकेले union की लागत O(n) हो जाती है। एक अन्य सरल दृष्टिकोण पैरेंट-पॉइंटर ट्रीज़ का उपयोग करता है लेकिन उनके आकारों को नियंत्रित किए बिना रूट्स को जोड़ता है। एक प्रतिकूल union क्रम तब एक लंबी श्रृंखला बना सकता है और find को धीमा कर सकता है।
अनुशंसित डिज़ाइन तीन स्टेट्स को बनाए रखता है:
parent[x],xका पैरेंट है, और प्रत्येक ट्री रूटparent[root] == rootको संतुष्ट करता है।size[root]उस रूट के कॉम्पोनेंट की नोड संख्या है; गैर-रूट्स पर पुराने मान कभी नहीं पढ़े जाते हैं।componentsपैरेंट फ़ॉरेस्ट में रूट्स की संख्या के बराबर है।
कार्यान्वयन नीचे दिया गया है। _validate छोटा है और केवल इस क्लास द्वारा उपयोग किया जाता है, इसलिए यह एक अलग यूटिलिटी मॉड्यूल बनने के बजाय अपनी कॉल साइट के पास रहता है।
class UnionFind:
def __init__(self, n: int) -> None:
if n < 0:
raise ValueError("n must be non-negative")
self.parent = list(range(n))
self.size = [1] * n
self.components = n
def _validate(self, x: int) -> None:
if x < 0 or x >= len(self.parent):
raise IndexError("node out of range")
def find(self, x: int) -> int:
self._validate(x)
while x != self.parent[x]:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a: int, b: int) -> bool:
root_a = self.find(a)
root_b = self.find(b)
if root_a == root_b:
return False
if self.size[root_a] < self.size[root_b]:
root_a, root_b = root_b, root_a
self.parent[root_b] = root_a
self.size[root_a] += self.size[root_b]
self.components -= 1
return True
def connected(self, a: int, b: int) -> bool:
return self.find(a) == self.find(b)
def count(self) -> int:
return self.componentsशुद्धता तीन चरणों में सिद्ध होती है। प्रारंभ में, प्रत्येक नोड एक-नोड ट्री का एकमात्र रूट होता है, इसलिए फ़ॉरेस्ट में n ट्रीज़ होते हैं और तीनों इनवेरिएंट्स सही रहते हैं। पाथ हाफ़िंग x के पैरेंट को उसके मूल पैरेंट के पैरेंट में बदल देता है। वह ग्रैंडपैरेंट मूल रूट के उसी रास्ते पर रहता है, इसलिए यह ऑपरेशन कॉम्पोनेंट्स को पार नहीं कर सकता है या find(x) द्वारा लौटाए गए रूट को बदल नहीं सकता है।
एक union केवल दो रूट्स को संशोधित करता है। समान रूट्स का मतलब है कि नोड्स पहले से ही जुड़े हुए हैं, इसलिए कोई स्टेट नहीं बदलती है। विभिन्न रूट्स के लिए, root_b को root_a पर इंगित करना दो ट्रीज़ को एक में मिलाता है और साइकिल नहीं बना सकता क्योंकि वे रूट्स अलग-अलग ट्रीज़ के थे। नया रूट साइज़ पुराने ट्री साइज़ का योग होता है, और रूट काउंट ठीक एक कम हो जाता है। इंडक्शन द्वारा, दो नोड्स तभी जुड़े होते हैं जब find एक ही रूट लौटाता है, और count() हमेशा सही कॉम्पोनेंट काउंट से मेल खाता है।
Union by size यह गारंटी देता है कि जब भी किसी नोड की गहराई इसलिए बढ़ती है क्योंकि उसका पूरा ट्री जुड़ा हुआ है, तो उसके नए कॉम्पोनेंट का आकार कम से कम दोगुना हो जाता है। पाथ कम्प्रेशन के बिना भी, ट्री की ऊँचाई अधिक से अधिक O(log n) होती है। पाथ हाफ़िंग के साथ संयुक्त होने पर, इनिशियलाइज़ेशन के बाद m finds और unions के क्रम की एमॉर्टाइज़्ड सीमा O(m α(n)) होती है। इन्वर्स एकरमैन फ़ंक्शन α बहुत धीमी गति से बढ़ता है। "लगभग स्थिर एमॉर्टाइज़्ड समय" सटीक इंटरव्यू शॉर्टहैंड है; "स्ट्रिक्ट वर्स्ट-केस O(1)" नहीं है। दोनों ऐरे O(n) स्पेस का उपयोग करते हैं, और इटरेटिव find, O(1) ऑक्सिलरी स्टैक स्पेस का उपयोग करता है।
स्टेट को इस ऑपरेशन क्रम से ट्रैक किया जा सकता है:
n = 6 count = 6
union(0, 1) -> True count = 5
union(1, 2) -> True count = 4
union(3, 4) -> True count = 3
connected(0, 2) -> True
connected(0, 4) -> False
union(2, 4) -> True count = 2
union(0, 3) -> False count = 2सत्यापन के लिए एक से अधिक उदाहरणों की आवश्यकता होती है। बिना किसी क्वेरी के n = 0, n = 1 पर एक सेल्फ़-यूनियन, एक डुप्लिकेट यूनियन, एक ब्रिज द्वारा जुड़े दो अलग-अलग कॉम्पोनेंट्स, एक अलग-थलग नोड, विपरीत क्रम में प्रस्तुत यूनियन्स, और अमान्य लेबल्स -1 और n को कवर करें। यादृच्छिक छोटे ग्राफ़ के लिए, एक ऑरेकल के रूप में एक एडजेंसी लिस्ट बनाए रखें। प्रत्येक एज इंसर्शन के बाद, BFS के साथ कनेक्टिविटी और कॉम्पोनेंट काउंट की पुनर्गणना करें और Union-Find के साथ चरण-दर-चरण उनकी तुलना करें। यह डिफरेंशियल टेस्ट सूक्ष्म काउंट और रूट बग्स को पकड़ता है।
यदि सभी m एजेस पहले से ज्ञात हैं और कॉलर एक कॉम्पोनेंट काउंट मांगता है, तो एडजेंसी-लिस्ट DFS/BFS, O(n + m) समय और स्पेस का उपयोग करता है और उद्देश्य को स्पष्ट रूप से बताता है। Union-Find अपना स्थान तब बनाता है जब एजेस वृद्धिशील रूप से आती हैं और क्वेरीज़ यूनियन्स के साथ इंटरलीव होती हैं, या जब क्रुस्कल के एल्गोरिदम को यह परीक्षण करने की आवश्यकता होती है कि क्या कोई अनडायरेक्टेड एज एक साइकिल बनाएगी। ऑपरेशन मॉडल—न कि केवल ग्राफ़ की उपस्थिति—चयन को निर्धारित करता है।
उच्च गुणवत्ता वाला नमूना उत्तर
"मैं पहले पुष्टि करूँगा कि रिश्ते केवल जोड़े जाते हैं, क्वेरीज़ जोड़ के साथ इंटरलीव होती हैं, और union को यह रिपोर्ट करना होगा कि क्या कोई मर्ज हुआ था। वह ऑपरेशन मॉडल Union-Find के अनुकूल है। यदि यह एक स्टैटिक ग्राफ़ पर एक कॉम्पोनेंट काउंट होता, तो मैं इसके बजाय DFS का उपयोग करता।
मेरी स्टेट parent, रूट्स पर size, और components में रूट्स की वर्तमान संख्या है। प्रत्येक नोड शुरुआत में खुद को इंगित करता है। find पुनरावृत्ति से रूट की ओर बढ़ता है और विज़िट किए गए प्रत्येक नोड को उसके ग्रैंडपैरेंट पर इंगित करता है, जिससे रिकर्सन के बिना रास्ता छोटा हो जाता है। union दोनों रूट्स प्राप्त करता है। समान रूट्स False लौटाते हैं और काउंट नहीं बदलते हैं। अन्यथा, छोटे ट्री का रूट बड़े ट्री के रूट को इंगित करता है, उनके आकार जोड़े जाते हैं, और काउंट घटा दिया जाता है।
प्रतिनिधित्व एक फ़ॉरेस्ट बना रहता है। पाथ हाफ़िंग केवल एक नोड को उसी ट्री में एक पूर्वज (ancestor) पर इंगित करता है, और union दो अलग-अलग ट्रीज़ के रूट्स को जोड़ता है, इसलिए कोई भी ऑपरेशन पैरेंट साइकिल नहीं बनाता है। प्रत्येक सफल union ठीक दो ट्रीज़ को एक में बदल देता है, जो काउंट इनवेरिएंट को भी सिद्ध करता है।
ऐरे के निर्माण में O(n) लागत आती है। union by size और पाथ हाफ़िंग के साथ, find, कनेक्टिविटी और union एमॉर्टाइज़्ड O(α(n)) हैं, जिसमें O(n) स्पेस है। मेरे परीक्षण इस बात पर जोर देते हैं कि सेल्फ़-यूनियन और डुप्लिकेट यूनियन काउंट को न बदलें, दो बड़े कॉम्पोनेंट्स को ब्रिज करना, एक अलग-थलग नोड, खाली संरचना, और अमान्य नकारात्मक लेबल्स शामिल हों। मैं BFS के विरुद्ध यादृच्छिक छोटे मामलों का डिफरेंशियल-टेस्ट भी करूँगा।"
सामान्य गलतियाँ
- सीधे
parent[a] = bअसाइन करना →aरूट नहीं हो सकता है, इसलिए मूल ट्री विभाजित हो सकता है या
एक अनियंत्रित श्रृंखला में बदल सकता है → दोनों रूट्स ढूंढें और केवल रूट्स को लिंक करें।
- हमेशा दूसरे ट्री को पहले से जोड़ना → एक प्रतिकूल क्रम एक लंबा रास्ता बनाता है → **दिशा
चुनने के लिए रूट size या rank का उपयोग करें।**
findसे केवलparent[x]लौटाना → एक पैरेंट का रूट होना आवश्यक नहीं है, इसलिए अप्रत्यक्ष कनेक्टिविटी
का गलत वर्गीकरण हो जाता है → सेल्फ़-पैरेंट रूट मिलने तक पॉइंटर्स का अनुसरण करें।
- प्रत्येक union कॉल के बाद
componentsघटाना → डुप्लिकेट यूनियन्स और सेल्फ़-यूनियन्स
काउंट को वास्तविकता से नीचे ले जाते हैं → इसे केवल तभी अपडेट करें जब रूट्स भिन्न हों।
- रूट्स की अदला-बदली के बाद पुराने रूट के आकार को अपडेट करना → मेटाडेटा वास्तविक ट्री से अलग हो जाता है →
पहले अंतिम पैरेंट रूट चुनें, फिर लगातार लिंक करें और आकार जोड़ें।
- प्रति ऑपरेशन सबसे खराब स्थिति
O(1)का दावा करना → गारंटी एक क्रम पर एमॉर्टाइज़्ड होती है और
इसमें इन्वर्स एकरमैन फ़ंक्शन शामिल होता है → एमॉर्टाइज़्ड O(α(n)) की रिपोर्ट करें।
- पायथन के नकारात्मक सूचकांकों को अनदेखा करना →
find(-1)विफल होने के बजाय अंतिम नोड तक पहुँचता है →
सार्वजनिक कार्यान्वयन में दोनों सीमाओं को मान्य करें।
- मनमाने ढंग से एज डिलीशन के लिए बुनियादी Union-Find का उपयोग करना → पैरेंट पॉइंटर्स डिलीशन के बाद
वैकल्पिक रास्तों को बनाए नहीं रखते हैं → ऑफ़लाइन डिलीशन प्रोसेसिंग, रोलबैक Union-Find, या डायनामिक कनेक्टिविटी स्ट्रक्चर का उपयोग करें।
connectedको केवल-पठनीय मानना → पाथ हाफ़िंगparentपर लिखता है, जिससे समवर्ती कॉल्स के तहत रेस कंडीशन्स
उत्पन्न होती हैं → ग्लोबल लॉक, पार्टिशनिंग या समवर्ती एल्गोरिदम चुनने से पहले सिंक्रोनाइज़ेशन को परिभाषित करें।
फ़ॉलो-अप और उन्हें कैसे संभालें
फ़ॉलो-अप 1: Union-Find एक अनडायरेक्टेड ग्राफ़ में साइकिल का पता कैसे लगा सकता है?
एजेस (u, v) को एक-एक करके प्रोसेस करें। यदि union(u, v), False लौटाता है, तो नई एज से पहले एंडपॉइंट्स पहले से ही जुड़े हुए थे, इसलिए वह एज एक साइकिल को पूरा करती है। True परिणाम केवल दो पहले से अलग कॉम्पोनेंट्स को जोड़ता है। यह नियम सीधे अनडायरेक्टेड ग्राफ़ पर लागू होता है। डायरेक्टेड-साइकिल डिटेक्शन के लिए तीन-रंग के DFS या टोपोलॉजिकल सॉर्टिंग जैसी विधि की आवश्यकता होती है।
फ़ॉलो-अप 2: क्या होगा यदि कॉलर को सबसे हाल के union को पूर्ववत (undo) करने की आवश्यकता हो?
रोलबैक Union-Find का उपयोग करें। union by size बनाए रखें और प्रत्येक वास्तविक परिवर्तन के पुराने पैरेंट, रूट साइज़ और कॉम्पोनेंट काउंट को संशोधित करने से पहले एक हिस्ट्री स्टैक पर पुश करें। Undo उन मानों को पुनर्स्थापित करता है। पाथ कम्प्रेशन को आम तौर पर छोड़ दिया जाता है क्योंकि एक find कई प्रविष्टियों को म्यूटेट करता है, रोलबैक लॉग को बढ़ाता है और सीमाओं को जटिल बनाता है। केवल union by size ऊँचाई को O(log n) तक सीमित करता है और एक ऑफ़लाइन ऑपरेशन टाइमलाइन पर डिवाइड-एंड-कॉन्कर के साथ अच्छी तरह से काम करता है।
फ़ॉलो-अप 3: क्या होगा यदि संबंधों को मनमाने ढंग से हटाया जा सकता है?
मानक Union-Find मनमाने ढंग से ऑनलाइन डिलीशन्स का उत्तर नहीं दे सकता है। यदि पूरा ऑपरेशन क्रम ज्ञात है, तो प्रत्येक एज के सक्रिय अंतराल को समय के साथ एक सेगमेंट ट्री में रखें और इसे रोलबैक Union-Find के साथ ट्रैवर्स करें; जो डिलीशन्स केवल अंत में होते हैं उन्हें इंसर्शन के रूप में पीछे की ओर भी प्रोसेस किया जा सकता है। वास्तव में ऑनलाइन, बार-बार इंसर्शन, डिलीशन और क्वेरी के लिए अधिक उन्नत पूर्ण रूप से गतिशील कनेक्टिविटी संरचना की आवश्यकता होती है। इसलिए ऑपरेशन्स ऑफ़लाइन हैं या नहीं, यह एक समस्या-परिभाषित करने वाला स्पष्टीकरण है।
फ़ॉलो-अप 4: आप किसी कॉम्पोनेंट का आकार या उसके सभी सदस्यों को कैसे लौटाएँगे?
आकार पहले से ही रूट पर संग्रहीत है, इसलिए size_of(x) = size[find(x)] समान एमॉर्टाइज़्ड सीमा रखता है। सदस्यों की सूची केवल रूट के आकार से पुनर्प्राप्त नहीं की जा सकती है। सभी नोड्स को स्कैन करने और रूट्स की तुलना करने में O(n α(n)) खर्च होता है; सदस्य सेट्स को बनाए रखने से मर्ज और मेमोरी लागत जुड़ जाती है। कभी-कभार के निर्यात के लिए स्कैन करना आमतौर पर सरल होता है। बार-बार गणना एक अलग प्रतिनिधित्व को उचित ठहरा सकती है।
फ़ॉलो-अप 5: आप एकाधिक थ्रेड्स से आने वाले कॉल्स को कैसे संभालेंगे?
सबसे छोटा सही परिवर्तन find, union, और connected के चारों ओर एक म्यूटेक्स लगाना है, क्योंकि पाथ हाफ़िंग पैरेंट ऐरे पर लिखता है। इसे सिद्ध करना आसान है लेकिन यह सभी ऑपरेशन्स को सीरियलाइज़ कर देता है। केवल मापा गया कंटेंशन ही एटॉमिक कम्पेयर-एंड-स्वैप, डिटर्मिनिस्टिक लिंकिंग या पार्टिशनिंग पर आधारित एक समवर्ती डिज़ाइन को उचित ठहराता है। ऐसे डिज़ाइन को एसाइक्लिक पैरेंट पॉइंटर्स और एटॉमिक साइज़ और कॉम्पोनेंट-काउंट अपडेट्स को फिर से सिद्ध करना होगा; ऐरे को एटॉमिक वेरिएबल्स से बदलना अपने आप में पर्याप्त नहीं है।