লিনকু দিয়ে কীভাবে গাছ সমতল করবেন?


96

সুতরাং আমার সহজ গাছ আছে:

class MyNode
{
 public MyNode Parent;
 public IEnumerable<MyNode> Elements;
 int group = 1;
}

আমি একটি আছে IEnumerable<MyNode>। আমি একটি ফ্ল্যাট তালিকা হিসাবে MyNode(অভ্যন্তরীণ নোড অবজেক্টস ( Elements) সহ সকলের একটি তালিকা পেতে চাই Where group == 1। লিনকিউ-এর মাধ্যমে কীভাবে এমন কাজ করবেন?


4
সমতল তালিকাটি আপনি কী অর্ডারে চান?
ফিলিপ

4
নোডগুলি কখন সন্তানের নোড থাকা বন্ধ করে দেয়? আমি মনে করি এটি কখন Elementsনাল বা ফাঁকা?
অ্যাডাম হল্ডসওয়ার্থ


এটিকে সম্বোধন করার সহজতম / সর্বাধিক সুস্পষ্ট উপায় হ'ল পুনরাবৃত্তিশীল লিনকুই কোয়েরিটি ব্যবহার করা। এই প্রশ্ন: স্ট্যাকওভারফ্লো / প্রশ্নগুলি / 22২২২৮১ / এক্সপ্রেসিং-রিসার্শন- ইন- লিঙ্ক নিয়ে এই নিয়ে অনেক আলোচনা হয়েছে এবং আপনি কীভাবে এটি বাস্তবায়ন করতে পারেন সে সম্পর্কে এই নির্দিষ্ট উত্তরটি বিশদভাবে যায়।
আলভারো রদ্রিগেজ

উত্তর:


141

আপনি এইভাবে একটি গাছ সমতল করতে পারেন:

IEnumerable<MyNode> Flatten(IEnumerable<MyNode> e) =>
    e.SelectMany(c => Flatten(c.Elements)).Concat(new[] { e });

তারপরে আপনি groupব্যবহার করে ফিল্টার করতে পারবেন Where(...)

কিছু "স্টাইলের জন্য পয়েন্ট" উপার্জন করতে, Flattenস্ট্যাটিক শ্রেণিতে কোনও এক্সটেনশন ফাংশনে রূপান্তর করুন ।

public static IEnumerable<MyNode> Flatten(this IEnumerable<MyNode> e) =>
    e.SelectMany(c => c.Elements.Flatten()).Concat(e);

"আরও ভাল স্টাইল" এর জন্য আরও পয়েন্ট অর্জনের জন্য, Flattenজেনেরিক এক্সটেনশন পদ্ধতিতে রূপান্তর করুন যা একটি গাছ এবং একটি ফাংশন নেয় যা নোড থেকে বংশধর উত্পাদন করে:

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> e
,   Func<T,IEnumerable<T>> f
) => e.SelectMany(c => f(c).Flatten(f)).Concat(e);

এই ফাংশনটিকে এভাবে কল করুন:

IEnumerable<MyNode> tree = ....
var res = tree.Flatten(node => node.Elements);

আপনি যদি পোস্ট-অর্ডের চেয়ে প্রাক-অর্ডারে চাটুটি পছন্দ করেন তবে পাশের দিকে ঘুরে দেখুন Concat(...)


@ অ্যাডামহোল্ডসওয়ার্থ সম্পাদনার জন্য ধন্যবাদ! কলটিতে Concatথাকা উপাদানটি হওয়া উচিত new[] {e}, না new[] {c}(এটি cসেখানে সংকলন করে না)।
সের্গেই কালিনিচেনকো

আমি অসম্মতি: সংকলিত, পরীক্ষিত, এবং সাথে কাজ করা c। ব্যবহার eকরে সংকলন হয় না। if (e == null) return Enumerable.Empty<T>();নাল সন্তানের তালিকাগুলি মোকাবেলা করতে আপনি যুক্ত করতে পারেন ।
অ্যাডাম হল্ডসওয়ার্থ

