লিনকিউ ব্যবহার করে একটি গাছ অনুসন্ধান করা


87

আমার এই শ্রেণি থেকে একটি গাছ তৈরি হয়েছে।

class Node
{
    public string Key { get; }
    public List<Node> Children { get; }
}

একটি শর্তের সাথে মেলে এমনগুলি পেতে আমি সমস্ত শিশু এবং তাদের সমস্ত বাচ্চাদের অনুসন্ধান করতে চাই:

node.Key == SomeSpecialKey

আমি কীভাবে এটি বাস্তবায়ন করতে পারি?


মজাদার, আমি মনে করি আপনি এটি সিলেক্টম্যানি ফাংশনটি ব্যবহার করে সম্পন্ন করতে পারবেন, মনে রাখবেন কিছুক্ষণ আগে একই রকম কিছু করতে হবে।
জেঠরো

উত্তর:


176

এটি একটি ভ্রান্ত ধারণা যা এর জন্য পুনরাবৃত্তি প্রয়োজন। এটির জন্য একটি স্ট্যাক বা একটি সারি প্রয়োজন হবে এবং পুনরাবৃত্তি ব্যবহার করে এটি কার্যকর করার সবচেয়ে সহজ উপায়। সম্পূর্ণতার জন্য আমি একটি পুনরাবৃত্ত উত্তর প্রদান করব।

static IEnumerable<Node> Descendants(this Node root)
{
    var nodes = new Stack<Node>(new[] {root});
    while (nodes.Any())
    {
        Node node = nodes.Pop();
        yield return node;
        foreach (var n in node.Children) nodes.Push(n);
    }
}

উদাহরণস্বরূপ এটি ব্যবহারের জন্য এই এক্সপ্রেশনটি ব্যবহার করুন:

root.Descendants().Where(node => node.Key == SomeSpecialKey)

31
+1 এবং গাছটি এত গভীর হয়ে যায় যে পুনরাবৃত্ত হওয়া ট্র্যাভারসাল কল স্ট্যাকটি ফুঁকবে এবং এর কারণ ঘটায় এই পদ্ধতিটি অবিরত থাকবে StackOverflowException
লুক এইচ

4
@ লুকেহ যদিও এই পরিস্থিতিতে এই জাতীয় বিকল্পগুলি রাখা দরকারী তবে এর অর্থ একটি খুব বড় গাছ। আপনার গাছটি খুব গভীর না হলে পুনরাবৃত্তির পদ্ধতিগুলি সাধারণভাবে আরও সহজ / আরও পঠনযোগ্য।
ফোর্বস লিন্ডসে

4
@ তাসকান: পুনরাবৃত্তকারী পুনরাবৃত্তকারীগুলির ব্যবহারের কার্যকারিতাও অন্তর্ভুক্ত রয়েছে, ব্লগস.এমএসডিএন / বি / ওয়েসডিয়ার / আর্কাইভ / ২০০7/৩০/৩৩/২০১৪ এর বিভাগ " আইট্রেটারদের ব্যয়" বিভাগটি দেখুন (স্বীকার করতে হবে যে গাছগুলি এখনও মোটামুটি গভীর হতে হবে এটি লক্ষণীয় হতে পারে)। এবং, নতুনভাবে, আমি এখানে পুনরাবৃত্ত উত্তরগুলির মতোই পঠনযোগ্য হিসাবে Vidtige এর উত্তর খুঁজে পেতে পারি।
লুক এইচ

4
হ্যাঁ, পারফরম্যান্সের কারণে আমার সমাধানটি বেছে নেবেন না। অযোগ্যতা প্রমাণিত না হলে পঠনযোগ্যতা সর্বদা প্রথম। যদিও আমার সমাধানটি বেশ সোজা এগিয়ে রয়েছে, তাই আমি অনুমান করি এটি স্বাদের বিষয় ... আমি আসলে আমার উত্তরটি কেবল পুনরাবৃত্ত উত্তরের পরিপূরক হিসাবে পোস্ট করেছি তবে আমি আনন্দিত যে লোকেরা এটি পছন্দ করেছে।

