স্ট্র্যাকস বা পুনরাবৃত্তি না ব্যবহার করে মরিস ইনর্ডার ট্রি ট্রভারসাল ব্যাখ্যা করুন


125

কেউ দয়া করে আমাকে স্ট্যাকস বা পুনরাবৃত্তি না ব্যবহার করে নিম্নলিখিত মরিস ইনর্ডার ট্রি ট্রভারসাল অ্যালগরিদম বুঝতে সাহায্য করতে পারেন? আমি এটি কীভাবে কাজ করে তা বোঝার চেষ্টা করছিলাম তবে এটি কেবল আমার থেকে দূরে রইল।

 1. Initialize current as root
 2. While current is not NULL
  If current does not have left child     
   a. Print currents data
   b. Go to the right, i.e., current = current->right
  Else
   a. In current's left subtree, make current the right child of the rightmost node
   b. Go to this left child, i.e., current = current->left

আমি বুঝতে পারছি গাছ একটি ভাবে রুপান্তরিত করা হয়েছে যে current node, তৈরি করা হয় right childএর max nodeমধ্যে right subtreeএবং inorder ট্র্যাভেরসাল জন্য এই সম্পত্তি ব্যবহার করুন। তবে এর বাইরে আমি হারিয়ে গেছি।

সম্পাদনা: এটির সাথে সি ++ কোড পাওয়া গেছে। গাছটি সংশোধন করার পরে কীভাবে পুনরুদ্ধার করা হয়েছে তা বুঝতে আমার খুব কষ্ট হয়েছিল। ম্যাজিকটি ক্লজটিতে রয়েছে else, যা ডান পাতায় একবার পরিবর্তন হয়ে গেলে আঘাত করা হয়। বিশদ জন্য কোড দেখুন:

/* Function to traverse binary tree without recursion and
   without stack */
void MorrisTraversal(struct tNode *root)
{
  struct tNode *current,*pre;

  if(root == NULL)
     return; 

  current = root;
  while(current != NULL)
  {
    if(current->left == NULL)
    {
      printf(" %d ", current->data);
      current = current->right;
    }
    else
    {
      /* Find the inorder predecessor of current */
      pre = current->left;
      while(pre->right != NULL && pre->right != current)
        pre = pre->right;

      /* Make current as right child of its inorder predecessor */
      if(pre->right == NULL)
      {
        pre->right = current;
        current = current->left;
      }

     // MAGIC OF RESTORING the Tree happens here: 
      /* Revert the changes made in if part to restore the original
        tree i.e., fix the right child of predecssor */
      else
      {
        pre->right = NULL;
        printf(" %d ",current->data);
        current = current->right;
      } /* End of if condition pre->right == NULL */
    } /* End of if condition current->left == NULL*/
  } /* End of while */
}

12
আমি এই অ্যালগরিদম এর আগে কখনও শুনিনি। বেশ মার্জিত!
ফ্রেড ফু

5
আমি ভেবেছিলাম সিউডো কোড + কোড (সম্ভবত)) এর উত্সটি নির্দেশ করা কার্যকর হতে পারে ।
বার্নহার্ড বার্কার


উপরের কোডে, নিম্নলিখিত লাইনের প্রয়োজন নেই: pre->right = NULL;
prashant.kr.mod

উত্তর:


155

আমি যদি সঠিকভাবে অ্যালগরিদমটি পড়ছি তবে এটি কীভাবে কাজ করে তার উদাহরণ হওয়া উচিত:

     X
   /   \
  Y     Z
 / \   / \
A   B C   D

প্রথমত, Xমূল, সুতরাং এটি হিসাবে আরম্ভ করা হয় currentXএকটি বাম সন্তান রয়েছে, তাই বাম Xসাবট্রির ডানদিকের ডান সন্তান হিসাবে তৈরি করা হয়েছে X- Xএকটি অন্তর্বর্তী ট্র্যাভারসাল করার তাত্ক্ষণিক পূর্বসূরী । সুতরাং Xসঠিক সন্তান হিসাবে তৈরি করা হয় B, তারপর currentসেট করা হয় Y। গাছটি এখন এইরকম দেখাচ্ছে:

    Y
   / \
  A   B
       \
        X
       / \
     (Y)  Z
         / \
        C   D