4
আরও বেশি `পাবলিক স্ট্যাটিক আইমনুমেবল <টি> ফ্ল্যাটেন <টি> (এই আইনিউমারবল <টি> উত্স, ফানক <টি, আই এনিউমারেবল <টি>> চ) {যদি (উত্স == নাল) ফিরে আসে অনুমিত। কার্যকর <টি> (); রিটার্ন উত্স.স্লেক্টম্যানি (সি => এফ (সি)। ফ্ল্যাটেন (চ))। কনক্যাট (উত্স); } `
myWallJSON

10
নোট করুন যে এই দ্রবণটি O (nh) যেখানে n গাছের আইটেমের সংখ্যা এবং h গাছের গড় গভীরতা। যেহেতু h ও (1) এবং ও (এন) এর মধ্যে হতে পারে তাই এটি একটি ও (এন) এবং একটি হে (এন স্কোয়ার্ড) অ্যালগোরিদমের মধ্যে। আরও ভাল অ্যালগরিদম আছে।
এরিক লিপার্ট

4
আমি লক্ষ্য করেছি যে ফাংশনটি সমতল তালিকাতে উপাদানগুলি যুক্ত করবে না যদি তালিকাটি আইনেম্যুয়াল <ব্যাসটাইপ> এর হয়। আপনি ফাংশনটিকে এভাবে কল করে সমাধান করতে পারেন: var res = ট্রি. ফ্ল্যাটেন (নোড => নোড। উপাদানসমূহ Oঅফটাইপ <ডেরাইভেড টাইপ>)
ফ্রাঙ্ক হোরেম্যানস

126

গৃহীত উত্তরের সমস্যাটি হ'ল গাছটি গভীর হলে এটি অদক্ষ। গাছটি যদি খুব গভীর হয় তবে এটি স্ট্যাকটি উড়িয়ে দেয়। আপনি একটি সুস্পষ্ট স্ট্যাক ব্যবহার করে সমস্যার সমাধান করতে পারেন:

public static IEnumerable<MyNode> Traverse(this MyNode root)
{
    var stack = new Stack<MyNode>();
    stack.Push(root);
    while(stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;
        foreach(var child in current.Elements)
            stack.Push(child);
    }
}

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

এখন আমাদের একটি ট্র্যাভারসাল হয়েছে, আপনার ক্যোয়ারী সোজা:

root.Traverse().Where(item=>item.group == 1);

4
@ জোহনিকার্ডি: আপনি যদি কোনও বিষয় নিয়ে তর্ক করতে যাচ্ছেন তবে সম্ভবত কোডটি অবশ্যই স্পষ্টভাবে সঠিক নয়। এটি আরও পরিষ্কারভাবে কী সঠিক করে তুলতে পারে?
এরিক লিপার্ট

4
@ ইব্রামথারওয়াত: সঠিক। আপনি Traverseসমস্ত উপাদান কল করতে পারে । অথবা আপনি Traverseএকটি সিকোয়েন্স নিতে পরিবর্তন করতে পারেন, এবং এটি ক্রমের সমস্ত উপাদানগুলিকে উপরে চাপিয়ে দিতে পারেন stack। মনে রাখবেন, stack"উপাদানগুলি আমি এখনও ট্র্যাভ করি নি" is অথবা আপনি এমন একটি "ডামি" রুট তৈরি করতে পারেন যেখানে আপনার ক্রমটি এর শিশুদের হয় এবং তারপরে ডামি রুটটি অতিক্রম করে।
এরিক লিপার্ট

4
আপনি যদি এটি করেন তবে foreach (var child in current.Elements.Reverse())আরও প্রত্যাশিত চাটুটি পাবেন। বিশেষত, বাচ্চারা প্রথমে সর্বশেষ সন্তানের চেয়ে তাদের ক্রম হিসাবে উপস্থিত হবে। বেশিরভাগ ক্ষেত্রে এটি বিবেচনা করা উচিত নয়, তবে আমার ক্ষেত্রে আমার পূর্বাভাসযোগ্য এবং প্রত্যাশিত ক্রমযুক্ত চাটুকারিতা প্রয়োজন।
মাইকা জোল্টু

