क्या पेड़ एक "फर्स्टचाइल्ड, नेक्ससिबलिंग" संरचना द्वारा आयोजित किए जाते हैं? यदि नहीं, तो क्यों नहीं?


12

आमतौर पर, ट्री डेटा संरचनाओं को इस तरह से व्यवस्थित किया जाता है कि प्रत्येक नोड में इसके सभी बच्चों के लिए संकेत होते हैं।

       +-----------------------------------------+
       |        root                             | 
       | child1            child2         child3 |
       +--+------------------+----------------+--+
          |                  |                |
+---------------+    +---------------+    +---------------+
|    node1      |    |     node2     |    |     node3     |
| child1 child2 |    | child1 child2 |    | child1 child2 |
+--+---------+--+    +--+---------+--+    +--+---------+--+
   |         |          |         |          |         |

यह स्वाभाविक लगता है, लेकिन यह कुछ समस्याओं के साथ आता है। उदाहरण के लिए, जब बच्चे के नोड्स की संख्या बदलती है, तो आपको चिल्ड को प्रबंधित करने के लिए एक सरणी या सूची जैसी कोई चीज चाहिए।

केवल (पहले) बच्चे और (अगले) भाई-बहनों के बजाय का उपयोग करके, हमें कुछ ऐसा मिलता है जो ऐसा दिखता है:

       +-------------------+
       |        root       |
       | child    sibling  +--->NULL
       +--+----------------+
          |             
+----------------+    +----------------+    +----------------+
|    node1       |    |     node2      |    |     node3      |
| child  sibling +--->| child  sibling +--->| child  sibling +--->NULL
+--+-------------+    +--+-------------+    +--+-------------+
   |                     |                     |

जाहिर है, इस तरह की संरचना पेड़ों के रूप में अच्छी तरह से प्रतिनिधित्व कर सकती है, लेकिन यह कुछ फायदे भी प्रदान करता है। सबसे महत्वपूर्ण यह है कि हमें किसी भी बच्चे के नोड्स की संख्या के बारे में चिंता करने की आवश्यकता नहीं है। जब एक पार्स ट्री के लिए उपयोग किया जाता है, तो यह एक गहरा पेड़ बनने के बिना "ए + बी + सी + डी + ई" जैसे शब्द के लिए एक प्राकृतिक प्रतिनिधित्व प्रदान करता है।

क्या संग्रह पुस्तकालय उस तरह से वृक्ष संरचनाएं प्रदान करते हैं? क्या पार्सर ऐसी संरचना का उपयोग करते हैं? यदि नहीं, तो इसके क्या कारण हैं?


2
खैर, यह संरचना स्पष्ट रूप से उच्च जटिलता की लागत पर आती है। यह केवल इसके लायक है अगर आपको वास्तव में बच्चों की एक चर संख्या की आवश्यकता है। कई पेड़ों की एक निश्चित संख्या में बच्चे (या कम से कम एक निश्चित अधिकतम) उनके डिजाइन में निहित हैं। उन मामलों में अतिरिक्त अप्रत्यक्ष कोई मूल्य नहीं जोड़ते हैं।
जोकिम सॉयर

4
लिंक की गई सूची में आइटम डालना O(n)एल्गोरिथ्म में एक कारक का परिचय देता है।

और नोड 3 को रूट से प्राप्त करने के लिए आपको रूट का सिडर लेने की आवश्यकता होगी ...
टैकोरॉय

Tacroy: सही है, मूल को वापस खोजना बिल्कुल आसान नहीं है, लेकिन अगर मुझे वास्तव में इसकी आवश्यकता है, तो एक बैक पॉइंटर को
स्पष्ट

जवाबों:


7

पेड़, सूचियों की तरह, "अमूर्त डेटा प्रकार" हैं जिन्हें विभिन्न तरीकों से लागू किया जा सकता है। हर तरह से इसके फायदे और नुकसान हैं।

पहले उदाहरण में, इस संरचना का मुख्य लाभ यह है कि आप ओ (1) में किसी भी बच्चे तक पहुंच सकते हैं। नुकसान यह है कि एक बच्चे को जोड़ना कभी-कभी थोड़ा अधिक महंगा हो सकता है जब सरणी का विस्तार करना होगा। यह लागत हालांकि अपेक्षाकृत कम है। यह सबसे सरल कार्यान्वयन में से एक है।