(Y)উপরে Yএবং এর সমস্ত শিশুকে বোঝায় যা পুনরাবৃত্তি সংক্রান্ত সমস্যার জন্য বাদ দেওয়া হয়েছে। গুরুত্বপূর্ণ অংশটি যাইহোক তালিকাভুক্ত করা হয়েছে। এখন গাছটি এক্স-এর পিছনে একটি লিঙ্ক রয়েছে, ট্র্যাভারসাল অব্যাহত রয়েছে ...

 A
  \
   Y
  / \
(A)  B
      \
       X
      / \
    (Y)  Z
        / \
       C   D

তারপরে Aআউটপুট করা হয়, কারণ এতে কোনও বাম সন্তান নেই, এবং currentএটি আবার ফিরে আসে Y, যা Aআগের পুনরাবৃত্তির ডান সন্তান হিসাবে তৈরি হয়েছিল । পরবর্তী পুনরাবৃত্তিতে, ওয়াইয়ের উভয় সন্তান রয়েছে। যাইহোক, লুপটির দ্বৈত-পরিস্থিতি এটি নিজের কাছে পৌঁছালে এটি বন্ধ করে দেয়, এটি ইঙ্গিত দেয় যে এটি বাম সাবট্রিকে ইতিমধ্যে বিস্তৃত হয়েছে। সুতরাং, এটি নিজেই মুদ্রণ করে এবং এটি তার ডান সাবট্রি দিয়ে চালিয়ে যায়, যা এটি B

Bনিজেই মুদ্রণ করে এবং তারপরে এটি currentহয়ে যায় X, যা একই চেকিং প্রক্রিয়াটির মধ্য দিয়ে যায় Yএবং বুঝতে পারে যে এর বাম সাবট্রিকে ট্র্যাজড করা হয়েছে, এর সাথে চালিয়ে যাওয়া Z। গাছের বাকী অংশ একই প্যাটার্ন অনুসরণ করে।

কোনও পুনরাবৃত্তি প্রয়োজন হয় না, কারণ একটি স্ট্যাকের মাধ্যমে ব্যাকট্র্যাকিংয়ের উপর নির্ভর করার পরিবর্তে (উপ) গাছের গোড়ায় ফিরে আসা একটি লিঙ্কটি এমন বিন্দুতে স্থানান্তরিত হয় যেখানে এটি যে কোনও উপায়ে পুনরাবৃত্ত ইনঅর্ডার ট্রি ট্রভারসাল অ্যালগরিদমকে অ্যাক্সেস করা হত - তার পরে বাম সাবট্রি শেষ হয়েছে।


3
ব্যাখ্যার জন্য ধন্যবাদ. বাম শিশুটি কেটে ফেলা হয় না, পরিবর্তে গাছটি ট্র্যাভারসাল করার উদ্দেশ্যে ডানদিকের পাতায় যুক্ত করা নতুন ডান সন্তানের বিচ্ছিন্ন করে পরে গাছটি পুনরুদ্ধার করা হয়। কোড সহ আমার আপডেট হওয়া পোস্টটি দেখুন।
brainydexter

1
চমৎকার স্কেচ, তবে আমি এখনও লুপের অবস্থা বুঝতে পারি না। প্রাক -> ডান! = বর্তমানের জন্য কেন পরীক্ষা করা দরকার?
No_name

6
আমি দেখছি না কেন এটি কাজ করে। আপনি এ মুদ্রণের পরে, তারপরে Y মূল হয়ে যায়, এবং আপনার এখনও বাম সন্তানের হিসাবে A থাকে। সুতরাং, আমরা আগের মতো একই পরিস্থিতিতে আছি। এবং আমরা এটির পুনরাবৃত্তি করি বাস্তবে এটি অসীম লুপের মতো দেখায়।
ব্যবহারকারী 678392

