प्रतिनिधि इंटरव्यू विषय

कोडिंग इंटरव्यू: बाइनरी ट्री को सीरियलाइज़ और डिसीरियलाइज़ करना

कोडिंगकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

साइन किए गए 32-बिट पूर्णांकों के एक मनमाने बाइनरी ट्री के लिए serialize(root) और deserialize(data) को लागू करें। पुनर्गठित ट्री में समान मान और संरचना होनी चाहिए, और उत्तर में शुद्धता, जटिलता, अमान्य-इनपुट हैंडलिंग और रिकर्सन-गहराई की सीमाओं की व्याख्या होनी चाहिए।

समस्या और लागू संदर्भ

एक मनमाने बाइनरी ट्री के लिए दो फ़ंक्शन लागू करें:

  • serialize(root) ट्री को एक स्ट्रिंग में परिवर्तित करता है।
  • deserialize(data) समान मानों और संरचना वाले ट्री का पुनर्निर्माण करता है।

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

इस ट्री के लिए:

text
1
       / \
      2   3
         / \
        4   5

चुना गया प्रारूप है:

text
1,2,#,#,3,4,#,#,5,#,#

प्रत्येक पूर्णांक एक नोड को रिकॉर्ड करता है, # एक अनुपस्थित चाइल्ड को रिकॉर्ड करता है, कॉमा टोकन को अलग करते हैं, और प्रीऑर्डर यह निर्धारित करता है कि टोकन कैसे प्रोसेस किए जाते हैं। 2026 में अपडेट की गई सार्वजनिक सामग्री इस सटीक समस्या को प्रीऑर्डर और लेवल-ऑर्डर समाधानों के साथ प्रस्तुत करती है, जबकि interviewing.io का एक रिप्ले दिखाता है कि इसे Meta के एक इंजीनियर के साथ मॉक इंटरव्यू में पूछा गया है। यह इस बात का समर्थन करता है कि यह एक वर्तमान प्रतिनिधि कोडिंग अभ्यास है, बिना यह दावा किए कि हर कंपनी या इंटरव्यू इसका उपयोग करता है।

इंटरव्यूअर क्या मूल्यांकन कर रहा है

पहला संकेत यह है कि क्या उम्मीदवार ट्रैवर्सल चुनने से पहले रिवर्सिबिलिटी (reversibility) को परिभाषित करता है। केवल प्रीऑर्डर मान पर्याप्त नहीं हैं। बाएं चाइल्ड 2 के साथ एक रूट 1 और दाएं चाइल्ड 2 के साथ एक रूट 1 दोनों [1, 2] उत्पन्न करते हैं जब तक कि अनुपस्थित बच्चों को एन्कोड न किया जाए। उनके चिह्नित रूप भिन्न होते हैं:

text
Left child:  1,2,#,#,#
Right child: 1,#,2,#,#

दूसरा संकेत एन्कोडर और डिकोडर को एक-दूसरे के व्युत्क्रम (inverses) के रूप में डिज़ाइन करना है। प्रीऑर्डर में, एक डिकोडर एक टोकन पढ़ता है। # एक खाली सब-ट्री को पूरा करता है। एक मान एक नोड शुरू करता है, जिसके बाद अगला पूर्ण सब-ट्री बाएं चाइल्ड का होता है और उसके बाद का पूर्ण सब-ट्री दाएं चाइल्ड का होता है। इसलिए यह प्रारूप सब-ट्री की लंबाई को संग्रहीत किए बिना अपनी स्वयं की रिकर्सिव सीमाएं प्रदान करता है।

तीसरा संकेत एक वास्तविक शुद्धता (correctness) तर्क है। n नोड्स वाले ट्री के लिए, n + 1 null चाइल्ड पॉइंटर्स होते हैं, इसलिए एन्कोडिंग में ठीक 2n + 1 टोकन होते हैं। इससे भी महत्वपूर्ण बात यह है कि डिकोडर को एक सब-ट्री के लिए सटीक टोकन का उपभोग करना चाहिए और इटरेटर को अगले सब-ट्री पर स्थित छोड़ना चाहिए। संरचनात्मक आगमन (Structural induction) इस विशेषता को सिद्ध करता है।

अंत में, इंटरव्यूअर इंजीनियरिंग सीमाओं की तलाश करता है: अमान्य इनपुट, नकारात्मक और डुप्लिकेट मान, रिकर्सन गहराई, आउटपुट आकार, और कब BFS या कोई प्रोडक्शन सीरियलाइज़ेशन प्रारूप बेहतर विकल्प है।