दूसरे उदाहरण में, मुख्य लाभ यह है कि आप हमेशा ओ (1) में एक बच्चे को जोड़ते हैं। मुख्य नुकसान यह है कि एक बच्चे के लिए यादृच्छिक अभिगम की लागत O (n) है। इसके अलावा, दो कारणों से विशाल पेड़ों के लिए यह कम दिलचस्प हो सकता है: इसमें एक ऑब्जेक्ट हेडर की मेमोरी ओवरहेड और दो नोड प्रति नोड है, और नोड्स बेतरतीब ढंग से मेमोरी पर फैले हुए हैं जो सीपीयू कैश और के बीच बहुत अधिक स्वैपिंग का कारण हो सकता है। स्मृति जब पेड़ का पता लगाया जाता है, तो यह कार्यान्वयन उनके लिए कम आकर्षक होता है। हालांकि यह सामान्य पेड़ों और अनुप्रयोगों के लिए कोई समस्या नहीं है।

एक अंतिम दिलचस्प संभावना जिसका उल्लेख नहीं किया गया था, पूरे पेड़ को एक ही सरणी में संग्रहीत करना है। यह अधिक जटिल कोड की ओर जाता है, लेकिन कभी-कभी विशिष्ट मामलों में, विशेष रूप से विशाल पेड़ों के लिए बहुत फायदेमंद कार्यान्वयन होता है, क्योंकि आप ऑब्जेक्ट हेडर की लागत को छोड़ सकते हैं और सन्निहित स्मृति आवंटित कर सकते हैं।


1
उदाहरण के लिए: एक B + वृक्ष कभी भी इस "फ़र्स्टचाइल्ड, नेक्ससिबलिंग" संरचना का उपयोग नहीं करेगा। यह डिस्क-आधारित ट्री के लिए गैरबराबरी के बिंदु के लिए अक्षम होगा, और स्मृति-आधारित ट्री के लिए अभी भी बहुत अक्षम है। इन-मेमोरी आर-ट्री इस संरचना को सहन कर सकता है, लेकिन यह अभी भी बहुत अधिक कैश-मिस होगा। मैं ऐसी स्थिति के बारे में सोचने के लिए कठोर हूं, जहां "फर्स्टचाइल्ड, नेक्ससिबलिंग" बेहतर होगी। खैर, हाँ, यह एक वाक्यविन्यास पेड़ के लिए काम कर सकता है जैसा कि बारूद का उल्लेख है। और कुछ?
क्वर्टी

3
"आप हमेशा O (1) में एक बच्चे को जोड़ते हैं" - मुझे लगता है कि आप हमेशा O (1) में 0 पर एक बच्चा डाल सकते हैं, लेकिन एक बच्चे को जोड़ना स्पष्ट रूप से O (n) लगता है।
स्कॉट व्हिटलॉक

पूरे पेड़ को एक ही सरणी में संग्रहीत करना ढेर के लिए आम है।
ब्रायन

1
@ संकेत: ठीक है, मैंने मान लिया कि लिस्ट में पिछली वस्तु के साथ-साथ एक पॉइंटर / संदर्भ भी था, जो इसे पहली या अंतिम स्थिति के लिए O (1) बना देगा ... हालाँकि यह
OPs

मैं शर्त लगाता हूं कि (शायद बहुत पतित मामलों को छोड़कर ) "फ़र्स्टचाइल्ड, नेक्ससिबलिंग" कार्यान्वयन सरणी-आधारित चाइल्ड टेबल कार्यान्वयन की तुलना में अधिक कुशल नहीं है । कैश इलाका जीतता है, बड़ा समय। बी के पेड़ आधुनिक वास्तुकला पर अब तक के सबसे कुशल कार्यान्वयन साबित हुए हैं, जो कैश में सुधार के कारण पारंपरिक रूप से इस्तेमाल किए गए लाल-काले पेड़ों के खिलाफ जीत रहे हैं।
कोनराड रुडोल्फ

2

लगभग हर परियोजना जिसमें कुछ संपादन योग्य मॉडल या दस्तावेज़ होते हैं, उसके पास एक पदानुक्रमित संरचना होगी। विभिन्न संस्थाओं के लिए आधार-वर्ग के रूप में 'श्रेणीबद्ध नोड' को लागू करने के लिए यह काम आ सकता है। अक्सर लिंक्ड-लिस्ट (चाइल्ड सिबलिंग, 2 डी मॉडल) प्राकृतिक तरीका है जिससे कई क्लास लाइब्रेरीज़ विकसित होती हैं, हालाँकि बच्चे विविध प्रकार के हो सकते हैं, और शायद एक " ऑब्जेक्ट मॉडल " वह नहीं है जो हम सामान्य रूप से पेड़ों के बारे में बात करते समय मानते हैं।