এটি কি ওয়াই এবং বি এর মধ্যে সংযোগ বিচ্ছিন্ন করে না? যখন এক্স বর্তমান হিসাবে সেট করা হয় এবং ওয়াই প্রাক হিসাবে সেট করা থাকে, তারপরে এটি বর্তমানের (এক্স) সন্ধান না হওয়া অবধি প্রাকের ডান সাবট্রিকে নীচে দেখবে এবং তারপরে এটি প্রাক => ডানকে ন্যূনাল হিসাবে সেট করে, যা বি ঠিক হবে? উপরে পোস্ট করা কোড অনুসারে
অচিন্ট

17

রিকার্সিভ ইন-অর্ডার ট্র্যাভেরসাল হল: (in-order(left)->key->in-order(right))। (এটি ডিএফএসের অনুরূপ)

আমরা যখন ডিএফএস করি তখন আমাদের কোথায় ব্যাকট্র্যাক করতে হবে তা জানতে হবে (এজন্য আমরা সাধারণত একটি স্ট্যাক রাখি)।

আমরা যখন কোনও প্যারেন্ট নোড দিয়ে যাচ্ছি তখন যেখানে আমাদের ব্যাকট্র্যাক করতে হবে -> আমরা নোডটি খুঁজে পাই যা আমাদের প্রয়োজন থেকে প্যারেন্ট ট্র্যাক করতে হবে এবং এর লিঙ্কটি প্যারেন্ট নোডের সাথে আপডেট করতে হবে।

আমরা কখন ব্যাকট্র্যাক করব? যখন আমরা আর যেতে পারি না। আমরা যখন আর যেতে পারি না? যখন কোনও বাম সন্তানের উপস্থিতি নেই।

আমরা কোথায় ফিরে যাব? বিজ্ঞপ্তি: সফলতা!

সুতরাং, আমরা বাম-সন্তানের পথ ধরে নোডগুলি অনুসরণ করে বর্তমান নোডের দিকে নির্দেশ করতে প্রতিটি ধাপে পূর্বসূরিকে সেট করুন। এইভাবে, পূর্বসূরীদের উত্তরসূরিদের লিঙ্ক থাকবে (ব্যাকট্র্যাকিংয়ের জন্য একটি লিঙ্ক)।

আমাদের ব্যাকট্র্যাকের প্রয়োজন না হওয়া পর্যন্ত আমরা বাম অনুসরণ করি। যখন আমাদের ব্যাকট্র্যাক করা দরকার তখন আমরা বর্তমান নোডটি প্রিন্ট করি এবং উত্তরসূরীর ডান লিঙ্কটি অনুসরণ করি।

যদি আমরা স্রেফ ব্যাকট্র্যাক করে ফেলেছি -> আমাদের ডান সন্তান অনুসরণ করতে হবে (আমাদের বাম সন্তানের সাথে সম্পন্ন করা হয়েছে)।

আমরা কীভাবে ব্যাকট্র্যাক করেছি তা কীভাবে বলব? বর্তমান নোডের পূর্বসূরি পান এবং এটির একটি সঠিক লিঙ্ক আছে কিনা তা পরীক্ষা করুন (এই নোডের সাথে)। যদি এটি থাকে - তবে আমরা এটি অনুসরণ করেছি। গাছটি পুনরুদ্ধার করতে লিঙ্কটি সরান।

যদি কোনও বাম লিঙ্ক না থাকে => আমরা ব্যাকট্র্যাক করি নি এবং বাম বাচ্চাদের অনুসরণ করা উচিত should

এখানে আমার জাভা কোডটি (দুঃখিত, এটি সি ++ নয়)

public static <T> List<T> traverse(Node<T> bstRoot) {
    Node<T> current = bstRoot;
    List<T> result = new ArrayList<>();
    Node<T> prev = null;
    while (current != null) {
        // 1. we backtracked here. follow the right link as we are done with left sub-tree (we do left, then right)
        if (weBacktrackedTo(current)) {
            assert prev != null;
            // 1.1 clean the backtracking link we created before
            prev.right = null;
            // 1.2 output this node's key (we backtrack from left -> we are finished with left sub-tree. we need to print this node and go to right sub-tree: inOrder(left)->key->inOrder(right)
            result.add(current.key);
            // 1.15 move to the right sub-tree (as we are done with left sub-tree).
            prev = current;
            current = current.right;
        }
        // 2. we are still tracking -> going deep in the left
        else {
            // 15. reached sink (the leftmost element in current subtree) and need to backtrack
            if (needToBacktrack(current)) {
                // 15.1 return the leftmost element as it's the current min
                result.add(current.key);
                // 15.2 backtrack:
                prev = current;
                current = current.right;
            }
            // 4. can go deeper -> go as deep as we can (this is like dfs!)
            else {
                // 4.1 set backtracking link for future use (this is one of parents)
                setBacktrackLinkTo(current);
                // 4.2 go deeper
                prev = current;
                current = current.left;
            }
        }
    }
    return result;
}

