কোন বাইনারি গাছে দুটি নোডের সর্বনিম্ন সাধারণ পূর্বপুরুষকে কীভাবে খুঁজে পাবেন?


187

এখানে বাইনারি ট্রি অগত্যা বাইনারি অনুসন্ধান ট্রি নাও হতে পারে।
কাঠামো হিসাবে গ্রহণ করা যেতে পারে -

struct node {
    int data;
    struct node *left;
    struct node *right;
};

বন্ধুর সাথে আমি সর্বাধিক সমাধানটি কাজ করতে পারি এই ধরণের কিছু - এই বাইনারি গাছটি
বিবেচনা করুন :

বাইনারি ট্রি

ইনঅর্ডার ট্র্যাভারসাল ফলন - 8, 4, 9, 2, 5, 1, 6, 3, 7

এবং পোস্টর্ডার ট্র্যাভার্সাল ফলন - 8, 9, 4, 5, 2, 6, 7, 3, 1

সুতরাং উদাহরণস্বরূপ, যদি আমরা 8 এবং 5 নোডের সাধারণ পূর্বপুরুষকে সন্ধান করতে চাই, তবে আমরা সমস্ত নোডের একটি তালিকা তৈরি করব যা ইনর্ডার ট্রি ট্রিভারসালে 8 থেকে 5 এর মধ্যে রয়েছে, যা এই ক্ষেত্রে ঘটে [4, 9] , 2]। তারপরে আমরা যাচাই করি যে তালিকার এই তালিকার কোন নোডটি পোস্টর্ডার ট্র্যাভার্সাল-এ শেষ দেখা যাচ্ছে, যা ২ Hence

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

তবে এটি একটি খুব অশোধিত পদ্ধতির এবং আমি নিশ্চিত নই যে এটি কোনও ক্ষেত্রে ভেঙে গেছে কিনা। এই সমস্যার আর কোনও (সম্ভবত আরও অনুকূল) সমাধান আছে কি?


6
কৌতূহলের বাইরে এর ব্যবহারিক ব্যবহার কী?
ডেভিড ব্রুনেলেল

19
@ ডেভিড: এলসিএ ক্যোয়ারির উত্তর দেওয়া বেশ কার্যকর। এলসিএ + প্রত্যয় গাছ = শক্তিশালী স্ট্রিং সম্পর্কিত আলগোরিদিম।

44
এবং আমি যখন অনুরূপ প্রশ্ন জিজ্ঞাসা করেছি তখন এটির সাক্ষাত্কার প্রশ্নের মতো মন্তব্যে ভোট দেওয়া হয়েছে। দ্বৈততা কি তাই? :(
কিছু_ অন্য_গুই

5
প্রশ্নের বিবরণ জন্য সিডেন্ট +1। :)
amod

5
@ ডেভিডব্রুনেল এলসিএ কম্পিউটিংয়ের একটি ব্যবহারিক প্রয়োগ: ওয়েব পৃষ্ঠাগুলি রেন্ডার করার সময় এটি একটি প্রয়োজনীয় গণনা, বিশেষত ক্যাসকেডিং স্টাইল শীট (সিএসএস) গণনা করার সময় যা কোনও নির্দিষ্ট ডিওএম উপাদানগুলির জন্য প্রযোজ্য।
zc22

উত্তর:


74

নিক জনসন সঠিক বলেছেন যে কোনও ও (এন) সময়ের জটিলতা অ্যালগরিদম আপনার পক্ষে সর্বোত্তম যা আপনি করতে পারেন যদি আপনার প্যারেন্ট পয়েন্টার না থাকে)) সেই অ্যালগোরিদমের একটি সাধারণ পুনরাবৃত্ত সংস্করণের জন্য কিন্ডিংয়ের পোস্টে কোডটি দেখুন যা ও (এন) সময়ে চলে ।

তবে মনে রাখবেন যে আপনার নোডের প্যারেন্ট পয়েন্টার থাকলে উন্নত অ্যালগরিদম সম্ভব is প্রশ্নে উভয় নোডের জন্য নোড থেকে শুরু করে এবং পিতামাতার সম্মুখ সন্নিবেশ করে মূল থেকে নোডের পাথ সম্বলিত একটি তালিকা তৈরি করুন।

সুতরাং আপনার উদাহরণে 8 এর জন্য, আপনি (পদক্ষেপগুলি দেখায়) পাবেন: {4}, {2, 4}, {1, 2, 4}

আপনার অন্যান্য নোডকে প্রশ্নে একই করুন, ফলস্বরূপ (পদক্ষেপগুলি দেখানো হয়নি): {1, 2}

এখন আপনি যে দুটি তালিকার প্রথম উপাদানটির সন্ধান করেছেন সেখানে দুটি তালিকার তুলনা করুন যেখানে তালিকার পার্থক্য রয়েছে, বা তালিকার মধ্যে একটির শেষ উপাদানটি, যেটি প্রথমে আসে।

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


গাছটি কীভাবে নির্মিত হয় নির্বিশেষে, যদি এটি কোনও অপারেশন হয়ে থাকে তবে আপনি গাছটির মাঝে এটি পরিবর্তন না করেই অনেক বার সঞ্চালন করেন, অন্যান্য অ্যালগরিদম রয়েছে যা আপনি ব্যবহার করতে পারেন যা ও (এন) [রৈখিক] সময় প্রস্তুতির প্রয়োজন, তবে তারপরে কোনও সন্ধান করুন জোড়া কেবল ও (1) [ধ্রুবক] সময় নেয়। এই অ্যালগরিদমগুলির রেফারেন্সের জন্য, উইকিপিডিয়ায় সর্বনিম্ন সাধারণ পূর্বপুরুষের সমস্যা পৃষ্ঠাটি দেখুন । (মূলত এই লিঙ্কটি পোস্ট করার জন্য জেসনকে ক্রেডিট)


1
প্যারেন্ট পয়েন্টার দেওয়া থাকলে তা কাজ করে। গাছের নোডগুলি আমার প্রশ্নের মতো কাঠামো হিসাবে রয়েছে - কেবল বাম / ডান সন্তানের পয়েন্টার, কোনও প্যারেন্ট পয়েন্টার। যদি কোনও প্যারেন্ট পয়েন্টার না পাওয়া যায় এবং গাছটি বাইনারি অনুসন্ধানের গাছ না হয় এবং কেবল একটি বাইনারি গাছ হয় তবে সেখানে কোনও ও (লগ (এন)) সমাধান রয়েছে?
সিদ্ধন্ত