4
@MicahZoltu, আপনি এড়াতে পারে .Reverseবিনিময় দ্বারা Stack<T>একটি জন্যQueue<T>
রুবেনস ফড়িয়া

4
@ মাইকাজল্টু আপনি অর্ডারটি সম্পর্কে সঠিক, তবে সমস্যাটি Reverseহ'ল এটি অতিরিক্ত পুনরুক্তি তৈরি করে যা এই পদ্ধতির এড়াতে বোঝানো হয়েছে। @RubensFarias বদলে Queueজন্য Stackপানা প্রথম ট্র্যাভেরসাল মধ্যে ফলাফল নেই।
জ্যাক এ

26

কেবল সম্পূর্ণতার জন্য, এখানে ড্যাসব্লিংকনলাইট এবং এরিক লিপার্টের উত্তরগুলির সংমিশ্রণটি দেওয়া হল। ইউনিট পরীক্ষিত এবং সবকিছু। :-)

 public static IEnumerable<T> Flatten<T>(
        this IEnumerable<T> items,
        Func<T, IEnumerable<T>> getChildren)
 {
     var stack = new Stack<T>();
     foreach(var item in items)
         stack.Push(item);

     while(stack.Count > 0)
     {
         var current = stack.Pop();
         yield return current;

         var children = getChildren(current);
         if (children == null) continue;

         foreach (var child in children) 
            stack.Push(child);
     }
 }

4
নালরফেরেন্সএক্সেপশন var শিশুদের এড়াতে = getChildren (বর্তমান); if (children! = নাল) {foreach (বাচ্চাদের মধ্যে শিশুদের) স্ট্যাক P পুশ (শিশু); }
সার্জ

4
আমি লক্ষ করতে চাই যে এটি তালিকাটি সমতল করে দিলেও এটি বিপরীত ক্রমে এটি ফিরিয়ে দেয়। শেষ উপাদানটি প্রথম হয়ে যায় ইত্যাদি
কর্কাস

22

হালনাগাদ:

বাসা বাঁধার স্তরে (গভীরতা) আগ্রহী ব্যক্তিদের জন্য। সুস্পষ্ট গণক স্ট্যাক বাস্তবায়ন সম্পর্কে একটি ভাল জিনিস হ'ল যে কোনও মুহুর্তে (এবং বিশেষত উপাদানটি উত্পাদন stack.Countকরার সময় ) বর্তমানে প্রক্রিয়াজাতকরণ গভীরতার প্রতিনিধিত্ব করে। সুতরাং এটি বিবেচনায় নেওয়া এবং সি # 7.0 মান টিপলগুলি ব্যবহার করে, আমরা কেবল নীচের মতো পদ্ধতি ঘোষণাকে পরিবর্তন করতে পারি:

public static IEnumerable<(T Item, int Level)> ExpandWithLevel<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)

এবং yieldবিবৃতি:

yield return (item, stack.Count);

তারপরে আমরা উপরেরটিতে সাধারণ প্রয়োগ করে মূল পদ্ধতিটি প্রয়োগ করতে পারি Select:

public static IEnumerable<T> Expand<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector) =>
    source.ExpandWithLevel(elementSelector).Select(e => e.Item);

মূল:

আশ্চর্যজনকভাবে কেউ (এমনকি এরিক) পুনরাবৃত্তির প্রাক-অর্ডার ডিএফটির "প্রাকৃতিক" পুনরাবৃত্ত পোর্টটি দেখায় নি, তাই এটি এখানে:

    public static IEnumerable<T> Expand<T>(
        this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = source.GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    yield return item;
                    var elements = elementSelector(item);
                    if (elements == null) continue;
                    stack.Push(e);
                    e = elements.GetEnumerator();
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }

আমি ধরে নিয়েছি আপনি প্রি-অর্ডার বজায় রাখার জন্য eপ্রতিবার কল elementSelectorকরলেই আপনি স্যুইচ করেন - যদি অর্ডারটির কোনও বিষয় না হয় তবে আপনি eএকবারে শুরু হওয়া সমস্তটির প্রক্রিয়াকরণের জন্য ফাংশনটি পরিবর্তন করতে পারবেন ?
নেটমেজ