private static <T> void setBacktrackLinkTo(Node<T> current) {
    Node<T> predecessor = getPredecessor(current);
    if (predecessor == null) return;
    predecessor.right = current;
}

private static boolean needToBacktrack(Node current) {
    return current.left == null;
}

private static <T> boolean weBacktrackedTo(Node<T> current) {
    Node<T> predecessor = getPredecessor(current);
    if (predecessor == null) return false;
    return predecessor.right == current;
}

private static <T> Node<T> getPredecessor(Node<T> current) {
    // predecessor of current is the rightmost element in left sub-tree
    Node<T> result = current.left;
    if (result == null) return null;
    while(result.right != null
            // this check is for the case when we have already found the predecessor and set the successor of it to point to current (through right link)
            && result.right != current) {
        result = result.right;
    }
    return result;
}

4
আমি আপনার উত্তরটি অনেক পছন্দ করি কারণ এটি এই সমাধানটি সামনে আসার দিকে উচ্চ পর্যায়ের যুক্তি সরবরাহ করে!
কেএফএল

6

আমি এখানে অ্যালগরিদমের জন্য একটি অ্যানিমেশন তৈরি করেছি: https://docs.google.com/presentation/d/11GWAeUN0ckP7yjHrQkIB0WT9ZUhDBSa-WR0VsPU38fg/edit?usp=sharing

আশা করি এটি বুঝতে সহায়তা করবে। নীল বৃত্তটি কার্সার এবং প্রতিটি স্লাইডটি বাইরের যখন লুপের পুনরাবৃত্তি হয়।

মরিস ট্র্যাভারসালের জন্য এখানে কোড (আমি গিকসের জন্য এটি কপি এবং সংশোধন করেছি):

def MorrisTraversal(root):
    # Set cursor to root of binary tree
    cursor = root
    while cursor is not None:
        if cursor.left is None:
            print(cursor.value)
            cursor = cursor.right
        else:
            # Find the inorder predecessor of cursor
            pre = cursor.left
            while True:
                if pre.right is None:
                    pre.right = cursor
                    cursor = cursor.left
                    break
                if pre.right is cursor:
                    pre.right = None
                    cursor = cursor.right
                    break
                pre = pre.right
#And now for some tests. Try "pip3 install binarytree" to get the needed package which will visually display random binary trees
import binarytree as b
for _ in range(10):
    print()
    print("Example #",_)
    tree=b.tree()
    print(tree)
    MorrisTraversal(tree)

আপনার অ্যানিমেশন বেশ আকর্ষণীয়। দয়া করে এটিকে এমন একটি চিত্র তৈরি করার বিষয়টি বিবেচনা করুন যা আপনার পোস্টে অন্তর্ভুক্ত হবে কারণ বাহ্যিক লিঙ্কগুলি প্রায়শই কিছু সময়ের পরে মারা যায়।
ল্যানস্লট

1
অ্যানিমেশন সহায়ক!
ওয়াইফ্রেড

বাইনারি ট্রি গ্রন্থাগারের দুর্দান্ত স্প্রেডশিট এবং ব্যবহার। তবে কোডটি সঠিক নয়, এটি রুট নোডগুলি মুদ্রণ করতে ব্যর্থ। আপনার লাইনের print(cursor.value)পরে যোগ করতে হবেpre.right = None
সাতম