2
যদি আপনার পিতামাতা এবং প্রদত্ত নোডের মধ্যে পাথ সন্ধানের কোনও বিশেষ উপায় না থাকে তবে এটি খুঁজে পেতে ও ও (এন) সময় লাগবে। এটি ও (লগ (এন)) সময় পাওয়া অসম্ভব করে দেবে। তবে ও (এন) এর এক সময় ব্যয়, ও (1) জুটির সন্ধানটি যাইহোক আপনার সেরা বেট হতে পারে যদি আপনি মাঝখানে গাছটি পরিবর্তন না করেই বহুবার এই ক্রিয়াকলাপটি চালিয়ে যাচ্ছেন। অন্যথায়, যদি সম্ভব হয় তবে আপনার প্যারেন্ট পয়েন্টার যুক্ত করা উচিত। এটি বেশ কয়েকটি সম্ভাব্য অ্যালগরিদম দ্রুত তৈরি করতে পারে, তবুও আমি নিশ্চিত যে এটি কোনও বিদ্যমান অ্যালগোরিদমের ক্রম পরিবর্তন করে না। আশাকরি এটা সাহায্য করবে.
কেভিন ক্যাথকার্ট

1
এই পদ্ধতির হে (1) মেমরি ব্যবহার করা যাবে না - Artelius এর (এবং অন্যদের) সমাধান দেখতে এ stackoverflow.com/questions/1594061/...
টম Sirgedas

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

1
@ ALBI O(h)কেবলমাত্র O(log(n))যদি গাছটি ভারসাম্যপূর্ণ হয়। যে কোনও গাছের জন্য, এটি বাইনারি হোক বা না হোক, যদি আপনার প্যারেন্ট পয়েন্টার থাকে তবে আপনি কোনও পাতাগুলি থেকে মূল পর্যন্ত O(h)সময় নির্ধারণ করতে পারেন , কেবল সময় সময় পিতামাতার পয়েন্টার অনুসরণ করে h। এটি আপনাকে পাতা থেকে মূলের পথ দেয়। যদি পাথগুলি স্ট্যাক হিসাবে সংরক্ষণ করা হয়, তবে স্ট্যাকটি পুনরাবৃত্তি করা আপনাকে শিকড় থেকে পাতায় যাওয়ার পথ দেয়। যদি আপনার প্যারেন্ট পয়েন্টারগুলির অভাব হয় এবং আপনার গাছের জন্য কোনও বিশেষ কাঠামো না থাকে তবে শিকড় থেকে পাতায় যাওয়ার পথটি খুঁজে পেতে O(n)সময় লাগে ।
কেভিন ক্যাথকার্ট

108

rootনোড থেকে শুরু করে নীচের দিকে অগ্রসর হওয়া যদি আপনি কোনও নোড খুঁজে পান যা হয় হয় pবা qতার সরাসরি শিশু হিসাবে থাকে তবে এটি এলসিএ। (সম্পাদনা করুন - এটি নোডের মানটি হয় pবা qএটি হওয়া উচিত , এটি ফিরিয়ে দিন Otherwise নইলে এটি ব্যর্থ হবে যখন একজনের pবা qঅন্যের প্রত্যক্ষ সন্তান হয়))

অন্যথায় যদি আপনি pতার ডান (বা বাম) সাবট্রির সাথে এবং qতার বামে (বা ডান) সাবট্রির সাথে একটি নোড খুঁজে পান তবে এটি এলসিএ।

স্থির কোডটি দেখে মনে হচ্ছে:

treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {

        // no root no LCA.
        if(!root) {
                return NULL;
        }

        // if either p or q is the root then root is LCA.
        if(root==p || root==q) {
                return root;
        } else {
                // get LCA of p and q in left subtree.
                treeNodePtr l=findLCA(root->left , p , q);

                // get LCA of p and q in right subtree.
                treeNodePtr r=findLCA(root->right , p, q);

                // if one of p or q is in leftsubtree and other is in right
                // then root it the LCA.
                if(l && r) {
                        return root;
                }
                // else if l is not null, l is LCA.
                else if(l) {
                        return l;
                } else {
                        return r;
                }
        }
}

নীচের কোডটি ব্যর্থ হয় যখন হয় অন্যের সরাসরি শিশু।

treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {

        // no root no LCA.
        if(!root) {
                return NULL;
        }

        // if either p or q is direct child of root then root is LCA.
        if(root->left==p || root->left==q || 
           root->right ==p || root->right ==q) {
                return root;
        } else {
                // get LCA of p and q in left subtree.
                treeNodePtr l=findLCA(root->left , p , q);

                // get LCA of p and q in right subtree.
                treeNodePtr r=findLCA(root->right , p, q);

                // if one of p or q is in leftsubtree and other is in right
                // then root it the LCA.
                if(l && r) {
                        return root;
                }
                // else if l is not null, l is LCA.
                else if(l) {
                        return l;
                } else {
                        return r;
                }
        }
}

কোড ইন অ্যাকশন


2
মার্জিত সমাধান, তবে মূল == পি || রুট == কি => রিটার্ন রুট বিটকে অতিমাত্রায় মনে হয়। যদি এটির মূলটি পি / কিউ হয়ে যায় তবে অন্যান্য চাওয়া নোডটি আসলে গাছে নেই?
আয়ান দুর্কান

15
আমার ধারণা এই কোডটি ব্যর্থ হয় যখন পি বা কিউ মান হয় যা বাইনারি গাছের মধ্যে নেই। আমি কি সঠিক? উদাহরণস্বরূপ, এলসিএ (8,20)। আপনার কোডটি ৮ টি রিটার্ন দেয় 8 তবে 20 বাইনারি গাছে উপস্থিত নেই
javaMan

3
এই সমাধানের জন্য ব্যয় কী? এটা দক্ষ? এটি পি এবং কিউ উভয়ই সন্ধান করার পরেও অনুসন্ধান চালিয়ে যেতে দেখা যায়। গাছের মধ্যে পি এবং কিউ অনন্য হতে পারে না কারণ এটি কোনও বিএসটি নয় এবং এতে নকল থাকতে পারে বলেই কি এই সম্ভাবনা রয়েছে?
মাইকেবি

3
@ মাইকেবি, এই সমাধানটি অবশ্যই ও (এন), কারণ আপনি প্রতিটি নোডকে সবচেয়ে খারাপ ক্ষেত্রে একবারেই অতিক্রম করেন। পিটার লি, প্যারেন্ট পয়েন্টার ব্যবহার না করে আপনি এটি তৈরি করতে পারেন এটি সবচেয়ে দক্ষ। আপনার কি আরও ভাল সমাধান আছে?
gsingh2011

8
প্রথম অসম্পূর্ণ সমাধানটি মুছে ফেলা উচিত যাতে এটি বিভ্রান্তিকর না হয়
জিনান জিং

50

এখানে জাভাতে ওয়ার্কিং কোড রয়েছে Here

public static Node LCA(Node root, Node a, Node b) {
   if (root == null) {
       return null;
   }

   // If the root is one of a or b, then it is the LCA
   if (root == a || root == b) {
       return root;
   }

   Node left = LCA(root.left, a, b);
   Node right = LCA(root.right, a, b);

   // If both nodes lie in left or right then their LCA is in left or right,
   // Otherwise root is their LCA
   if (left != null && right != null) {
      return root;
   }

   return (left != null) ? left : right; 
}