उत्तर देने से पहले स्पष्ट करने वाले प्रश्न

  • क्या यह एक मनमाना बाइनरी ट्री है या बाइनरी सर्च ट्री (BST)? एक मनमाने ट्री को संरचनात्मक मार्कर्स की आवश्यकता होती है। एक BST को कभी-कभी प्रीऑर्डर और एक स्पष्ट डुप्लिकेट नीति से पुनर्गठित किया जा सकता है।
  • क्या स्ट्रिंग को किसी मौजूदा वायर प्रारूप का पालन करना चाहिए? यह प्रॉम्प्ट एक निजी प्रारूप की अनुमति देता है। क्रॉस-सर्विस स्टोरेज के लिए स्कीमा वर्ज़निंग, अनुकूलता नियम और अक्सर एक मानक कोडेक की आवश्यकता होती है।
  • क्या नोड मानों में डिलीमीटर या सेंटिनल हो सकता है? वे पूर्णांक हैं, इसलिए कॉमा और # स्पष्ट हैं। सामान्य स्ट्रिंग्स को एस्केपिंग या लंबाई उपसर्गों (length prefixes) की आवश्यकता होगी।
  • क्या ट्री खाली हो सकता है? हाँ। यह # में सीरियलाइज़ होता है।
  • क्या इनपुट अत्यधिक गहरा या प्रतिकूल (adversarial) हो सकता है? रिकर्सिव उत्तर सीमित ऊंचाई मानता है। अविश्वसनीय या अत्यधिक तिरछे (skewed) ट्री के लिए एक स्पष्ट स्टैक और संसाधन सीमाओं की आवश्यकता होती है।
  • क्या deserialize केवल serialize से विश्वसनीय आउटपुट प्राप्त करेगा? कोड सख्त रहता है: खाली टेक्स्ट, अमान्य पूर्णांक, कटे हुए ट्री और अतिरिक्त टोकन अस्वीकार कर दिए जाते हैं।
  • क्या हम पठनीयता के लिए अनुकूलित करते हैं या न्यूनतम बाइट्स के लिए? प्रीऑर्डर टेक्स्ट को समझाना और टेस्ट करना आसान है। एक कॉम्पैक्ट बाइनरी प्रोटोकॉल टैग और पूर्णांकों को अलग तरीके से एन्कोड करेगा।

30-सेकंड का उत्तर ढांचा

"मैं प्रीऑर्डर ट्रैवर्सल का उपयोग करूंगा और प्रत्येक अनुपस्थित चाइल्ड के लिए # उत्सर्जित करूंगा। एक मान टोकन का अर्थ है एक नोड बनाना, फिर उसके बाएं और दाएं सब-ट्री को रिकर्सिव रूप से डिकोड करना; # का अर्थ है None वापस करना। Null मार्कर्स आवश्यक हैं क्योंकि केवल मान एक बाएं चाइल्ड को दाएं चाइल्ड से अलग नहीं कर सकते हैं। एन्कोडर और डिकोडर एक-दूसरे को दर्शाते हैं, और संरचनात्मक आगमन से पता चलता है कि प्रत्येक डिकोड कॉल ठीक एक सब-ट्री का उपभोग करती है। दोनों ऑपरेशन O(n) समय लेते हैं और O(n) डेटा उत्पन्न करते हैं, ऊंचाई h के लिए O(h) कॉल स्टैक के साथ। मैं कटे हुए या अतिरिक्त इनपुट को भी अस्वीकार करूंगा और गहराई असीमित होने पर एक इटरेटिव BFS या स्टैक-आधारित संस्करण का उल्लेख करूंगा।"

चरण-दर-चरण गहन विश्लेषण

चरण एक: एक जवाबी उदाहरण के साथ केवल-मान वाले आधार को अस्वीकार करें।

प्रीऑर्डर, इनऑर्डर या पोस्टऑर्डर मान अपने आप में किसी मनमाने बाइनरी ट्री की विशिष्ट पहचान नहीं करते हैं। डुप्लिकेट मानों की अनुमति होने पर प्रीऑर्डर और इनऑर्डर को संयोजित करना भी अस्पष्ट हो जाता है। प्रारूप को मानों के साथ-साथ संरचना को भी एन्कोड करना चाहिए। एक इंटरव्यू स्ट्रिंग प्रारूप के लिए null मार्कर सबसे सरल संरचनात्मक संकेत है।