4
public static void morrisInOrder(Node root) {
        Node cur = root;
        Node pre;
        while (cur!=null){
            if (cur.left==null){
                System.out.println(cur.value);      
                cur = cur.right; // move to next right node
            }
            else {  // has a left subtree
                pre = cur.left;
                while (pre.right!=null){  // find rightmost
                    pre = pre.right;
                }
                pre.right = cur;  // put cur after the pre node
                Node temp = cur;  // store cur node
                cur = cur.left;  // move cur to the top of the new tree
                temp.left = null;   // original cur left be null, avoid infinite loops
            }        
        }
    }

আমি মনে করি এই কোডটি বুঝতে আরও ভাল হবে, অসীম লুপগুলি এড়াতে কেবল নাল ব্যবহার করুন, অন্য যাদু ব্যবহার করতে হবে না। এটি প্রিআর্ডারে সহজেই পরিবর্তন করা যেতে পারে।


1
সমাধানটি খুব ঝরঝরে তবে একটি সমস্যা আছে। নুথের মতে গাছটি শেষ পর্যন্ত পরিবর্তন করা উচিত নয়। এমনটি করে temp.left = nullগাছ হারিয়ে যাবে।
অঙ্কুর

এই পদ্ধতিটি বাইনারি গাছকে লিঙ্কযুক্ত তালিকায় রূপান্তর করার মতো জায়গায় ব্যবহার করা যেতে পারে।
সাইবার_রাজ

@ শান যা বলেছিলেন তার মতো, অ্যালগরিদমটি মূল গাছকে পরিবর্তন করা উচিত নয়। আপনার অ্যালগরিদম এটি অনুসরণ করার জন্য কাজ করার সময় এটি মূল গাছটি ধ্বংস করে। অতএব, এটি আসল অ্যালগরিদমের চেয়ে আসলে আলাদা এবং অতএব বিভ্রান্তিকর।
চাওএসএক্সডেমোনে

2

আমি মরিস ট্র্যাভারসালের খুব ভাল চিত্রের ব্যাখ্যা পেয়েছি ।

মরিস ট্র্যাভারসাল


ভবিষ্যতে লিঙ্কটি ভেঙে গেলে কেবলমাত্র লিঙ্কের উত্তরটি তার মান হারাবে, দয়া করে লিঙ্ক থেকে প্রাসঙ্গিক প্রসঙ্গটি উত্তরে যুক্ত করুন।
অরুণ বিনোথ

অবশ্যই। আমি তাড়াতাড়ি যোগ করব।
আশীষ রঞ্জন

1

আমি আশা করি নীচের সিউডো কোডটি আরও প্রকাশিত:

node = root
while node != null
    if node.left == null
        visit the node
        node = node.right
    else
        let pred_node be the inorder predecessor of node
        if pred_node.right == null /* create threading in the binary tree */
            pred_node.right = node
            node = node.left
        else         /* remove threading from the binary tree */
            pred_node.right = null 
            visit the node
            node = node.right

প্রশ্নের সি ++ কোড উল্লেখ করে অভ্যন্তরীণ লুপটি বর্তমান নোডের সৃজনশীল পূর্বসূরীর সন্ধান করে। একটি স্ট্যান্ডার্ড বাইনারি গাছে, পূর্বসূরীর ডান সন্তান অবশ্যই নাল হতে হবে, যখন থ্রেড সংস্করণে ডান সন্তানের অবশ্যই বর্তমান নোডের দিকে নির্দেশ করতে হবে। যদি ডান শিশুটি নাল হয় তবে এটি বর্তমান নোডে সেট করা থাকে, কার্যকরভাবে থ্রেডিং তৈরি করে , যা একটি রিটার্নিং পয়েন্ট হিসাবে ব্যবহৃত হয় যা অন্যথায় সাধারণত স্ট্যাকের উপরে রাখা উচিত। যদি ডান শিশুটি নাল না থাকে তবে অ্যালগরিদম নিশ্চিত করে যে মূল গাছটি পুনরুদ্ধার করা হয়েছে এবং তারপরে ডান সাবট্রিতে ট্র্যাভারসাল চালিয়ে যেতে হবে (এই ক্ষেত্রে এটি বাম সাবট্রিতে পরিদর্শন করা হয়েছিল বলে জানা যায়)।


0