4
গাছে নোড না থাকলে এটি কাজ করে না।
প্রতীক খাদলোয়া

প্রদত্ত গাছটি যদি একটি বিএসটি হয় তবে আপনি কি আপনার কোডটি অপ্টিমাইজ করবেন?
মোনা জালাল

1
"মূলটি যদি এক বা বি এর একটি হয় তবে এটি এলসিএ।" এটি সত্য নাও হতে পারে। আপনি এই মুহুর্তে যা জানেন তা হ'ল এলসিএ খুঁজতে আপনার কোনও বাচ্চাদের পরীক্ষা করার দরকার নেই। এটি ঘটেছিল কারণ আমরা পরে যাচাই করতে পারি যে মূলের পিতামাতার জন্য, উভয় শাখায় ম্যাচ ছিল (এলসিএ অভিভাবক) অথবা তাদের মধ্যে একটির (এই ক্ষেত্রে যে কোনওটি এলসিএ হতে পারে, বা আরও বৃহত্তর পূর্বপুরুষটি এলসিএ হতে পারে) )।

28

এখনও পর্যন্ত দেওয়া উত্তরগুলি পুনরাবৃত্তি বা স্টোর ব্যবহার করে, উদাহরণস্বরূপ, স্মৃতিতে একটি পথ।

আপনার খুব গভীর গাছ থাকলে এই উভয়ই পন্থা ব্যর্থ হতে পারে।

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

কোডটি এখানে:

findLowestCommonAncestor(v,w):
  depth_vv = depth(v);
  depth_ww = depth(w);

  vv = v; 
  ww = w;

  while( depth_vv != depth_ww ) {
    if ( depth_vv > depth_ww ) {
      vv = parent(vv);
      depth_vv--;
    else {
      ww = parent(ww);
      depth_ww--;
    }
  }

  while( vv != ww ) {
    vv = parent(vv);
    ww = parent(ww);
  }

  return vv;    

এই অ্যালগরিদমের সময় জটিলতা হ'ল: ও (এন)। এই অ্যালগরিদমের স্পেস জটিলতা হ'ল: ও (1)।

গভীরতার গণনা সম্পর্কে, আমরা প্রথমে সংজ্ঞাটি মনে করতে পারি: যদি v মূল হয়, গভীরতা (v) = 0; অন্যথায়, গভীরতা (v) = গভীরতা (পিতা বা মাতা (v)) + ১। আমরা নীচের হিসাবে গভীরতা গণনা করতে পারি:

depth(v):
  int d = 0;
  vv = v;
  while ( vv is not root ) {
    vv = parent(vv);
    d++;
  }
  return d;

6
বাইনারি গাছগুলির সাধারণত পিতামণ্ডলের উপাদানগুলির একটি উল্লেখ থাকে না। পিতামাতার রেফারেন্স যুক্ত করা কোনও সমস্যা ছাড়াই করা যেতে পারে তবে আমি ও (এন) সহায়ক স্থান বিবেচনা করব।
জন কুরলাক

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

8

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

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


8

এটি এখানে পাওয়া যাবে: - http://goursaha.freeoda.com/DataStructure/LowestCommonAncestor.html

 tree_node_type *LowestCommonAncestor(
 tree_node_type *root , tree_node_type *p , tree_node_type *q)
 {
     tree_node_type *l , *r , *temp;
     if(root==NULL)
     {
        return NULL;
     }

    if(root->left==p || root->left==q || root->right ==p || root->right ==q)
    {
        return root;
    }
    else
    {
        l=LowestCommonAncestor(root->left , p , q);
        r=LowestCommonAncestor(root->right , p, q);

        if(l!=NULL && r!=NULL)
        {
            return root;
        }
        else
        {
        temp = (l!=NULL)?l:r;
        return temp;
        }
    }
}

আপনি দয়া করে আমাকে বলতে পারেন যে পি উপস্থিত থাকলে আপনার কোডটি কীভাবে আচরণ করবে তবে কিউ গাছটিতে উপস্থিত নেই? একইভাবে পি এবং কিউ উভয়ই উপস্থিত নেই। ধন্যবাদ !!!
চেষ্টা করছেন

সময়ের নিরিখে বড় ওটি কী? আমি মনে করি এটি হে (এন * লগ (এন)), দুটি ধীর slow
পিটার লি


6

দুটি নোডের সাধারণ পূর্বপুরুষের সন্ধান করতে: -

  • বাইনারি অনুসন্ধান ব্যবহার করে গাছের মধ্যে দেওয়া নোড নোড 1 সন্ধান করুন এবং এই প্রক্রিয়াটিতে দেখা সমস্ত নোডকে একটি অ্যারে এ 1 এ সংরক্ষণ করুন। সময় - ও (লগইন), স্থান - ও (লগইন)
  • বাইনারি অনুসন্ধান ব্যবহার করে গাছের মধ্যে প্রদত্ত নোড 2 সন্ধান করুন এবং এ প্রক্রিয়াটিতে দেখা সমস্ত নোডকে একটি অ্যারে এ 2 বলে সংরক্ষণ করুন। সময় - ও (লগইন), স্থান - ও (লগইন)
  • যদি এ 1 তালিকা বা এ 2 তালিকা খালি থাকে তবে একটি নোডের অস্তিত্ব নেই তাই কোনও সাধারণ পূর্বপুরুষ নেই।
  • যদি এ 1 তালিকা এবং এ 2 তালিকাটি খালি না থাকে তবে আপনি মিল না পাওয়া নোড না পাওয়া পর্যন্ত তালিকায় সন্ধান করুন। যত তাড়াতাড়ি আপনি এই জাতীয় নোড পাবেন তারপরে নোডের আগে সাধারণ পূর্বপুরুষ।

এটি বাইনারি অনুসন্ধান গাছের জন্য কাজ করবে।


2
তিনি পরিষ্কার করে বলেছিলেন গাছটি কোনও বিএসটি অগত্যা নয়।
পিটার লি

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


3

নীচের পুনরাবৃত্তির অ্যালগরিদম ভারসাম্যযুক্ত বাইনারি গাছের জন্য ও (লগ এন) এ চলবে। গেটএলসিএ () ফাংশনে পাস হওয়া নোডগুলির মধ্যে যদি দুটিই মূলের সমান হয় তবে রুটটি এলসিএ হবে এবং কোনও পুনর্বিবেচনা করার প্রয়োজন হবে না।

পরীক্ষার মামলা। [1] দুটি নোড n1 এবং n2 গাছের মধ্যে রয়েছে এবং তাদের পিতামাতার নোডের উভয় পাশে থাকে। [2] হয় নোড এন 1 বা এন 2 মূল হয়, এলসিএ মূল হয়। [3] গাছটিতে কেবল এন 1 বা এন 2 রয়েছে, এলসিএ হয় গাছের মূলের বাম সাবট্রির মূল নোড বা এলসিএ হবে গাছের মূলের ডান সাবট্রির মূল নোড।

[4] n1 বা n2 দুটিও গাছে নেই, কোনও এলসিএ নেই। [5] এন 1 এবং এন 2 উভয়ই একে অপরের পাশে সোজা রেখায় থাকে, এলসিএ হয় এন 1 বা এন 2 এর মধ্যে থাকবে যা কখনও গাছের গোড়ায় বন্ধ থাকে।

//find the search node below root
bool findNode(node* root, node* search)
{
    //base case
    if(root == NULL)
        return false;

    if(root->val == search->val)
        return true;

    //search for the node in the left and right subtrees, if found in either return true
    return (findNode(root->left, search) || findNode(root->right, search));
}

//returns the LCA, n1 & n2 are the 2 nodes for which we are
//establishing the LCA for
node* getLCA(node* root, node* n1, node* n2)
{
    //base case
    if(root == NULL)
        return NULL;

    //If 1 of the nodes is the root then the root is the LCA
    //no need to recurse.
    if(n1 == root || n2 == root)
        return root;

    //check on which side of the root n1 and n2 reside
    bool n1OnLeft = findNode(root->left, n1);
    bool n2OnLeft = findNode(root->left, n2);

    //n1 & n2 are on different sides of the root, so root is the LCA
    if(n1OnLeft != n2OnLeft)
        return root;

    //if both n1 & n2 are on the left of the root traverse left sub tree only
    //to find the node where n1 & n2 diverge otherwise traverse right subtree
    if(n1OnLeft)
        return getLCA(root->left, n1, n2);
    else
        return getLCA(root->right, n1, n2);
}

3

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

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

Iterative, O (1) স্থান

পাইথন

def lowestCommonAncestor(self, root, p, q):
    while (root.val - p.val) * (root.val - q.val) > 0:
        root = (root.left, root.right)[p.val > root.val]
    return root

জাভা

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    while ((root.val - p.val) * (root.val - q.val) > 0)
        root = p.val < root.val ? root.left : root.right;
    return root;
}