11
আমি মনে করি এটি উল্লেখযোগ্য যে উপরে উপস্থাপিত সমাধানটি একটি (সর্বশেষ-শিশু-প্রথম) গভীরতা-প্রথম অনুসন্ধান করে। আপনি যদি (প্রথম-শিশু-প্রথম) প্রস্থ-প্রথম অনুসন্ধান চান, আপনি নোড সংগ্রহের ধরণটি Queue<Node>(থেকে Enqueue/ Dequeueথেকে Push/ এর সাথে সম্পর্কিত পরিবর্তনগুলি Pop) এ পরিবর্তন করতে পারেন ।
অ্যান্ড্রু কুনস

16

লিনকের সাথে অবজেক্টের একটি গাছ অনুসন্ধান করা হচ্ছে

public static class TreeToEnumerableEx
{
    public static IEnumerable<T> AsDepthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
    {
        yield return head;

        foreach (var node in childrenFunc(head))
        {
            foreach (var child in AsDepthFirstEnumerable(node, childrenFunc))
            {
                yield return child;
            }
        }

    }

    public static IEnumerable<T> AsBreadthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
    {
        yield return head;

        var last = head;
        foreach (var node in AsBreadthFirstEnumerable(head, childrenFunc))
        {
            foreach (var child in childrenFunc(node))
            {
                yield return child;
                last = child;
            }
            if (last.Equals(node)) yield break;
        }

    }
}

4
+1 সমস্যাটি সাধারণভাবে সমাধান করে। সংযুক্ত নিবন্ধটি দুর্দান্ত ব্যাখ্যা দিয়েছে।
জন যিশু

সম্পূর্ণ আপনি পরামিতি উপর নাল পরীক্ষণ প্রয়োজন হতে headএবং childrenFuncতাই পরামিতি পরীক্ষণের ট্র্যাভেরসাল সময় পিছিয়েছে নয় দুটি পদ্ধতি বিরতি।
এরিক

15

যদি আপনি সিনট্যাক্সের মতো লিনক বজায় রাখতে চান তবে আপনি সমস্ত বংশধরদের (শিশু + শিশুদের ইত্যাদি) পাওয়ার জন্য একটি পদ্ধতি ব্যবহার করতে পারেন

static class NodeExtensions
{
    public static IEnumerable<Node> Descendants(this Node node)
    {
        return node.Children.Concat(node.Children.SelectMany(n => n.Descendants()));
    }
}

এই সংখ্যাটি তারপরে যেখানে বা প্রথমে বা যাই হোক না কেন ব্যবহার করে অন্য যেভাবে অনুসন্ধান করা যেতে পারে।


আমি এই পছন্দ, পরিষ্কার! :)
বিদায় দিন

3

আপনি গাছের নোডগুলি গণনা করতে এই এক্সটেনশন পদ্ধতিটি ব্যবহার করতে পারেন:

static IEnumerable<Node> GetTreeNodes(this Node rootNode)
{
    yield return rootNode;
    foreach (var childNode in rootNode.Children)
    {
        foreach (var child in childNode.GetTreeNodes())
            yield return child;
    }
}

তারপরে একটি Where()ধারা সহ এটি ব্যবহার করুন :

var matchingNodes = rootNode.GetTreeNodes().Where(x => x.Key == SomeSpecialKey);

4
নোট করুন যে গাছটি গভীর হলে এই কৌশলটি অদক্ষ এবং গাছ খুব গভীর হলে একটি ব্যতিক্রম ছুঁড়ে ফেলতে পারে।
এরিক লিপার্ট

4
@ এরিক ভাল পয়েন্ট এবং ছুটি থেকে ফিরে স্বাগত জানাই? (বিশ্বজুড়ে বিস্তৃত এই ইন্টারনেট জিনিসটি কী তা বলা শক্ত))

2

সম্ভবত আপনার প্রয়োজন

node.Children.Where(child => child.Key == SomeSpecialKey)

অথবা, যদি আপনাকে আরও একটি স্তর গভীরতর অনুসন্ধান করতে হয়,