चरण दो: एक ऐसा व्याकरण चुनें जिसे बाएं से दाएं डिकोड किया जा सके।

प्रारूप को रिकर्सिव रूप से वर्णित किया जा सकता है:

text
tree := "#"
      | integer "," tree "," tree

वास्तविक कार्यान्वयन पहले कॉमा पर टोकनाइज़ करता है, इसलिए प्रत्येक रिकर्सिव कॉल एक टोकन का उपभोग करती है और, एक मान के लिए, दो बाद के सब-ट्री एन्कोडिंग का उपभोग करती है। साइन किए गए पूर्णांक टेक्स्ट में कभी भी , या # नहीं होता है। एक खाली ट्री # है; मान 7 वाला एक लीफ नोड 7,#,# है।

यह व्याकरण एक उपयोगी गणना इनवेरिएंट (counting invariant) भी देता है। n वास्तविक नोड्स वाले एक बाइनरी ट्री में n + 1 null चाइल्ड पॉइंटर्स होते हैं। इसलिए सीरियलाइज़ेशन n मान टोकन और n + 1 null टोकन उत्सर्जित करता है, कुल 2n + 1 टोकन के लिए। यह गणना एक नैदानिक उपकरण (diagnostic) है, पार्सिंग का विकल्प नहीं: अमान्य टोकन का कुल योग भी विषम हो सकता है।

चरण तीन: सममित रिकर्सिव ऑपरेशन्स को लागू करें।

python
from __future__ import annotations

from dataclasses import dataclass


MIN_INT32 = -(2**31)
MAX_INT32 = 2**31 - 1


@dataclass
class TreeNode:
    val: int
    left: TreeNode | None = None
    right: TreeNode | None = None


class Codec:
    NULL = "#"
    SEP = ","

    def serialize(self, root: TreeNode | None) -> str:
        tokens: list[str] = []

        def visit(node: TreeNode | None) -> None:
            if node is None:
                tokens.append(self.NULL)
                return

            if node.val < MIN_INT32 or node.val > MAX_INT32:
                raise ValueError("node value is outside signed 32-bit range")

            tokens.append(str(node.val))
            visit(node.left)
            visit(node.right)

        visit(root)
        return self.SEP.join(tokens)

    def deserialize(self, data: str) -> TreeNode | None:
        if data == "":
            raise ValueError("serialization cannot be empty")

        tokens = iter(data.split(self.SEP))

        def build() -> TreeNode | None:
            try:
                token = next(tokens)
            except StopIteration:
                raise ValueError("serialization is truncated") from None

            if token == self.NULL:
                return None

            try:
                value = int(token)
            except ValueError:
                raise ValueError(f"invalid integer token: {token}") from None

            if value < MIN_INT32 or value > MAX_INT32:
                raise ValueError("node value is outside signed 32-bit range")

            node = TreeNode(value)
            node.left = build()
            node.right = build()
            return node

        root = build()

        try:
            extra = next(tokens)
        except StopIteration:
            return root

        raise ValueError(f"trailing token: {extra}")

एक टोकन सूची बनाने से सीरियलाइज़ेशन के दौरान बार-बार स्ट्रिंग संयोजन से बचा जा सकता है। डिकोडर रिकर्सिव कॉलों के बीच एक इटरेटर साझा करता है, ताकि कोई चाइल्ड शुरुआत से दोबारा पढ़ना शुरू न करे। रूट के पूरा होने के बाद बचे हुए टोकन की जांच करने से 1,#,#,9,#,# जैसे मान्य-उपसर्ग वाले इनपुट को स्वीकार होने से रोका जाता है।

चरण चार: सिद्ध करें कि डिकोडिंग सीरियलाइज़ेशन को उलट देती है।

ट्री T पर संरचनात्मक आगमन (structural induction) का उपयोग करें।

  • आधार स्थिति (Base case): यदि T खाली है, तो सीरियलाइज़ेशन # उत्सर्जित करता है। डिकोडर # पढ़ता है, None लौटाता है, और ठीक उस सब-ट्री के एक टोकन का उपभोग करता है।
  • आगमन चरण (Inductive step): मान लें कि दावा बाएं और दाएं सब-ट्री के लिए सही है। सीरियलाइज़ेशन रूट मान उत्सर्जित करता है, उसके बाद पूर्ण बायां एन्कोडिंग, फिर पूर्ण दायां एन्कोडिंग। डिकोडर वही रूट बनाता है, परिकल्पना के अनुसार पहली रिकर्सिव कॉल ठीक बाएं एन्कोडिंग का उपभोग करती है, और दूसरी ठीक दाएं एन्कोडिंग का उपभोग करती है। यह समान संरचना और मानों का पुनर्निर्माण करता है और T के तुरंत बाद रुक जाता है।