অতিরিক্ত প্রবাহের ক্ষেত্রে, আমি (রুট.ওয়াল - (দীর্ঘ) পি। ওভাল) * (রুট.ভাল - (দীর্ঘ) কি.ওয়াল) করব

পুনরাবৃত্তি

পাইথন

def lowestCommonAncestor(self, root, p, q):
    next = p.val < root.val > q.val and root.left or \
           p.val > root.val < q.val and root.right
    return self.lowestCommonAncestor(next, p, q) if next else root

জাভা

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    return (root.val - p.val) * (root.val - q.val) < 1 ? root :
           lowestCommonAncestor(p.val < root.val ? root.left : root.right, p, q);
}

2
Node *LCA(Node *root, Node *p, Node *q) {
  if (!root) return NULL;
  if (root == p || root == q) return root;
  Node *L = LCA(root->left, p, q);
  Node *R = LCA(root->right, p, q);
  if (L && R) return root;  // if p and q are on both sides
  return L ? L : R;  // either one of p,q is on one side OR p,q is not in L&R subtrees
}

2

এই গাছ বিবেচনা করুন এখানে চিত্র বর্ণনা লিখুন

যদি আমরা পোস্টর্ডার এবং প্রিঅর্ডার ট্র্যাভারসাল করি এবং প্রথম উপস্থিত সাধারণ পূর্বসূরি এবং উত্তরসূরি খুঁজে পাই তবে আমরা সাধারণ পূর্বপুরুষকে পাই।

পোস্টর্ডার => 0,2,1,5,4,6,3,8,10,11,9,14,15,13,12,7 প্রির্ডার => 7,3,1,0,2,6,4 , 5,12,9,8,11,10,13,15,14

  • যেমন: ১

8,11 এর মধ্যে সর্বনিম্ন সাধারণ পূর্বপুরুষ

পোস্টর্ডারে আমরা 8> এবং প্রির্ডারে 8 এবং 11 এর পরে => 9,14,15,13,12,7 আছে => 7,3,1,0,2,6,4,5,12,9 8 ও 11 এর আগে

9 হ'ল প্রথম সাধারণ সংখ্যা যা পোস্টারর্ডারে 8 এবং 11 এর পরে এবং প্রির্ডারে 8 এবং 11 এর আগে ঘটে তাই 9 টি উত্তর

  • যেমন: 2

5,10 এর সর্বনিম্ন সাধারণ পূর্বপুরুষ

11,9,14,15,13,12,7 পোস্টর্ডারে 7,3,1,0,2,6,4 প্রির্ডারে

Post হ'ল প্রথম সংখ্যা যা 5,10 এর পরে পোস্টর্ডারে এবং 5,10 প্রির্ডারে আগে ঘটে তাই 7 টি উত্তর


2

যদি নোড এক্সের বাচ্চাদের সাথে পূর্ণ * বাইনারি গাছ থাকে তবে এটি করার আরও দ্রুত উপায়ের চেয়ে 2 * x এবং 2 * x + 1

int get_bits(unsigned int x) {
  int high = 31;
  int low = 0,mid;
  while(high>=low) {
    mid = (high+low)/2;
    if(1<<mid==x)
      return mid+1;
    if(1<<mid<x) {
      low = mid+1;
    }
    else {
      high = mid-1;
    }
  }
  if(1<<mid>x)
    return mid;
  return mid+1;
}

unsigned int Common_Ancestor(unsigned int x,unsigned int y) {

  int xbits = get_bits(x);
  int ybits = get_bits(y);
  int diff,kbits;
  unsigned int k;
  if(xbits>ybits) {
    diff = xbits-ybits;
    x = x >> diff;
  }
  else if(xbits<ybits) {
    diff = ybits-xbits;
    y = y >> diff;
  }
  k = x^y;
  kbits = get_bits(k);
  return y>>kbits;  
}

এটা কিভাবে কাজ করে

  1. এক্স এবং ওয়াকে উপস্থাপনের জন্য প্রয়োজনীয় বিট পান যা বাইনারি অনুসন্ধান ব্যবহার করে ও (লগ (32))
  2. এক্স ও ওয়াইজের বাইনারি সংকেতের সাধারণ উপসর্গ হ'ল সাধারণ পূর্বপুরুষ
  3. বিটগুলির বৃহত্তর সংখ্যা দ্বারা যে কোনওটি প্রতিনিধিত্ব করা হয় কে >> ডিফ দ্বারা একই বিটে আনা হয়
  4. k = x ^ y এক্স ও y এর সাধারণ উপসর্গ মুছে ফেলে
  5. বাকি প্রত্যয় উপস্থাপন বিটস সন্ধান করুন
  6. সাধারণ পূর্বপুরুষ যা সাধারণ উপসর্গ পেতে x বা y প্রত্যয় বিট দ্বারা শিফট করুন।