@ নেটমেজ আমি বিশেষভাবে প্রাক অর্ডার চেয়েছিলাম। ছোট পরিবর্তন সহ এটি পোস্ট অর্ডার পরিচালনা করতে পারে। কিন্তু প্রধান বক্তব্য হচ্ছে, হয় গভীরতা প্রথম ট্র্যাভেরসাল । জন্য দম প্রথম ট্র্যাভেরসাল আমি ব্যবহার করেন Queue<T>। যাইহোক, এখানে ধারণাটি গণনাকারীদের সাথে একটি ছোট স্ট্যাক রাখা, পুনরাবৃত্তির প্রয়োগে যা ঘটছে তার সাথে খুব মিল similar
ইভান স্টয়েভ

@ ইভানস্টয়েভ আমি ভেবেছিলাম কোডটি সরল করা হবে। আমি অনুমান করি যে এটি ব্যবহারের Stackফলে জিগ-জ্যাগ বার্থথ প্রথম ট্র্যাভারসাল হবে in
নেটমেজ

একটি Stack<IEnumerator<T>>পরিবর্তে একটি বজায় রাখার বিন্দুটি কী Stack<T>? গণকগুলি সাধারণত পরিবর্তনীয় মানগুলির ধরণ এবং সাধারণত রাষ্ট্রীয় মেশিন হিসাবে প্রয়োগ করা হয়। সুতরাং আমি প্রত্যাশা করি যে কোনও Stack<IEnumerator<T>>সমাধান সাধারণত মেমরির অক্ষম হয়ে যায় এবং আবর্জনা সংগ্রহকারীকে চাপ দেওয়া (বক্সযুক্ত মানের ধরণের কারণে)।
থিওডর জৌলিয়াস

4
ইভান কাছাকাছি পরিদর্শন করার পরে আপনি উভয় পয়েন্টে ঠিক আছেন। বক্সিংটি অনিবার্য নয় এবং সমস্ত বাচ্চাকে সঞ্চয় করার চেয়ে বাচ্চাদের একটি গুণক সংরক্ষণ করা অবশ্যই পছন্দনীয়। উত্সাহিত। :-)
থিওডর জৌলিয়াস

7

আমি এখানে দেওয়া উত্তরগুলি সহ কিছু ছোট সমস্যা পেয়েছি:

  • আইটেমগুলির প্রাথমিক তালিকাটি বাতিল হলে কী হবে?
  • বাচ্চাদের তালিকায় যদি নাল মান থাকে?

পূর্ববর্তী উত্তরগুলিতে নির্মিত এবং নিম্নলিখিতগুলি নিয়ে আসে:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> Flatten<T>(
        this IEnumerable<T> items, 
        Func<T, IEnumerable<T>> getChildren)
    {
        if (items == null)
            yield break;

        var stack = new Stack<T>(items);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;

            if (current == null) continue;

            var children = getChildren(current);
            if (children == null) continue;

            foreach (var child in children)
                stack.Push(child);
        }
    }
}

এবং ইউনিট পরীক্ষা:

[TestClass]
public class IEnumerableExtensionsTests
{
    [TestMethod]
    public void NullList()
    {
        IEnumerable<Test> items = null;
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(0, flattened.Count());
    }
    [TestMethod]
    public void EmptyList()
    {
        var items = new Test[0];
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(0, flattened.Count());
    }
    [TestMethod]
    public void OneItem()
    {
        var items = new[] { new Test() };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(1, flattened.Count());
    }
    [TestMethod]
    public void OneItemWithChild()
    {
        var items = new[] { new Test { Id = 1, Children = new[] { new Test { Id = 2 } } } };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(2, flattened.Count());
        Assert.IsTrue(flattened.Any(i => i.Id == 1));
        Assert.IsTrue(flattened.Any(i => i.Id == 2));
    }
    [TestMethod]
    public void OneItemWithNullChild()
    {
        var items = new[] { new Test { Id = 1, Children = new Test[] { null } } };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(2, flattened.Count());
        Assert.IsTrue(flattened.Any(i => i.Id == 1));
        Assert.IsTrue(flattened.Any(i => i == null));
    }
    class Test
    {
        public int Id { get; set; }
        public IEnumerable<Test> Children { get; set; }
    }
}