इस प्रकार deserialize(serialize(T)) संरचनात्मक रूप से T के बराबर है, और प्रत्येक कॉल इटरेटर को अगले न पढ़े गए सब-ट्री पर छोड़ती है। अंतिम अनुगामी-टोकन (trailing-token) जांच यह पुष्टि करती है कि रूट ने पूरे इनपुट का उपभोग कर लिया है।

चरण पाँच: आउटपुट या स्टैक को छिपाए बिना लागत की गणना करें।

दोनों ऑपरेशन प्रत्येक वास्तविक नोड और null पॉइंटर पर एक बार जाते हैं, इसलिए समय O(n) है। सीरियलाइज़ किया गया आउटपुट और टोकनाइज़ किया गया इनपुट O(n) हैं। पुनर्गठित ट्री स्वयं भी O(n) है। रिकर्सिव कॉल-स्टैक का उपयोग O(h) है, जहाँ h ट्री की ऊंचाई है: एक संतुलित ट्री के लिए O(log n) और पूरी तरह से तिरछे ट्री के लिए O(n)

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

चरण छह: प्रीऑर्डर DFS की तुलना लेवल-ऑर्डर BFS से करें।

यदि वे null जानकारी बनाए रखते हैं तो दोनों O(n) समय और आउटपुट स्पेस में रिवर्सिबल हो सकते हैं।

प्रारूपमुख्य लाभमुख्य लागत
Nulls के साथ प्रीऑर्डर DFSएन्कोडर और डिकोडर का रिकर्सिव आकार समान होता हैरिकर्सिव संस्करण O(h) कॉल स्टैक का उपयोग करता है
Nulls के साथ लेवल-ऑर्डर BFSइटरेटिव और सरणी ट्री उदाहरणों के दृश्य रूप से करीबकतार में O(w) नोड्स हो सकते हैं और विरल आउटपुट विस्तृत हो सकता है
केवल मानछोटामनमाने-ट्री की संरचना खो देता है
मानक वर्ज़न्ड बाइनरी प्रारूपइंटरऑपरेबिलिटी और कॉम्पैक्ट टाइप्ड फ़ील्डइस इंटरव्यू की आवश्यकता से अधिक प्रोटोकॉल तंत्र

BFS तब बेहतर होता है जब रिकर्सन गहराई तत्काल जोखिम हो या आसपास का सिस्टम पहले से ही लेवल-ऑर्डर प्रतिनिधित्व का उपयोग करता हो। बेस इंटरव्यू के लिए प्रीऑर्डर बेहतर है क्योंकि इसका व्याकरण और प्रमाण छोटे हैं।

चरण सात: राउंड ट्रिप और अमान्य इनपुट सत्यापित करें।

राउंड-ट्रिप परीक्षणों में शामिल होना चाहिए:

स्थितिअपेक्षित सीरियलाइज़ेशन
खाली ट्री#
एकल नोड 77,#,#
रूट 1, बायां चाइल्ड 21,2,#,#,#
रूट 1, दायां चाइल्ड 21,#,2,#,#
नकारात्मक डुप्लिकेट बच्चेसंरचना और दोनों दोहराए गए मान संरक्षित हैं
साइन किए गए 32-बिट चरम मानदोनों सीमाएं पार्स होती हैं और राउंड-ट्रिप करती हैं

"", 1,#, x,#,#, 2147483648,#,#, और 1,#,#,2,#,# को भी अस्वीकार करें। उत्पन्न ट्री के लिए, मूल और डिकोड किए गए ट्री की रिकर्सिव रूप से तुलना करें और serialize(deserialize(serialize(root))) == serialize(root) का दावा (assert) करें। एक तिरछे-ट्री परीक्षण को स्वीकृत ऊंचाई सीमा के करीब चलना चाहिए ताकि स्टैक धारणा आकस्मिक होने के बजाय दृश्यमान हो।

उच्च-गुणवत्ता वाला नमूना उत्तर