এটি কাজ করে কারণ দুটি সংখ্যা সমান না হওয়া পর্যন্ত মূলত দুটি বৃহত সংখ্যাকে দুটি পুনরাবৃত্তভাবে ভাগ করে। এই সংখ্যাটি সাধারণ পূর্বপুরুষ। বিভাজক কার্যকরভাবে ডান শিফট অপশন হয়। সুতরাং নিকটতম পূর্বপুরুষের সন্ধানের জন্য আমাদের দুটি সংখ্যার সাধারণ উপসর্গ খুঁজে পাওয়া দরকার


2

স্কেলে, আপনি এটি করতে পারেন:

  abstract class Tree
  case class Node(a:Int, left:Tree, right:Tree) extends Tree
  case class Leaf(a:Int) extends Tree

  def lca(tree:Tree, a:Int, b:Int):Tree = {
    tree match {
      case Node(ab,l,r) => {
        if(ab==a || ab ==b) tree else {
          val temp = lca(l,a,b);
          val temp2 = lca(r,a,b);
          if(temp!=null && temp2 !=null)
            tree
          else if (temp==null && temp2==null)
            null
          else if (temp==null) r else l
        }

      }
      case Leaf(ab) => if(ab==a || ab ==b) tree else null
    }
  }

1
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null || root == p || root == q){
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        return left == null ? right : right == null ? left : root;
    }

0

এটি করার সি ++ উপায় এখানে। যতটা সম্ভব বোঝার জন্য অ্যালগরিদম রাখার চেষ্টা করেছেন:

// Assuming that `BinaryNode_t` has `getData()`, `getLeft()` and `getRight()`
class LowestCommonAncestor
{
  typedef char type;    
  // Data members which would behave as place holders
  const BinaryNode_t* m_pLCA;
  type m_Node1, m_Node2;

  static const unsigned int TOTAL_NODES = 2;

  // The core function which actually finds the LCA; It returns the number of nodes found
  // At any point of time if the number of nodes found are 2, then it updates the `m_pLCA` and once updated, we have found it!
  unsigned int Search (const BinaryNode_t* const pNode)
  {
    if(pNode == 0)
      return 0;

    unsigned int found = 0;

    found += (pNode->getData() == m_Node1);
    found += (pNode->getData() == m_Node2);

    found += Search(pNode->getLeft()); // below condition can be after this as well
    found += Search(pNode->getRight());

    if(found == TOTAL_NODES && m_pLCA == 0)
      m_pLCA = pNode;  // found !

    return found;
  }

public:
  // Interface method which will be called externally by the client
  const BinaryNode_t* Search (const BinaryNode_t* const pHead,
                              const type node1,
                              const type node2)
  {
    // Initialize the data members of the class
    m_Node1 = node1;
    m_Node2 = node2;
    m_pLCA = 0;

    // Find the LCA, populate to `m_pLCANode` and return
    (void) Search(pHead);
    return m_pLCA;
  }
};

এটি কিভাবে ব্যবহার করতে:

LowestCommonAncestor lca;
BinaryNode_t* pNode = lca.Search(pWhateverBinaryTreeNodeToBeginWith);
if(pNode != 0)
  ...

0

নিম্নতম সাধারণ পূর্বসূরিটি খুঁজে পাওয়ার সহজতম উপায়টি নিম্নলিখিত অ্যালগরিদমটি ব্যবহার করে:

রুট নোড পরীক্ষা করুন

যদি মান 1 এবং মান 2 রুট নোডের মানটি কঠোরভাবে কম হয় 
    বাম সাবট্রি পরীক্ষা করুন
অন্যথায় যদি মান 1 এবং মান 2 রুট নোডের মানের চেয়ে কঠোরতর হয় 
    ডান সাবট্রি পরীক্ষা করুন
আর
    প্রত্যাবর্তন মূল
public int LCA(TreeNode root, int value 1, int value 2) {
    while (root != null) {
       if (value1 < root.data && value2 < root.data)
           return LCA(root.left, value1, value2);
       else if (value2 > root.data && value2 2 root.data)
           return LCA(root.right, value1, value2);
       else
           return root
    }

    return null;
} 

6
এটি বিএসটি নয়!
পিটার লি

0

আমি একটি সমাধান খুঁজে পেয়েছি

  1. অর্ডার নিন
  2. প্রির্ডার নিন
  3. পোস্টর্ডার নিন

3 টি ট্র্যাভারসালের উপর নির্ভর করে আপনি সিদ্ধান্ত নিতে পারেন কে এলসিএ। এলসিএ থেকে উভয় নোডের দূরত্ব সন্ধান করুন। এই দুটি দূরত্ব যোগ করুন, যার উত্তর।


0

আমি যা মনে করি তা এখানেই

  1. মুষ্টি নোডের জন্য রুটটি সন্ধান করুন, এআর 1 এ সংরক্ষণ করুন।
  2. 2 নোডের জন্য রুটটি সন্ধান করা শুরু করুন, এমনটি করার সময় রুট থেকে আর্ট 1 পর্যন্ত প্রতিটি মান পরীক্ষা করুন।
  3. মান পৃথক হলে প্রস্থান করুন exit পুরানো ম্যাচ করা মান হ'ল এলসিএ।

জটিলতা: পদক্ষেপ 1: ও (এন), পদক্ষেপ 2 = ~ ও (এন), মোট = ~ ও (এন)।


0