4

অন্য কেউ যদি এটি আবিষ্কার করে তবে গাছটি সমতল করার পরে স্তরটিও জানতে হবে, এটি কোনামিমানের ড্যাসব্লিংকনলাইট এবং এরিক লিপার্টের সমাধানগুলির সংমিশ্রণে প্রসারিত:

    public static IEnumerable<Tuple<T, int>> FlattenWithLevel<T>(
            this IEnumerable<T> items,
            Func<T, IEnumerable<T>> getChilds)
    {
        var stack = new Stack<Tuple<T, int>>();
        foreach (var item in items)
            stack.Push(new Tuple<T, int>(item, 1));

        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            foreach (var child in getChilds(current.Item1))
                stack.Push(new Tuple<T, int>(child, current.Item2 + 1));
        }
    }

2

একটি সত্যই অন্য বিকল্প হ'ল সঠিক ওও নকশা রাখা।

যেমন MyNodeসমস্ত সমতল ফিরে আসতে বলুন ।

এটার মত:

class MyNode
{
    public MyNode Parent;
    public IEnumerable<MyNode> Elements;
    int group = 1;

    public IEnumerable<MyNode> GetAllNodes()
    {
        if (Elements == null)
        {
            return Enumerable.Empty<MyNode>(); 
        }

        return Elements.SelectMany(e => e.GetAllNodes());
    }
}

এখন আপনি উপরের স্তরের মাইনোডকে সমস্ত নোড পেতে বলতে পারেন।

var flatten = topNode.GetAllNodes();

আপনি যদি শ্রেণিটি সম্পাদনা করতে না পারেন, তবে এটি কোনও বিকল্প নয়। তবে অন্যথায়, আমি মনে করি এটি আলাদা (পুনরাবৃত্ত) লিনকিউ পদ্ধতির চেয়ে বেশি পছন্দ করা যেতে পারে।

এটি লিনকিউ ব্যবহার করছে, সুতরাং আমি মনে করি এই উত্তরটি এখানে প্রযোজ্য;)


হয়তো Enumerabl.Empty নতুন তালিকার চেয়ে ভাল?
ফ্রাঙ্ক

4
প্রকৃতপক্ষে! আপডেট হয়েছে!
জুলিয়ান

1

আপনার বাসা বাঁধার মাত্রা এবং তালিকাটি "ক্রম" সমতল হওয়া এবং কোনামিমানের দেওয়া উত্তরের মতো উল্টো না হওয়া সেক্ষেত্রে ডেভ এবং ইভান স্টয়েভের উত্তর একত্রিত করা।

 public static class HierarchicalEnumerableUtils
    {
        private static IEnumerable<Tuple<T, int>> ToLeveled<T>(this IEnumerable<T> source, int level)
        {
            if (source == null)
            {
                return null;
            }
            else
            {
                return source.Select(item => new Tuple<T, int>(item, level));
            }
        }

        public static IEnumerable<Tuple<T, int>> FlattenWithLevel<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
        {
            var stack = new Stack<IEnumerator<Tuple<T, int>>>();
            var leveledSource = source.ToLeveled(0);
            var e = leveledSource.GetEnumerator();
            try
            {
                while (true)
                {
                    while (e.MoveNext())
                    {
                        var item = e.Current;
                        yield return item;
                        var elements = elementSelector(item.Item1).ToLeveled(item.Item2 + 1);
                        if (elements == null) continue;
                        stack.Push(e);
                        e = elements.GetEnumerator();
                    }
                    if (stack.Count == 0) break;
                    e.Dispose();
                    e = stack.Pop();
                }
            }
            finally
            {
                e.Dispose();
                while (stack.Count != 0) stack.Pop().Dispose();
            }
        }
    }