आपके पहले मॉडल के एक पेड़ (नोड) का मेरा पसंदीदा कार्यान्वयन एक-लाइनर (सी # में) है:

public class node : List<node> { /* props go here */ }

अपने स्वयं के प्रकार की एक सामान्य सूची से विरासत (या किसी अन्य सामान्य संग्रह से विरासत में मिली-प्रकार)। चलना एक दिशा में संभव है: जड़ को नीचे की ओर बनाएं (आइटम अपने माता-पिता को नहीं जानते)।

माता-पिता केवल पेड़

एक और मॉडल जिसका आपने उल्लेख नहीं किया है, वह वह जगह है जहां हर बच्चे के माता-पिता का संदर्भ होता है:

               null
                 |
       +---------+---------------------------------+
       |       parent                              |
       | root                                      |
       +-------------------------------------------+
          |                   |                |
+---------+------+    +-------+--------+    +--+-------------+
|     parent     |    |     parent     |    |     parent     |
|     node 1     |    |     node 2     |    |     node 3     |
+----------------+    +----------------+    +----------------+

इस पेड़ को घूमना केवल दूसरे तरीके से संभव है, आम तौर पर इन सभी नोड्स को संग्रह (सरणी, हैशटेबल, शब्दकोश आदि) में संग्रहीत किया जाएगा और एक नोड को श्रेणीबद्ध स्थान के अलावा अन्य मानदंडों पर संग्रह की खोज करके स्थित किया जाएगा। वृक्ष जो आमतौर पर प्राथमिक महत्व का नहीं होगा।

ये पैरेंट-ओनली ट्री आमतौर पर डेटाबेस एप्लिकेशन में देखे जाते हैं। "Select * WHERE ParentId = x" कथनों वाले नोड के बच्चों को खोजना काफी आसान है। हालाँकि, हम शायद ही कभी पेड़ के नोड क्लास ऑब्जेक्ट में इस तरह से तब्दील हो पाते हैं। Statefull (डेस्कटॉप) अनुप्रयोगों में उन्हें मौजूदा ट्री-नोड नियंत्रण में लपेटा जा सकता है। स्टेटलेस (वेब) अनुप्रयोगों में भी, जिसकी संभावना नहीं है। मैंने देखा है कि ORM- मैपिंग क्लास-जेनरेटर टूल स्टैक ओवरफ्लो एरर फेंकते हैं, जब उन तालिकाओं के लिए क्लासेस बनाते हैं जो खुद के साथ संबंध रखते हैं (चकली), तो शायद ये पेड़ उस तरह के आम नहीं हैं।

द्विदिश नौगम्य पेड़

हालांकि अधिकांश व्यावहारिक मामलों में, दोनों दुनिया में सबसे अच्छा होना सुविधाजनक है। नोड्स जिनमें बच्चों की एक सूची है और इसके अलावा उनके माता-पिता को जानते हैं: द्विदिश नाविक पेड़।

                          null
                            |
       +--------------------+--------------------+
       |                  parent                 |
       |        root                             | 
       | child1            child2         child3 |
       +--+------------------+----------------+--+
          |                  |                |
+---------+-----+    +-------+-------+    +---+-----------+
|      parent   |    |     parent    |    |  parent       |
|    node1      |    |     node2     |    |     node3     |
| child1 child2 |    | child1 child2 |    | child1 child2 |
+--+---------+--+    +--+---------+--+    +--+---------+--+
   |         |          |         |          |         |

यह कई और पहलुओं पर विचार करता है:

  • माता-पिता के लिंकिंग और अनलिंकिंग को लागू करने के लिए कहां?
    • बूसी लॉजिक का ध्यान रखें, और नोड से बाहर के पहलू को छोड़ दें (वे भूल जाएँगे!)
    • नोड्स में बच्चे पैदा करने के तरीके हैं (पुन: आदेश देने की अनुमति नहीं है) (उनके System.Xml.XmlDocument DOM कार्यान्वयन में माइक्रोसाफ्ट विकल्प, जो मुझे पहली बार सामना करने पर पागल कर देता है)
    • नोड्स अपने निर्माता में एक अभिभावक लेते हैं (पुन: आदेश देने की अनुमति नहीं देते हैं)
    • सभी ऐड () में, सम्मिलित करें () और निकालें () तरीके और नोड्स के उनके अधिभार (आमतौर पर मेरी पसंद)
  • हठ
    • जब चलना है तो पेड़ कैसे चलें (उदाहरण के लिए माता-पिता को छोड़ें)
    • डी-सीरियलाइज़ करने के बाद दो-तरफ़ा लिंक को फिर से कैसे बनाया जाए (सभी अभिभावकों को फिर से डिसेरिएलाइज़ेशन एक्शन के रूप में स्थापित किया जाए)
  • सूचनाएं
    • स्थिर तंत्र (IsDirty ध्वज), गुणों में पुनरावर्ती संभाल?
    • घटनाएँ, माता-पिता के माध्यम से बच्चों के माध्यम से, या दोनों तरीकों से बबल अप करें (उदाहरण के लिए विंडोज़ संदेश पंप पर विचार करें)।

अब इस सवाल का जवाब देने के लिए , द्विदिश नौवहन योग्य पेड़ (मेरे करियर और क्षेत्र में अब तक) सबसे व्यापक रूप से उपयोग किए जाते हैं। उदाहरण हैं। System। Windows.orms.Control या .Net फ्रेमवर्क में System.Web.UI.Crol के माइक्रोप्रोसेस कार्यान्वयन, लेकिन साथ ही प्रत्येक DOM (डॉक्यूमेंट ऑब्जेक्ट मॉडल) कार्यान्वयन में नोड्स होंगे जो अपने माता-पिता के साथ-साथ अभिज्ञान को जानते हैं। उनके बच्चों की। कारण: कार्यान्वयन में आसानी पर उपयोग में आसानी। इसके अलावा, ये आम तौर पर अधिक विशिष्ट वर्गों के लिए आधार वर्ग होते हैं (XmlNode टैग, विशेषता और पाठ वर्गों का आधार हो सकता है) और ये आधार कक्षाएं सामान्य क्रमबद्धता और घटना से निपटने वाले आर्किटेक्चर लगाने के लिए प्राकृतिक स्थान हैं।

कई आर्किटेक्चर के दिल में ट्री की परत है, और स्वतंत्र रूप से नेविगेट करने में सक्षम होने का मतलब है तेजी से समाधान को लागू करना।


1

मैं किसी भी कंटेनर लाइब्रेरी के बारे में नहीं जानता जो सीधे आपके दूसरे मामले का समर्थन करती है, लेकिन अधिकांश कंटेनर लाइब्रेरी आसानी से उस परिदृश्य का समर्थन कर सकते हैं। उदाहरण के लिए, C ++ में आप हो सकते हैं:

class Node;  // forward reference to satisfy the compiler
typedef std::list<Node*> NodeList;
class Node : public NodeList { /* . . . */ };  // a node is also a list

Node* n = new Node;
n->push_back(new Node);
Node* tree = new Node;
tree->push_back(new Node);
tree->push_back(n);

पार्सर शायद इसी तरह की एक संरचना का उपयोग करते हैं, क्योंकि यह कुशलता से आइटम और बच्चों की चर संख्या के साथ नोड्स का समर्थन करता है। मैं कुछ के लिए नहीं जानता क्योंकि मैं आमतौर पर उनके स्रोत कोड को नहीं पढ़ता।


1

जब बच्चों की सरणी बेहतर हो, तो उन मामलों में से एक है जब आपको बच्चों की यादृच्छिक पहुँच की आवश्यकता होती है। और यह आमतौर पर तब होता है जब बच्चों को हल किया जाता है। उदाहरण के लिए, फ़ाइल की तरह पदानुक्रम ट्री तेज पथ खोज के लिए इसका उपयोग कर सकता है। या DOM टैग ट्री जब इंडेक्स एक्सेस बहुत स्वाभाविक है

एक और उदाहरण है जब सभी बच्चों के लिए "संकेत" होने से अधिक सुविधाजनक उपयोग की अनुमति मिलती है। उदाहरण के लिए, आपके द्वारा वर्णित दोनों प्रकारों का उपयोग संबंधपरक डेटाबेस के साथ पेड़ संबंधों को लागू करते समय किया जा सकता है। लेकिन पूर्व (इस मामले में माता-पिता से बच्चों के लिए मास्टर-विस्तार) उपयोगी डेटा के लिए सामान्य एसक्यूएल के साथ क्वेरी करने की अनुमति देगा, जबकि उत्तरार्द्ध काफी सीमित कर देगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.