পাইথন সলিউশন সময় জটিলতা: ও (এন) স্পেস জটিলতা: ও (1)

দুর্দান্ত মরিস ইনর্ডার ট্র্যাভারসাল ব্যাখ্যা

class Solution(object):
def inorderTraversal(self, current):
    soln = []
    while(current is not None):    #This Means we have reached Right Most Node i.e end of LDR traversal

        if(current.left is not None):  #If Left Exists traverse Left First
            pre = current.left   #Goal is to find the node which will be just before the current node i.e predecessor of current node, let's say current is D in LDR goal is to find L here
            while(pre.right is not None and pre.right != current ): #Find predecesor here
                pre = pre.right
            if(pre.right is None):  #In this case predecessor is found , now link this predecessor to current so that there is a path and current is not lost
                pre.right = current
                current = current.left
            else:                   #This means we have traverse all nodes left to current so in LDR traversal of L is done
                soln.append(current.val) 
                pre.right = None       #Remove the link tree restored to original here 
                current = current.right
        else:               #In LDR  LD traversal is done move to R  
            soln.append(current.val)
            current = current.right

    return soln

আমি দুঃখিত, তবে এটি দুর্ভাগ্যক্রমে প্রশ্নের সরাসরি উত্তর নয়। ওপি এটি কীভাবে কাজ করে তার একটি ব্যাখ্যা চেয়েছিল, বাস্তবায়ন নয়, সম্ভবত তারা নিজেরাই অ্যালগরিদম বাস্তবায়ন করতে চায় বলে। আপনার মন্তব্যগুলি এমন ব্যক্তির পক্ষে ভাল যা ইতিমধ্যে অ্যালগরিদম বোঝে, তবে ওপি এখনও তা করে না। এছাড়াও, নীতি হিসাবে, উত্তরগুলি কেবলমাত্র বাইরের কোনও সংস্থার সাথে লিঙ্ক করার পরিবর্তে স্বয়ংসম্পূর্ণ হওয়া উচিত, কারণ লিঙ্কটি সময়ের সাথে সাথে পরিবর্তন বা ভেঙে যেতে পারে। লিঙ্কগুলি অন্তর্ভুক্ত করা ঠিক আছে তবে আপনি যদি তা করেন তবে আপনার লিঙ্কটি কী সরবরাহ করছে তার অন্তত সংক্ষিপ্তসারও অন্তর্ভুক্ত করা উচিত।
বেনামে 1847

0

পিএফবি মরিস-অর্ডার ট্র্যাভারসাল ব্যাখ্যা।

  public class TreeNode
    {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val = 0, TreeNode left = null, TreeNode right = null)
        {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    class MorrisTraversal
    {
        public static IList<int> InOrderTraversal(TreeNode root)
        {
            IList<int> list = new List<int>();
            var current = root;
            while (current != null)
            {
                //When there exist no left subtree
                if (current.left == null)
                {
                    list.Add(current.val);
                    current = current.right;
                }
                else
                {
                    //Get Inorder Predecessor
                    //In Order Predecessor is the node which will be printed before
                    //the current node when the tree is printed in inorder.
                    //Example:- {1,2,3,4} is inorder of the tree so inorder predecessor of 2 is node having value 1
                    var inOrderPredecessorNode = GetInorderPredecessor(current);
                    //If the current Predeccessor right is the current node it means is already printed.
                    //So we need to break the thread.
                    if (inOrderPredecessorNode.right != current)
                    {
                        inOrderPredecessorNode.right = null;
                        list.Add(current.val);
                        current = current.right;
                    }//Creating thread of the current node with in order predecessor.
                    else
                    {
                        inOrderPredecessorNode.right = current;
                        current = current.left;
                    }
                }
            }

            return list;
        }

        private static TreeNode GetInorderPredecessor(TreeNode current)
        {
            var inOrderPredecessorNode = current.left;
            //Finding Extreme right node of the left subtree
            //inOrderPredecessorNode.right != current check is added to detect loop
            while (inOrderPredecessorNode.right != null && inOrderPredecessorNode.right != current)
            {
                inOrderPredecessorNode = inOrderPredecessorNode.right;
            }

            return inOrderPredecessorNode;
        }
    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.