প্রথম গভীরতা বা প্রস্থকে প্রথমে নির্দিষ্ট করতে সক্ষম
হিউ

1

এখানে কিছু ক্যুই ব্যবহার করে বাস্তবায়ন ব্যবহারের জন্য প্রস্তুত এবং প্রথমে আমাকে এবং তারপরে আমার বাচ্চাদের ফ্ল্যাটেন গাছ ফিরিয়ে আনতে।

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, 
    Func<T,IEnumerable<T>> getChildren)
    {
        if (items == null)
            yield break;

        var queue = new Queue<T>();

        foreach (var item in items) {
            if (item == null)
                continue;

            queue.Enqueue(item);

            while (queue.Count > 0) {
                var current = queue.Dequeue();
                yield return current;

                if (current == null)
                    continue;

                var children = getChildren(current);
                if (children == null)
                    continue;

                foreach (var child in children)
                    queue.Enqueue(child);
            }
        }

    }

0
void Main()
{
    var allNodes = GetTreeNodes().Flatten(x => x.Elements);

    allNodes.Dump();
}

public static class ExtensionMethods
{
    public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector = null)
    {
        if (source == null)
        {
            return new List<T>();
        }

        var list = source;

        if (childrenSelector != null)
        {
            foreach (var item in source)
            {
                list = list.Concat(childrenSelector(item).Flatten(childrenSelector));
            }
        }

        return list;
    }
}

IEnumerable<MyNode> GetTreeNodes() {
    return new[] { 
        new MyNode { Elements = new[] { new MyNode() }},
        new MyNode { Elements = new[] { new MyNode(), new MyNode(), new MyNode() }}
    };
}

class MyNode
{
    public MyNode Parent;
    public IEnumerable<MyNode> Elements;
    int group = 1;
}

4
আপনার এক্সটেনশনে পূর্বাঞ্চ ব্যবহারের অর্থ এটি আর 'বিলম্বিত কার্যকর' নয় (অবশ্যই আপনি ফলন ফেরতের ব্যবহার না করে)।
ত্রি কিউ ট্রান

0

কোনামিমানের উত্তরের উপর ভিত্তি করে, এবং আদেশটি অপ্রত্যাশিত বলে মন্তব্য করা হয়েছে, এখানে একটি স্পষ্ট সাজান পরম সহ একটি সংস্করণ রয়েছে:

public static IEnumerable<T> TraverseAndFlatten<T, V>(this IEnumerable<T> items, Func<T, IEnumerable<T>> nested, Func<T, V> orderBy)
{
    var stack = new Stack<T>();
    foreach (var item in items.OrderBy(orderBy))
        stack.Push(item);

    while (stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;

        var children = nested(current).OrderBy(orderBy);
        if (children == null) continue;

        foreach (var child in children)
            stack.Push(child);
    }
}

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

var flattened = doc.TraverseAndFlatten(x => x.DependentDocuments, y => y.Document.DocDated).ToList();

0

নীচে পাথের প্রতিটি বস্তুর সূচি বলার অ্যাডিটোনাল বৈশিষ্ট্য সহ ইভান স্টয়েভের কোডটি রয়েছে। যেমন "আইটেম_120" এর জন্য অনুসন্ধান করুন:

Item_0--Item_00
        Item_01

Item_1--Item_10
        Item_11
        Item_12--Item_120

আইটেম এবং একটি int অ্যারে [1,2,0] প্রদান করবে। স্পষ্টতই, অ্যারেগুলির দৈর্ঘ্য হিসাবে নেস্টিং স্তরও পাওয়া যায়।

public static IEnumerable<(T, int[])> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren) {
    var stack = new Stack<IEnumerator<T>>();
    var e = source.GetEnumerator();
    List<int> indexes = new List<int>() { -1 };
    try {
        while (true) {
            while (e.MoveNext()) {
                var item = e.Current;
                indexes[stack.Count]++;
                yield return (item, indexes.Take(stack.Count + 1).ToArray());
                var elements = getChildren(item);
                if (elements == null) continue;
                stack.Push(e);
                e = elements.GetEnumerator();
                if (indexes.Count == stack.Count)
                    indexes.Add(-1);
                }
            if (stack.Count == 0) break;
            e.Dispose();
            indexes[stack.Count] = -1;
            e = stack.Pop();
        }
    } finally {
        e.Dispose();
        while (stack.Count != 0) stack.Pop().Dispose();
    }
}