node.Children.SelectMany(
        child => child.Children.Where(child => child.Key == SomeSpecialKey))

আপনার যদি সমস্ত স্তরের সন্ধান করতে হয় তবে নিম্নলিখিতগুলি নিন:

IEnumerable<Node> FlattenAndFilter(Node source)
{
    List<Node> l = new List();
    if (source.Key == SomeSpecialKey)
        l.Add(source);
    return
        l.Concat(source.Children.SelectMany(child => FlattenAndFilter(child)));
}

এটা কি বাচ্চাদের বাচ্চাদের সন্ধান করবে?
জেঠরো

আমি মনে করি এটি কাজ করবে না, কারণ এটি কেবলমাত্র গাছের এক স্তরে অনুসন্ধান করে এবং একটি সম্পূর্ণ গাছের
আবর্তন

@ ইউফুক: প্রথম লাইনটি কেবল 1 স্তর গভীর, দ্বিতীয় মাত্র 2 স্তর গভীরতে কাজ করে। আপনার যদি সমস্ত স্তরের সন্ধান করতে হয় তবে আপনার একটি পুনরাবৃত্ত ফাংশন প্রয়োজন।
ভ্লাদ

2
public class Node
    {
        string key;
        List<Node> children;

        public Node(string key)
        {
            this.key = key;
            children = new List<Node>();
        }

        public string Key { get { return key; } }
        public List<Node> Children { get { return children; } }

        public Node Find(Func<Node, bool> myFunc)
        {
            foreach (Node node in Children)
            {
                if (myFunc(node))
                {
                    return node;
                }
                else 
                {
                    Node test = node.Find(myFunc);
                    if (test != null)
                        return test;
                }
            }

            return null;
        }
    }

এবং তারপরে আপনি এটির মতো অনুসন্ধান করতে পারেন:

    Node root = new Node("root");
    Node child1 = new Node("child1");
    Node child2 = new Node("child2");
    Node child3 = new Node("child3");
    Node child4 = new Node("child4");
    Node child5 = new Node("child5");
    Node child6 = new Node("child6");
    root.Children.Add(child1);
    root.Children.Add(child2);
    child1.Children.Add(child3);
    child2.Children.Add(child4);
    child4.Children.Add(child5);
    child5.Children.Add(child6);

    Node test = root.Find(p => p.Key == "child6");

কারণ ফাইন্ডের ইনপুটটি হ'ল ফানক <নোড, বুল> মাইফ্যাঙ্ক আপনি এই পদ্ধতিটি কোনও অন্য সম্পত্তি দ্বারা ফিল্টার করতে ব্যবহার করতে পারেন যা আপনি নোডেও সংজ্ঞায়িত করতে পারেন। উদাহরণস্বরূপ নোডে একটি নামের সম্পত্তি ছিল এবং আপনি নাম অনুসারে একটি নোড সন্ধান করতে চেয়েছিলেন, আপনি কেবল p => p.Name == "কিছু" দিয়ে যেতে পারেন
বরুণ চ্যাটার্জী

2

কেন একটি IEnumerable<T>এক্সটেনশন পদ্ধতি ব্যবহার করবেন না

public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
    if (source == null)
    {
        yield break;
    }
    foreach (var item in source)
    {
        if (predicate(item))
        {
            yield return item;
        }
        var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
        foreach (var childItem in childResults)
        {
            yield return childItem;
        }
    }
}

তাহলে শুধু এই কাজ

var result = nodes.Children.SelectHierarchy(n => n.Children, n => n.Key.IndexOf(searchString) != -1);

0

কিছুক্ষণ আগে আমি একটি কোডপোজেক্ট নিবন্ধ লিখেছিলাম যা লিঙ্ককে কীভাবে গাছের মতো কাঠামো জিজ্ঞাসা করতে হয় তা বর্ণনা করে:

http://www.codeproject.com/KB/linq/LinqToTree.aspx

এটি একটি লিনাক-টু-এক্সএমএল স্টাইল এপিআই সরবরাহ করে যেখানে আপনি বংশধর, শিশু, পূর্বপুরুষ ইত্যাদি অনুসন্ধান করতে পারেন ...

