C# 平整结构与树结构互转
下面是示例代码
数结构定义
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            // List<Node> nodes = new List<Node>();
            // var root = new Node() { Id = "1", Data = "第一个节点" };
            // nodes.Add(root);
            // for (int i = 2; i < 30; i++)
            // {
            //     var childNode = new Node() { Id = i.ToString(), PId = (i - 1).ToString(), Data = $"第{i}个节点" };
            //     nodes.Add(childNode);
            // }
            //
            // foreach (var node in nodes)
            // {
            //     Console.WriteLine(node);
            // }
            var newNodes = new List<Node>();
            newNodes.Add(new Node() { Id = "1", PId = "", Data = "1节点" });
            newNodes.Add(new Node() { Id = "4", PId = "2", Data = "4节点" });
            newNodes.Add(new Node() { Id = "7", PId = "4", Data = "7节点" });
            newNodes.Add(new Node() { Id = "5", PId = "2", Data = "5节点" });
            newNodes.Add(new Node() { Id = "2", PId = "1", Data = "2节点" });
            newNodes.Add(new Node() { Id = "3", PId = "1", Data = "3节点" });
            // var treeList = BuildTree(newNodes);
            // var treeList = BuildTreeV1(newNodes);
            // var treeList = BuildTreeV3(newNodes);
            var treeList = newNodes.Where(x => x.IsRoot).ToList();
            foreach (var node in treeList)
            {
                BuildTreeV4(node, n => newNodes.Where(x => x.PId == n.Id));
            }
            var treeInfo = new Node()
            {
                Id = "1",
                Data = "1节点",
                Children = new List<Node>()
                {
                    new Node()
                    {
                        Id = "2",
                        PId = "1",
                        Data = "2节点",
                        Children = new List<Node>()
                        {
                            new Node()
                            {
                                Id = "4",
                                PId = "2",
                                Data = "4节点",
                                Children = new List<Node>()
                                {
                                    new Node()
                                    {
                                        Id = "7",
                                        PId = "2",
                                        Data = "7节点",
                                    }
                                }
                            },
                            new Node()
                            {
                                Id = "5",
                                PId = "2",
                                Data = "5节点",
                            }
                        }
                    },
                    new Node()
                    {
                        Id = "3",
                        PId = "1",
                        Data = "3节点",
                    }
                },
            };
            var myChildren = new List<Node>();
            // GetMyChildren(treeInfo, myChildren);
            GetMyChildrenV2(treeInfo, myChildren);
            GetMyChildrenV3(treeInfo, myChildren);
            foreach (var node in myChildren)
            {
                Console.WriteLine(node);
            }
            Console.ReadLine();
        }
        //预先知道有多少层级就写多少层for循环
        //缺点 局限性太强 只能适应已知层级的场景
        private static List<Node> BuildTree(List<Node> nodes)
        {
            var treeItems = new List<Node>();
            //找出根节点
            var roots = nodes.Where(x => x.IsRoot).ToList();
            //遍历根节点 找出子节点
            foreach (var currentNode in roots)
            {
                //筛选出子节点
                var childNodes = nodes.Where(x => x.PId == currentNode.Id).ToList();
                if (!childNodes.Any())
                {
                    break; //结束
                }
                foreach (var childNode in childNodes)
                {
                    //加入到自己的子节点集合中
                    if (currentNode.Children == null)
                    {
                        currentNode.Children = new List<Node>();
                    }
                    currentNode.Children.Add(childNode);
                    //若我的子节点有自己的子节点--找出子节点的子节点集合
                    var grandChildNodes = nodes.Where(x => x.PId == childNode.Id).ToList();
                    if (!grandChildNodes.Any())
                    {
                        break;
                    }
                    foreach (var grandChildNode in grandChildNodes)
                    {
                        //加入到自己的子节点集合中
                        if (childNode.Children == null)
                        {
                            childNode.Children = new List<Node>();
                        }
                        childNode.Children.Add(grandChildNode);
                        //若我的子节点有自己的子节点--找出子节点的子节点集合
                        //..........这仿佛是一种重复性的工作...........
                    }
                }
                //总之走完上面的操作当前节点的子节点均已归位
                treeItems.Add(currentNode);
            }
            return treeItems;
        }
        //递归方式
        //缺点 开销太大 层级过深会爆栈
        private static List<Node> BuildTreeV1(List<Node> nodes)
        {
            var treeItems = new List<Node>();
            //找出根节点
            var roots = nodes.Where(x => x.IsRoot).ToList();
            //遍历根节点 找出子节点
            foreach (var currentNode in roots)
            {
                FindMyChildren(currentNode, nodes);
                //总之走完上面的操作当前节点的子节点均已归位
                treeItems.Add(currentNode);
            }
            return treeItems;
        }
        private static void FindMyChildren(Node root, List<Node> nodes)
        {
            var childNodes = nodes.Where(x => x.PId == root.Id).ToList();
            // if (!childNodes.Any())
            // {
            //     return;
            // }
            foreach (var childNode in childNodes)
            {
                if (root.Children == null)
                {
                    root.Children = new List<Node>();
                }
                root.Children.Add(childNode);
                FindMyChildren(childNode, nodes);
            }
        }
        //装入字典使查找更高效
        //在当前循环中 找当前节点的父亲节点 将自己放到父亲的子节点集合中
        private static List<Node> BuildTreeV3(List<Node> nodes)
        {
            var treeItems = new Dictionary<string, Node>();
            if (nodes == null || nodes.Count == 0)
            {
                return null;
            }
            foreach (var currentNode in nodes)
            {
                treeItems.Add(currentNode.Id, currentNode);
            }
            foreach (var currentItem in treeItems)
            {
                if (currentItem.Value == null || currentItem.Value.IsRoot)
                {
                    continue;
                }
                var parent = treeItems.ContainsKey(currentItem.Value.PId) ? treeItems[currentItem.Value.PId] : null;
                if (parent == null)
                {
                    continue;
                }
                if (parent.Children == null)
                {
                    parent.Children = new List<Node>();
                }
                parent.Children.Add(currentItem.Value);
            }
            return treeItems.Values.Where(x => x.IsRoot).ToList();
        }
        //是不是可以将父子关系 放到一个委托中 这样代码通用性更好
        private static void BuildTreeV4(Node currentNode, Func<Node, IEnumerable<Node>> childSelector)
        {
            var childList = childSelector?.Invoke(currentNode);
            if (childList == null)
            {
                return;
            }
            foreach (var child in childList)
            {
                if (currentNode.Children == null)
                {
                    currentNode.Children = new List<Node>();
                }
                currentNode.Children.Add(child);
                BuildTreeV4(child, childSelector);
            }
        }
        //给定一个节点如何获取所有的子节点呢?
        //思路一 查找所有子节点 遍历子节点 找到子节点的子节点集合
        private static void GetMyChildren(Node root, List<Node> childNodes)
        {
            if (root == null || root.Children == null)
            {
                return;
            }
            foreach (var child in root.Children)
            {
                childNodes.Add(child);
                GetMyChildren(child, childNodes);
            }
        }
        //思路二 非递归方式 上面操作[先访问的节点先加入到集合 后访问的节点后加入到集合]有点先进先出的感觉 用队列
        /*
         *              1
         *          2       3
         *      4
         * 5        6
         * 访问路径为 1 2 3 4 5 6
         */
        //这种往下 每一层先访问所有子节点的方式 在树的遍历中叫 广度优先遍历
        private static void GetMyChildrenV2(Node root, List<Node> childNodes)
        {
            if (root == null|| root.Children == null|| root.Children.Count == 0)
            {
                return;
            }
            var queue = new Queue<Node>(root.Children);
            childNodes.AddRange(root.Children);
            while (queue.Any())
            {
                var currentNode = queue.Dequeue();
                if (currentNode != null)
                {
                    if (currentNode.Children != null)
                    {
                        childNodes.AddRange(currentNode.Children);
                        foreach (var child in currentNode.Children)
                        {
                            queue.Enqueue(child);
                        }
                    }
                }
            }
        }
        //思路三 非递归方式 先紧着一个节点的第一个子节点往下查找
        /*
         *              1
         *          2       3
         *      4
         * 5        6
         * 访问路径为 1 2 4 5 6 3
         */
        //就是访问到有子节点后 继续向下找子节点 知道没有子节点后 返回上一级 再访问父节点下的另一个兄弟节点
        //这种先访问左子树 再访问右子树的方式 在树的遍历中 称为深度优先--前序遍历
        private static void GetMyChildrenV3(Node root, List<Node> childNodes)
        {
            if (root == null || root.Children == null || root.Children.Count == 0)
            {
                return;
            }
            var stack = new Stack<Node>();
            for (int i = root.Children.Count - 1; i >= 0; i--)
            {
                stack.Push(root.Children[i]);
            }
            while (stack.Any())
            {
                var currentNode = stack.Pop();
                childNodes.Add(currentNode);
                if (currentNode != null)
                {
                    if (currentNode.Children != null)
                    {
                        for (int i = currentNode.Children.Count - 1; i >= 0; i--)
                        {
                            stack.Push(currentNode.Children[i]);
                        }
                    }
                }
            }
        }
    }
}
关于深度优先和广度优先,有一个博主动图做的很好:
https://blog.csdn.net/qq_44918331/article/details/115542177
深度优先--前序遍历(根左右)--动图来自上面博主的博客