"मैं पहले पुष्टि करूंगा कि यह एक मनमाना बाइनरी ट्री है, मान साइन किए गए 32-बिट पूर्णांक हैं, और प्रारूप को केवल हमारे डिकोडर द्वारा पढ़ने की आवश्यकता है। चूंकि डुप्लिकेट मानों की अनुमति है, मुझे संरचना को स्पष्ट रूप से एन्कोड करने की आवश्यकता है।

मैं प्रीऑर्डर में ट्रैवर्स करूंगा। एक वास्तविक नोड के लिए मैं उसका मान उत्सर्जित करता हूं, फिर उसके बाएं और दाएं सब-ट्री; एक अनुपस्थित चाइल्ड के लिए मैं # उत्सर्जित करता हूं। यह उदाहरण के लिए, एक बाएं चाइल्ड को दाएं चाइल्ड से अलग करता है, भले ही प्रीऑर्डर मान समान हों। डिकोडिंग के दौरान, एक साझा टोकन इटरेटर समान व्याकरण को दर्शाता है: #, None लौटाता है; अन्यथा मैं एक नोड बनाता हूं और रिकर्सिव रूप से बाएं फिर दाएं का निर्माण करता हूं।

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

प्रत्येक वास्तविक नोड और null पॉइंटर को एक बार संसाधित किया जाता है, इसलिए दोनों ऑपरेशन O(n) हैं। टेक्स्ट और टोकन O(n) स्पेस का उपयोग करते हैं, जबकि रिकर्सन O(h) स्टैक का उपयोग करता है। यदि ट्री की ऊंचाई प्रतिकूल हो सकती है, तो मैं एक स्पष्ट स्टैक या BFS पर स्विच करूंगा और आकार और गहराई की सीमाएं लागू करूंगा। मैं खाली, एकल-नोड, केवल-बाएं बनाम केवल-दाएं, डुप्लिकेट, नकारात्मक मान, पूर्णांक सीमाएं, विकृत स्ट्रिंग्स और यादृच्छिक राउंड ट्रिप का परीक्षण करूंगा।"

सामान्य गलतियाँ

  • केवल नोड मानों को सीरियलाइज़ करना → विभिन्न संरचनाएं समान ट्रैवर्सल उत्पन्न कर सकती हैं → null मार्कर्स या अन्य स्पष्ट संरचनात्मक सीमाएं उत्सर्जित करें।
  • ऐसे डिलीमीटर का उपयोग करना जो मानों के अंदर दिखाई दे सकता है → टोकन सीमाएं अस्पष्ट हो जाती हैं → मानों को एस्केप करें, लंबाई जोड़ें, या मान व्याकरण के बाहर एक डिलीमीटर चुनें।
  • प्रत्येक रिकर्सिव कॉल में एक नया इटरेटर बनाना → प्रत्येक चाइल्ड पहले टोकन को फिर से पढ़ता है → एक आगे बढ़ने वाले इटरेटर या इंडेक्स को साझा करें।
  • रूट को डिकोड करना और शेष टेक्स्ट को अनदेखा करना → एक मान्य उपसर्ग पीछे के दूषित डेटा को छुपाता है → पूर्ण इनपुट उपभोग की आवश्यकता रखें।
  • अनुपस्थित टोकन को एक असंबंधित अपवाद के रूप में सामने आने देना → कटे हुए डेटा का निदान करना कठिन होता है → समय से पहले समाप्ति को एक स्पष्ट पार्स त्रुटि में बदलें।
  • यह दावा करना कि सहायक स्पेस हमेशा O(log n) होता है → एक तिरछे ट्री की ऊंचाई n होती है, और टोकनाइज़ेशन भी लीनियर स्पेस का उपयोग करता है → आउटपुट, टोकन स्टोरेज, ट्री और कॉल-स्टैक लागतों को अलग करें।
  • डुप्लिकेट को परिभाषित किए बिना BST के लिए प्रीऑर्डर मानों को पर्याप्त कहना → समान कुंजियाँ पुनर्निर्माण को अस्पष्ट बना सकती हैं → मार्कर्स को हटाने से पहले क्रम और डुप्लिकेट नीति बताएं।
  • सीमाओं के बिना अविश्वसनीय गहराई के लिए रिकर्सिव कोड का उपयोग करना → एक लंबी श्रृंखला स्टैक को समाप्त कर सकती है → एक स्पष्ट स्टैक और संसाधन सीमाओं का उपयोग करें।
  • राउंड ट्रिप के बाद ऑब्जेक्ट आइडेंटिटी (reference) की तुलना करना → पुनर्निर्माण नए नोड्स बनाता है → मानों और संरचना की तुलना करें।
  • केवल एक संतुलित उदाहरण का परीक्षण करना → बायां/दायां अस्पष्टता और स्टैक जोखिम छिपे रहते हैं → खाली, एकतरफा, डुप्लिकेट, चरम, विकृत और तिरछे मामलों को शामिल करें।