এখানে রেফারেন্সের জন্য সি # (। নেট) এ দুটি পন্থা (উপরে আলোচনা করা হয়েছে):

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

  2. উভয় নোড (ও (এন)) অনুসন্ধান করা, এবং পাথের ট্র্যাক রাখা (অতিরিক্ত স্থান ব্যবহার করে - সুতরাং, # 1 সম্ভবত বর্ধনের গাছটি সুষম হয় তবে অতিরিক্ত মেমরির খরচ ঠিক তেমন হবে বলে ভেবেও স্থান সম্ভবত নগণ্য বলে মনে হয় হে (লগ (এন))।

    যাতে পাথগুলি তুলনা করা হয় (স্বীকৃত উত্তরের সাথে অবশ্যই অনুরূপ - তবে পাথরগুলি পয়েন্টার নোডটি বাইনারি ট্রি নোডে উপস্থিত না ধরে ধরে গণনা করা হয়)

  3. কেবলমাত্র সমাপ্তির জন্য ( প্রশ্নের সাথে সম্পর্কিত নয় ), বিএসটিতে এলসিএ (ও (লগ (এন))

  4. টেস্ট

পুনরাবৃত্তি:

private BinaryTreeNode LeastCommonAncestorUsingRecursion(BinaryTreeNode treeNode, 
            int e1, int e2)
        {
            Debug.Assert(e1 != e2);
            
            if(treeNode == null)
            {
                return null;
            }
            if((treeNode.Element == e1)
                || (treeNode.Element == e2))
            {
                //we don't care which element is present (e1 or e2), we just need to check 
                //if one of them is there
                return treeNode;
            }
            var nLeft = this.LeastCommonAncestorUsingRecursion(treeNode.Left, e1, e2);
            var nRight = this.LeastCommonAncestorUsingRecursion(treeNode.Right, e1, e2);
            if(nLeft != null && nRight != null)
            {
                //note that this condition will be true only at least common ancestor
                return treeNode;
            }
            else if(nLeft != null)
            {
                return nLeft;
            }
            else if(nRight != null)
            {
                return nRight;
            }
            return null;
        }

যেখানে উপরে ব্যক্তিগত পুনরাবৃত্তি সংস্করণটি সর্বজনীন পদ্ধতি অনুসরণ করে অনুরোধ করা হয়েছে:

public BinaryTreeNode LeastCommonAncestorUsingRecursion(int e1, int e2)
        {
            var n = this.FindNode(this._root, e1);
            if(null == n)
            {
                throw new Exception("Element not found: " + e1);
            }
            if (e1 == e2)
            {   
                return n;
            }
            n = this.FindNode(this._root, e2);
            if (null == n)
            {
                throw new Exception("Element not found: " + e2);
            }
            var node = this.LeastCommonAncestorUsingRecursion(this._root, e1, e2);
            if (null == node)
            {
                throw new Exception(string.Format("Least common ancenstor not found for the given elements: {0},{1}", e1, e2));
            }
            return node;
        }

উভয় নোডের পাথের উপর নজর রেখে সমাধান:

public BinaryTreeNode LeastCommonAncestorUsingPaths(int e1, int e2)
        {
            var path1 = new List<BinaryTreeNode>();
            var node1 = this.FindNodeAndPath(this._root, e1, path1);
            if(node1 == null)
            {
                throw new Exception(string.Format("Element {0} is not found", e1));
            }
            if(e1 == e2)
            {
                return node1;
            }
            List<BinaryTreeNode> path2 = new List<BinaryTreeNode>();
            var node2 = this.FindNodeAndPath(this._root, e2, path2);
            if (node1 == null)
            {
                throw new Exception(string.Format("Element {0} is not found", e2));
            }
            BinaryTreeNode lca = null;
            Debug.Assert(path1[0] == this._root);
            Debug.Assert(path2[0] == this._root);
            int i = 0;
            while((i < path1.Count)
                && (i < path2.Count)
                && (path2[i] == path1[i]))
            {
                lca = path1[i];
                i++;
            }
            Debug.Assert(null != lca);
            return lca;
        }

যেখানে FindNodeAndPath হিসাবে সংজ্ঞায়িত করা হয়েছে

private BinaryTreeNode FindNodeAndPath(BinaryTreeNode node, int e, List<BinaryTreeNode> path)
        {
            if(node == null)
            {
                return null;
            }
            if(node.Element == e)
            {
                path.Add(node);
                return node;
            }
            var n = this.FindNodeAndPath(node.Left, e, path);
            if(n == null)
            {
                n = this.FindNodeAndPath(node.Right, e, path);
            }
            if(n != null)
            {
                path.Insert(0, node);
                return n;
            }
            return null;
        }

বিএসটি (এলসিএ) - সম্পর্কিত নয় (কেবল রেফারেন্সের জন্য সম্পূর্ণ হওয়ার জন্য)

public BinaryTreeNode BstLeastCommonAncestor(int e1, int e2)
        {
            //ensure both elements are there in the bst
            var n1 = this.BstFind(e1, throwIfNotFound: true);
            if(e1 == e2)
            {
                return n1;
            }
            this.BstFind(e2, throwIfNotFound: true);
            BinaryTreeNode leastCommonAcncestor = this._root;
            var iterativeNode = this._root;
            while(iterativeNode != null)
            {
                if((iterativeNode.Element > e1 ) && (iterativeNode.Element > e2))
                {
                    iterativeNode = iterativeNode.Left;
                }
                else if((iterativeNode.Element < e1) && (iterativeNode.Element < e2))
                {
                    iterativeNode = iterativeNode.Right;
                }
                else
                {
                    //i.e; either iterative node is equal to e1 or e2 or in between e1 and e2
                    return iterativeNode;
                }
            }
            //control will never come here
            return leastCommonAcncestor;
        }

ইউনিট টেস্ট

[TestMethod]
        public void LeastCommonAncestorTests()
        {
            int[] a = { 13, 2, 18, 1, 5, 17, 20, 3, 6, 16, 21, 4, 14, 15, 25, 22, 24 };
            int[] b = { 13, 13, 13, 2, 13, 18, 13, 5, 13, 18, 13, 13, 14, 18, 25, 22};
            BinarySearchTree bst = new BinarySearchTree();
            foreach (int e in a)
            {
                bst.Add(e);
                bst.Delete(e);
                bst.Add(e);
            }
            for(int i = 0; i < b.Length; i++)
            {
                var n = bst.BstLeastCommonAncestor(a[i], a[i + 1]);
                Assert.IsTrue(n.Element == b[i]);
                var n1 = bst.LeastCommonAncestorUsingPaths(a[i], a[i + 1]);
                Assert.IsTrue(n1.Element == b[i]);
                Assert.IsTrue(n == n1);
                var n2 = bst.LeastCommonAncestorUsingRecursion(a[i], a[i + 1]);
                Assert.IsTrue(n2.Element == b[i]);
                Assert.IsTrue(n2 == n1);
                Assert.IsTrue(n2 == n);
            }
        }

0

ছদ্ম কোডে আগ্রহী কেউ যদি (বিশ্ববিদ্যালয়ের হোম ওয়ার্কের জন্য) থাকে তবে তা এখানে।

GETLCA(BINARYTREE BT, NODE A, NODE  B)
IF Root==NIL
    return NIL
ENDIF

IF Root==A OR root==B
    return Root
ENDIF

Left = GETLCA (Root.Left, A, B)
Right = GETLCA (Root.Right, A, B)

IF Left! = NIL AND Right! = NIL
    return root
ELSEIF Left! = NIL
    Return Left
ELSE
    Return Right
ENDIF

0

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

বাইনারি গাছের মধ্যে সর্বনিম্ন সাধারণ পূর্বপুরুষের সন্ধানের জন্য একটি ফাংশন বাস্তবায়নের জন্য এটি একটি ওয়ার্কিং সি কোড। আমি সমস্ত ইউটিলিটি ফাংশন ইত্যাদি সরবরাহ করছি, তবে দ্রুত বোঝার জন্য কমনএনস্টোর () এ ঝাঁপ দাও।

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <math.h>

static inline int min (int a, int b)
{
    return ((a < b) ? a : b);
}
static inline int max (int a, int b)
{
    return ((a > b) ? a : b);
}

typedef struct node_ {
    int value;
    struct node_ * left;
    struct node_ * right;
} node;

#define MAX 12

int IN_ORDER[MAX] = {0};
int POST_ORDER[MAX] = {0};

createNode(int value) 
{
    node * temp_node = (node *)malloc(sizeof(node));
    temp_node->left = temp_node->right = NULL;
    temp_node->value = value;
    return temp_node;
}

node *
insert(node * root, int value)
{
    if (!root) {
        return createNode(value);
    }

    if (root->value > value) {
        root->left = insert(root->left, value);
    } else {
        root->right = insert(root->right, value);
    }

    return root;
}


/* Builds inorder traversal path in the IN array */
void
inorder(node * root, int * IN)
{
    static int i = 0;

    if (!root) return;

    inorder(root->left, IN);
    IN[i] = root->value;
    i++;
    inorder(root->right, IN);
}

/* Builds post traversal path in the POST array */

void
postorder (node * root, int * POST)
{
    static int i = 0;

    if (!root) return;

    postorder(root->left, POST);
    postorder(root->right, POST);
    POST[i] = root->value;
    i++;
}


int
findIndex(int * A, int value)
{
    int i = 0;
    for(i = 0; i< MAX; i++) {
        if(A[i] == value) return i;
    }
}
int
CommonAncestor(int val1, int val2)
{
    int in_val1, in_val2;
    int post_val1, post_val2;
    int j=0, i = 0; int max_index = -1;

    in_val1 = findIndex(IN_ORDER, val1);
    in_val2 = findIndex(IN_ORDER, val2);
    post_val1 = findIndex(POST_ORDER, val1);
    post_val2 = findIndex(POST_ORDER, val2);

    for (i = min(in_val1, in_val2); i<= max(in_val1, in_val2); i++) {
        for(j = 0; j < MAX; j++) {
            if (IN_ORDER[i] == POST_ORDER[j]) {
                if (j > max_index) {
                    max_index = j;
                }
            }
        }
    }
    printf("\ncommon ancestor of %d and %d is %d\n", val1, val2, POST_ORDER[max_index]);
    return max_index;
}
int main()
{
    node * root = NULL; 

    /* Build a tree with following values */
    //40, 20, 10, 30, 5, 15, 25, 35, 1, 80, 60, 100
    root = insert(root, 40);
    insert(root, 20);
    insert(root, 10);
    insert(root, 30);
    insert(root, 5);
    insert(root, 15);
    insert(root, 25);
    insert(root, 35);
    insert(root, 1);
    insert(root, 80);
    insert(root, 60);
    insert(root, 100);

    /* Get IN_ORDER traversal in the array */
    inorder(root, IN_ORDER);

    /* Get post order traversal in the array */
    postorder(root, POST_ORDER);

    CommonAncestor(1, 100);


}

0

আরও একটি পদ্ধতির হতে পারে। তবে এটি উত্তর হিসাবে ইতিমধ্যে প্রস্তাবিত মত দক্ষ নয়।

  • নোড এন 1 এর জন্য একটি পাথ ভেক্টর তৈরি করুন।

  • নোড এন 2 এর জন্য একটি দ্বিতীয় পাথ ভেক্টর তৈরি করুন।

  • সেই পথ থেকে সেট নোডকে বোঝানো পাথ ভেক্টরটি প্রশ্নযুক্ত নোডে পৌঁছানোর জন্য অতিক্রম করবে।

  • উভয় পথের ভেক্টরগুলির সাথে তুলনা করুন। যে সূচকটি তারা মেলে না, সেই সূচকে নোডটি ফেরত পাঠাবে - 1. এটি এলসিএ দেবে।

এই পদ্ধতির পক্ষে মত:

পাথ ভেক্টর গণনার জন্য গাছটিকে দু'বার অতিক্রম করতে হবে। পাথ ভেক্টরগুলি সঞ্চয় করার জন্য অ্যাডিশনাল হে (এইচ) স্থানের প্রয়োজন।

তবে এটি কার্যকর করা এবং পাশাপাশি বুঝতেও সহজ।

পাথ ভেক্টর গণনার জন্য কোড:

private boolean findPathVector (TreeNode treeNode, int key, int pathVector[], int index) {

        if (treeNode == null) {
            return false;
        }

        pathVector [index++] = treeNode.getKey ();

        if (treeNode.getKey () == key) {
            return true;
        }
        if (findPathVector (treeNode.getLeftChild (), key, pathVector, index) || 
            findPathVector (treeNode.getRightChild(), key, pathVector, index)) {

            return true;        
        }

        pathVector [--index] = 0;
        return false;       
    }

0

এই মত চেষ্টা করুন

node * lca(node * root, int v1,int v2)
{

if(!root) {
            return NULL;
    }
    if(root->data == v1 || root->data == v2) {
        return root;}
    else
    {
        if((v1 > root->data && v2 < root->data) || (v1 < root->data && v2 > root->data))
        {
            return root;
        }

        if(v1 < root->data && v2 < root->data)
        {
            root = lca(root->left, v1, v2);
        }

        if(v1 > root->data && v2 > root->data)
        {
            root = lca(root->right, v1, v2);
        }
    }
return root;
}

0

অপরিশোধিত উপায়:

  • প্রতিটি নোডে
    • এক্স = এনডিয়া, এন 2 এর দুটি নোডের বাম দিকে উপস্থিত রয়েছে কিনা তা সন্ধান করুন
    • Y = সন্ধান করুন N1, n2 এর মধ্যে নোডের ডানদিকে বিদ্যমান কিনা
      • যদি নোড নিজেই এন 1 হয় || এন 2, আমরা সাধারণীকরণের উদ্দেশ্যে এটি বাম বা ডানদিকে পাওয়া যেতে পারি।
    • এক্স এবং ওয়াই উভয়ই যদি সত্য হয় তবে নোড হল সিএ

উপরের পদ্ধতিটির সমস্যাটি হ'ল আমরা একাধিকবার "সন্ধান" করব, অর্থাত প্রতিটি নোড একাধিকবার অতিক্রম হওয়ার সম্ভাবনা রয়েছে। আমরা যদি তথ্য রেকর্ড করতে পারি যাতে এটি আবার প্রক্রিয়া না করে (ডায়নামিক প্রোগ্রামিং ভাবেন) আমরা এই সমস্যাটি কাটিয়ে উঠতে পারি।

সুতরাং প্রতিটি নোড সন্ধান করার চেয়ে আমরা ইতিমধ্যে কী খুঁজে পেয়েছি তার রেকর্ড রাখি।

উত্তম উপায়:

  • আমরা প্রদত্ত নোডের জন্য বাম_সেট (যার অর্থ এন 1 | এন 2 বাম সাবট্রিতে পাওয়া গেছে) বা ডান_সেটটি প্রথম ফ্যাশনে দেখুন কিনা তা পরীক্ষা করে দেখুন। (দ্রষ্টব্য: আমরা মূলটিকে নিজেই বাম_সেটের সম্পত্তি দিচ্ছি যদি তা হয় এন 1 | এন 2 হয়)
  • যদি বাম_সেট এবং ডান_সেট উভয় হয় তবে নোডটি একটি এলসিএ।

কোড:

struct Node *
findCA(struct Node *root, struct Node *n1, struct Node *n2, int *set) {
   int left_set, right_set;
   left_set = right_set = 0;
   struct Node *leftCA, *rightCA;
   leftCA = rightCA = NULL;

   if (root == NULL) {
      return NULL;
   }
   if (root == n1 || root == n2) {
      left_set = 1;
      if (n1 == n2) {
         right_set = 1;
      }
   }

   if(!left_set) {
      leftCA = findCA(root->left, n1, n2, &left_set);
      if (leftCA) {
         return leftCA;
      }
   }
   if (!right_set) {
      rightCA= findCA(root->right, n1, n2, &right_set);
      if(rightCA) {
         return rightCA;
      }
   }

   if (left_set && right_set) {
      return root;
   } else {
      *set = (left_set || right_set);
      return NULL;
   }
}

0

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

public class searchTree {
    static boolean v1=false,v2=false;
    public static boolean bfs(Treenode root, int value){
         if(root==null){
           return false;
     }
    Queue<Treenode> q1 = new LinkedList<Treenode>();

    q1.add(root);
    while(!q1.isEmpty())
    {
        Treenode temp = q1.peek();

        if(temp!=null) {
            q1.remove();
            if (temp.value == value) return true;
            if (temp.left != null) q1.add(temp.left);
            if (temp.right != null) q1.add(temp.right);
        }
    }
    return false;

}
public static Treenode lcaHelper(Treenode head, int x,int y){

    if(head==null){
        return null;
    }

    if(head.value == x || head.value ==y){
        if (head.value == y){
            v2 = true;
            return head;
        }
        else {
            v1 = true;
            return head;
        }
    }

    Treenode left = lcaHelper(head.left, x, y);
    Treenode right = lcaHelper(head.right,x,y);

    if(left!=null && right!=null){
        return head;
    }
    return (left!=null) ? left:right;
}

public static int lca(Treenode head, int h1, int h2) {
    v1 = bfs(head,h1);
    v2 = bfs(head,h2);
    if(v1 && v2){
        Treenode lca = lcaHelper(head,h1,h2);
        return lca.value;
    }
    return -1;
}
}

0

আপনি সঠিক যে প্যারেন্ট নোড ব্যতীত ট্র্যাভারসাল সহ সমাধান আপনাকে ও (এন) সময় জটিলতা দেবে।

ট্র্যাভার্সাল পদ্ধতির ধরুন আপনি নোড এ এবং বি এর জন্য এলসিএ সন্ধান করছেন, সবচেয়ে সোজা পদ্ধতি হল প্রথমে মূল থেকে A এ যাওয়ার এবং তারপরে রুট থেকে বি পর্যন্ত পথ পাওয়াই আপনার এই দুটি পাথ একবার হয়ে গেলে আপনি সহজেই সেগুলি দিয়ে পুনরাবৃত্তি করতে পারেন এবং সর্বশেষ সাধারণ নোডটি সন্ধান করুন যা এ এবং বি এর সর্বনিম্ন সাধারণ পূর্বপুরুষ is

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

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

বিস্তারিত বিশ্লেষণ এবং সমাধানের জন্য সর্বনিম্ন সাধারণ পূর্বপুরুষটি পরীক্ষা করুন ।


0

এখানকার কয়েকটি সমাধান অনুমান করে যে মূল নোডের রেফারেন্স রয়েছে, কিছু ধারণা করে যে গাছটি একটি বিএসটি। rootনোড এবং গাছের উল্লেখ ছাড়াই হ্যাশম্যাপ ব্যবহার করে আমার সমাধান ভাগ করে নেওয়া বিএসটি বা নন-বিএসটি হতে পারে:

    var leftParent : Node? = left
    var rightParent : Node? = right
    var map = [data : Node?]()

    while leftParent != nil {
        map[(leftParent?.data)!] = leftParent
        leftParent = leftParent?.parent
    }

    while rightParent != nil {
        if let common = map[(rightParent?.data)!] {
            return common
        }
        rightParent = rightParent?.parent
    }

0

সমাধান 1: পুনরাবৃত্ত - দ্রুততর

  • ধারণাটি হ'ল গাছটি মূল থেকে শুরু করে যেতে হবে। যদি প্রদত্ত কীগুলির কোনও p এবং q মূলের সাথে মিলে যায় তবে রুটটি LCA হয়, ধরে নিবেন উভয় কী উপস্থিত রয়েছে। রুটটি কোনও কীগুলির সাথে মেলে না, তবে আমরা বাম এবং ডান সাবট্রির জন্য পুনরাবৃত্তি করি।
  • নোড যার বাম সাবট্রিতে একটি কী উপস্থিত রয়েছে এবং অন্য কীটি ডান সাবট্রিতে উপস্থিত রয়েছে এলসিএ। উভয় কী যদি বাম সাবট্রিতে থাকে, তবে বাম সাবট্রিতে এলসিএও রয়েছে, অন্যথায় এলসিএ ডান সাবট্রিতে থাকে।
  • সময় জটিলতা: ও (এন)
  • স্পেস জটিলতা: ও (এইচ) - পুনরাবৃত্ত কল স্ট্যাকের জন্য
class Solution 
{
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
    {
        if(root == null || root == p  || root == q)
            return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left == null)
            return right;
        else if(right == null)
            return left;
        else
            return root;    // If(left != null && right != null)
    }
}

সমাধান 2: আইট্রেটিভ - প্যারেন্ট পয়েন্টারগুলি ব্যবহার করে - ধীরে ধীরে

  • একটি খালি হ্যাশ টেবিল তৈরি করুন।
  • পি এবং এর পূর্বপুরুষদের সমস্ত হ্যাশ টেবিলে োকান।
  • হ্যাশ টেবিলটিতে কিউ বা এর পূর্বপুরুষদের কোনও উপস্থিত রয়েছে কিনা তা পরীক্ষা করুন, যদি হ্যাঁ তবে প্রথম বিদ্যমান পূর্বপুরুষকে ফিরিয়ে দিন।
  • সময় জটিলতা: ও (এন) - সবচেয়ে খারাপ ক্ষেত্রে আমরা বাইনারি গাছের সমস্ত নোডে যেতে পারি।
  • স্পেস জটিলতা: ও (এন) - স্পেস প্যারেন্ট পয়েন্টার হ্যাশ-টেবিল, পূর্বপুরুষ_সেট এবং সারি ব্যবহার করেছে, প্রতিটি ও (এন) হবে।
class Solution
{
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
    {
        HashMap<TreeNode, TreeNode> parent_map = new HashMap<>();
        HashSet<TreeNode> ancestors_set = new HashSet<>();
        Queue<TreeNode> queue = new LinkedList<>();

        parent_map.put(root, null);
        queue.add(root);

        while(!parent_map.containsKey(p) || !parent_map.containsKey(q))
        {
            TreeNode node = queue.poll();

            if(node.left != null)
            {
                parent_map.put(node.left, node);
                queue.add(node.left);
            }
            if(node.right != null)
            {
                parent_map.put(node.right, node);
                queue.add(node.right);
            }
        }

        while(p != null)
        {
            ancestors_set.add(p);
            p = parent_map.get(p);
        }

        while(!ancestors_set.contains(q))
            q = parent_map.get(q);

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