আপনার বর্তমান সমস্যার জন্য সম্ভবত ওভারকিল, তবে অন্যের পক্ষে এটি আগ্রহী হতে পারে।


0

আপনি গাছটিকে জিজ্ঞাসা করতে এই এক্সটেনশন পদ্ধতিটি ব্যবহার করতে পারেন।

    public static IEnumerable<Node> InTree(this Node treeNode)
    {
        yield return treeNode;

        foreach (var childNode in treeNode.Children)
            foreach (var flattendChild in InTree(childNode))
                yield return flattendChild;
    }

0

আমার একটি জেনেরিক এক্সটেনশন পদ্ধতি রয়েছে যা যে কোনওটিকে ফ্ল্যাট করতে পারে IEnumerable<T>এবং সেই সমতল সংগ্রহ থেকে, আপনি চান নোডটি পেতে পারেন।

public static IEnumerable<T> FlattenHierarchy<T>(this T node, Func<T, IEnumerable<T>> getChildEnumerator)
{
    yield return node;
    if (getChildEnumerator(node) != null)
    {
        foreach (var child in getChildEnumerator(node))
        {
            foreach (var childOrDescendant in child.FlattenHierarchy(getChildEnumerator))
            {
                yield return childOrDescendant;
            }
        }
    }
}

এটি এর মতো ব্যবহার করুন:

var q = from node in myTree.FlattenHierarchy(x => x.Children)
        where node.Key == "MyKey"
        select node;
var theNode = q.SingleOrDefault();

0

আমি গাছের আইটেমগুলি গণনার জন্য নিম্নলিখিত প্রয়োগগুলি ব্যবহার করি

    public static IEnumerable<Node> DepthFirstUnfold(this Node root) =>
        ObjectAsEnumerable(root).Concat(root.Children.SelectMany(DepthFirstUnfold));

    public static IEnumerable<Node> BreadthFirstUnfold(this Node root) {
        var queue = new Queue<IEnumerable<Node>>();
        queue.Enqueue(ObjectAsEnumerable(root));

        while (queue.Count != 0)
            foreach (var node in queue.Dequeue()) {
                yield return node;
                queue.Enqueue(node.Children);
            }
    }

    private static IEnumerable<T> ObjectAsEnumerable<T>(T obj) {
        yield return obj;
    }

উপরের প্রয়োগে BreadthFirstUnfold নোড সারির পরিবর্তে নোড সিক্যুয়েন্সের সারি ব্যবহার করে। এটি ক্লাসিক বিএফএস অ্যালগরিদম উপায় নয়।


0

এবং কেবল মজাদার জন্য (প্রায় এক দশক পরে) জেনারিকস ব্যবহার করে তবে একটি স্ট্যাক এবং যখন লুপ সহ একটি উত্তর, উত্তরটি @ বিডিস্টিজ দ্বারা গৃহীত উত্তরের ভিত্তিতে তৈরি করা হয়েছে।

public static class TypeExtentions
{

    public static IEnumerable<T> Descendants<T>(this T root, Func<T, IEnumerable<T>> selector)
    {
        var nodes = new Stack<T>(new[] { root });
        while (nodes.Any())
        {
            T node = nodes.Pop();
            yield return node;
            foreach (var n in selector(node)) nodes.Push(n);
        }
    }

    public static IEnumerable<T> Descendants<T>(this IEnumerable<T> encounter, Func<T, IEnumerable<T>> selector)
    {
        var nodes = new Stack<T>(encounter);
        while (nodes.Any())
        {
            T node = nodes.Pop();
            yield return node;
            if (selector(node) != null)
                foreach (var n in selector(node))
                    nodes.Push(n);
        }
    }
}

একটি সংগ্রহ দেওয়া হয়েছে যে কেউ এর মতো ব্যবহার করতে পারে

        var myNode = ListNodes.Descendants(x => x.Children).Where(x => x.Key == SomeKey);

বা একটি মূল অবজেক্টের সাথে

        var myNode = root.Descendants(x => x.Children).Where(x => x.Key == SomeKey);
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.