फॉलो-अप प्रश्न और उत्तर

फॉलो-अप 1: क्या बाइनरी सर्च ट्री (BST) null मार्कर्स को छोड़ सकता है?

अक्सर हाँ। एक सख्त BST इनवेरिएंट और अद्वितीय कुंजियों के साथ, निचली और ऊपरी सीमाओं को ले जाकर प्रीऑर्डर से पुनर्निर्माण किया जा सकता है: वर्तमान सीमा के अंदर के मान उस सब-ट्री के हैं, और इसके बाहर का पहला मान एक पूर्वज का है। यदि डुप्लिकेट की अनुमति है, तो अनुबंध में यह बताना होगा कि समान मान बाएं जाते हैं, दाएं जाते हैं, या नोड में गिने जाते हैं। उस नीति के बिना, कॉम्पैक्ट प्रारूप अस्पष्ट है। मनमाना-ट्री आधार समस्या इस अनुकूलन का उपयोग नहीं कर सकती है।

फॉलो-अप 2: आप मनमाने स्ट्रिंग मानों का समर्थन कैसे करेंगे?

कॉमा और # एक स्ट्रिंग के अंदर हो सकते हैं, इसलिए डिलीमीटर विभाजन अब स्व-सीमांकन (self-delimiting) नहीं रहता है। एक विकल्प 5:hello जैसा लंबाई उपसर्ग है, जिसके बाद स्पष्ट नोड/null टैग होते हैं। दूसरा एक स्कीमा के साथ एक मानक सीरियलाइज़ेशन लाइब्रेरी है। एस्केपिंग काम कर सकती है, लेकिन डिकोडर को एस्केप किए गए सेपरेटर्स को संरचनात्मक सेपरेटर्स से अलग करना होगा और अमान्य एस्केप अनुक्रमों को संभालना होगा। लंबाई उपसर्ग उपभोग नियमों को सिद्ध करना आसान बनाते हैं।

फॉलो-अप 3: लाखों नोड्स या अत्यधिक गहराई वाले ट्री के लिए क्या बदलता है?

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

फॉलो-अप 4: आप किसी प्रोडक्शन सिस्टम में इस प्रारूप का वर्ज़न कैसे निर्धारित करेंगे?

ट्री पेलोड के बाहर एक प्रारूप पहचानकर्ता और वर्ज़न जोड़ें, पूर्णांक चौड़ाई और टेक्स्ट एन्कोडिंग को परिभाषित करें, और निर्दिष्ट करें कि अज्ञात फ़ील्ड या वर्ज़न विफल (fail closed) होते हैं या नहीं। जब भ्रष्टाचार का पता लगाना हो तो एक अखंडता जांच (integrity check) शामिल करें, लेकिन चेकसम को प्रमाणीकरण के रूप में न मानें। रोलआउट के लिए पुराने को पढ़ने/नए को लिखने (read-old/write-new) की अनुकूलता और प्रत्येक समर्थित वर्ज़न के लिए फिक्स्चर की आवश्यकता होती है। क्रॉस-लैंग्वेज सेवाओं के लिए, इंटरव्यू कोडेक का विस्तार करने की तुलना में एक अनुरक्षित स्कीमा-आधारित प्रारूप आमतौर पर अधिक सुरक्षित होता है।

फॉलो-अप 5: क्या लेवल-ऑर्डर सीरियलाइज़ेशन सुरक्षित रूप से अंतिम nulls को ट्रिम कर सकता है?

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

सार्वजनिक स्रोत

संबंधित प्रश्न

संबंधित इंटरव्यू टूल

कोडिंग प्रॉम्प्ट के लिए स्क्रीनशॉट का उपयोग करें

समस्या को कैप्चर करें, फिर क्रम से प्रतिबंधों (constraints), समाधान, कोड, एज केस और जटिलता पर काम करें।

टूल देखें