广度优先--即先访问完下层所有,再往下下层访问。。。

还有一个博主写的 用的一趟for循环实现的;这个我测试了一下,是有问题的。
C# 非递归列表转树形结构的实现
测试代码
var newNodes = new List<Node>();
newNodes.Add(new Node(){Id = "1",PId = "",Data = "1节点"});
newNodes.Add(new Node(){Id = "4",PId = "2",Data = "4节点"});
newNodes.Add(new Node(){Id = "7",PId = "4",Data = "7节点"});
newNodes.Add(new Node(){Id = "5",PId = "2",Data = "5节点"});
newNodes.Add(new Node(){Id = "2",PId = "1",Data = "2节点"});
newNodes.Add(new Node(){Id = "3",PId = "1",Data = "3节点"});
var newTrees = BuildTreeV2(newNodes);
博主的代码:
public static List<Node> GetTree(List<Node> list, Func<Node, bool> IsRoot)
{
    var _list = new List<Node>(list);//复制 不修改原始数据
    for (int i = _list.Count() - 1; i > -1; i--)//不能使用foreach 删除或者添加元素。顺序遍历,删除元素之后,需要对当前索引执行--操作。逆序删除节点不需要特殊处理           
    {
        Node node = _list[i];
        if (!IsRoot(node))//顶级节点
        {
            Node pNode = _list.FirstOrDefault(a => a.Id == node.PId);//找到父节点
            if (pNode != null)
            {
                pNode.Children.Add(node);//添加节点
            }
            _list.RemoveAt(i);//无论是否找到 删除,剩下的全部为顶级节点
        }
    }
    return _list;
}

                    
                
                
            
        
浙公网安备 33010602011771号