হাই, @ এলিজ, আপনি এই কোডটি কোথায় পেস্ট করবেন? আমি ত্রুটি পেয়েছি যেমন "
আইডিটির

0

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

public static IEnumerable<T> Traverse<T>(
    this IEnumerable<T> source,
    Func<T, IEnumerable<T>> fnRecurse)
{
    if (source != null)
    {
        Stack<IEnumerator<T>> enumerators = new Stack<IEnumerator<T>>();
        try
        {
            enumerators.Push(source.GetEnumerator());
            while (enumerators.Count > 0)
            {
                var top = enumerators.Peek();
                while (top.MoveNext())
                {
                    yield return top.Current;

                    var children = fnRecurse(top.Current);
                    if (children != null)
                    {
                        top = children.GetEnumerator();
                        enumerators.Push(top);
                    }
                }

                enumerators.Pop().Dispose();
            }
        }
        finally
        {
            while (enumerators.Count > 0)
                enumerators.Pop().Dispose();
        }
    }
}

একটি কাজের উদাহরণ পাওয়া যাবে এখানে


0

এখানে উপস্থাপিত বেশিরভাগ উত্তর গভীরতা-প্রথম বা জিগ-জাগ সিকোয়েন্স তৈরি করছে। উদাহরণস্বরূপ নীচের গাছ দিয়ে শুরু:

        1                   2 
       / \                 / \
      /   \               /   \
     /     \             /     \
    /       \           /       \
   11       12         21       22
  / \       / \       / \       / \
 /   \     /   \     /   \     /   \
111 112   121 122   211 212   221 222

ড্যাসব্লিংক্লাইটলাইটের উত্তর এই সমতল ক্রম উত্পাদন করে:

111, 112, 121, 122, 11, 12, 211, 212, 221, 222, 21, 22, 1, 2

কোনামিমানের উত্তর (যা এরিক লিপার্টের উত্তরকে সাধারণীকরণ করে) এই সমতল ক্রমটি তৈরি করে:

2, 22, 222, 221, 21, 212, 211, 1, 12, 122, 121, 11, 112, 111

ইভান স্টয়েভের উত্তর এই সমতল ক্রম উত্পাদন করে:

1, 11, 111, 112, 12, 121, 122, 2, 21, 211, 212, 22, 221, 222

আপনি যদি এর মতো প্রস্থের প্রথম ক্রমটিতে আগ্রহী হন :

1, 2, 11, 12, 21, 22, 111, 112, 121, 122, 211, 212, 221, 222

... তবে এটি আপনার জন্য সমাধান:

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source,
    Func<T, IEnumerable<T>> childrenSelector)
{
    var queue = new Queue<T>(source);
    while (queue.Count > 0)
    {
        var current = queue.Dequeue();
        yield return current;
        var children = childrenSelector(current);
        if (children == null) continue;
        foreach (var child in children) queue.Enqueue(child);
    }
}

বাস্তবায়নের পার্থক্যটি মূলত একটি এর Queueপরিবর্তে ব্যবহার করে Stack। কোন আসল বাছাই হচ্ছে না।


সাবধানতা: এই প্রয়োগটি স্মৃতি-দক্ষতা সম্পর্কিত অনুকূল থেকে অনেক দূরে, যেহেতু মোট সংখ্যার উপাদানগুলির একটি বিশাল শতাংশ গণনার সময় অভ্যন্তরীণ কাতারে সংরক্ষণ করা হবে being Stackবেসড ট্রি-ট্র্যাভারসাল- Queueভিত্তিক বাস্তবায়নগুলির চেয়ে মেমরির ব্যবহার সম্পর্কে অনেক বেশি দক্ষ ।

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