tree 树形递归解析算法,处理特殊的根节点条件,特定的顶级顶层条件。树形获取顶级下面所有的子级id 电脑版发表于:2024/11/7 21:19 [TOC] **一般情况下的递归解析树形参考:** https://www.tnblog.net/aojiancc2/article/details/4785 这个是sqlsugar封装的关于树形相关的方法 https://www.donet5.com/home/doc?masterId=1&typeId=2311 ### 树形递归解析算法,处理特殊的根节点条件,特定的顶级顶层条件 在sqlsguar中有一个这样的表 ``` public class Tree { [SqlSugar.SugarColumn(IsPrimaryKey = true)] public int Id { get; set; } public string Name { get; set; } public int ParentId { get; set; } public string Type { get; set; } public List<Tree> Child { get; set; } } ``` 一般情况下顶级的都是ParentId=0或者是null,但是这里的情况有一点特殊,最顶层的数据是type='校区'或者type = '一级部门' 应该怎么写递归算法处理这种结构的数据成一个树形结构呢。 最顶层的数据的 `Type` 属性值为 `'校区'` 或 `'一级部门'`。以下是实现这个功能的代码示例: 首先,假设你已经从数据库中获取了所有的 `Tree` 对象并存储在一个列表中,比如 `List<Tree> allTrees`。 接下来,你可以使用以下递归算法来构建树形结构: ```csharp using System; using System.Collections.Generic; using System.Linq; public class Tree { [SqlSugar.SugarColumn(IsPrimaryKey = true)] public int Id { get; set; } public string Name { get; set; } public int ParentId { get; set; } public string Type { get; set; } public List<Tree> Child { get; set; } = new List<Tree>(); } public class TreeBuilder { public List<Tree> BuildTree(List<Tree> allTrees) { // 查找根节点,即 Type 为 '校区' 或 '一级部门' 的节点 var roots = allTrees.Where(t => t.Type == "校区" || t.Type == "一级部门").ToList(); // 创建一个字典,方便根据 Id 快速查找节点 var treeLookup = allTrees.ToDictionary(t => t.Id); // 递归函数,构建树形结构 void BuildTreeRecursive(Tree parent) { foreach (var childId in allTrees .Where(t => t.ParentId == parent.Id) .Select(t => t.Id)) { if (treeLookup.TryGetValue(childId, out var child)) { parent.Child.Add(child); BuildTreeRecursive(child); } } } // 对每个根节点调用递归函数 foreach (var root in roots) { BuildTreeRecursive(root); } return roots; } } // 示例用法 public class Program { public static void Main() { // 假设这是从数据库中获取的所有 Tree 对象 List<Tree> allTrees = new List<Tree> { // 示例数据 new Tree { Id = 1, Name = "校区A", ParentId = 0, Type = "校区" }, new Tree { Id = 2, Name = "部门1", ParentId = 1, Type = "一级部门" }, new Tree { Id = 3, Name = "部门2", ParentId = 1, Type = "一级部门" }, new Tree { Id = 4, Name = "小组1", ParentId = 2, Type = "小组" }, new Tree { Id = 5, Name = "小组2", ParentId = 2, Type = "小组" }, // 添加更多数据... }; TreeBuilder builder = new TreeBuilder(); List<Tree> treeStructure = builder.BuildTree(allTrees); // 输出或处理 treeStructure PrintTree(treeStructure, 0); } public static void PrintTree(List<Tree> trees, int indent) { foreach (var tree in trees) { Console.WriteLine($"{new string(' ', indent * 2)}{tree.Name} ({tree.Type})"); PrintTree(tree.Child, indent + 1); } } } ``` #### 代码解释 1. **Tree 类**:包含树节点的定义,包括 `Id`, `Name`, `ParentId`, `Type`, 和 `Child` 列表。 2. **TreeBuilder 类**:包含构建树形结构的 `BuildTree` 方法。 3. **BuildTree 方法**: - 首先,查找根节点,即 `Type` 为 `'校区'` 或 `'一级部门'` 的节点。 - 创建一个字典 `treeLookup`,用于根据 `Id` 快速查找节点。 - 定义递归函数 `BuildTreeRecursive`,用于构建树形结构。 - 对每个根节点调用递归函数。 4. **PrintTree 方法**:用于打印树形结构,便于验证结果。 5. **示例用法**:创建一些示例数据,并调用 `BuildTree` 方法构建树形结构,最后打印出来。 通过这种方式,你可以将平面表数据转换为树形结构,并处理特定的根节点条件。 ### 树形获取顶级下面所有的子级id 代码如下: ```csharp using System; using System.Collections.Generic; using System.Linq; public class Tree { public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } public string Type { get; set; } public List<Tree> Child { get; set; } = new List<Tree>(); } public class TreeHelper { public Dictionary<int, List<int>> GetAllSubDepartmentIds(List<Tree> trees) { // 字典来存储每个顶级部门ID及其所有子部门ID Dictionary<int, List<int>> subDepartmentIdsByTopDepartment = new Dictionary<int, List<int>>(); // 找出所有顶级节点(没有父节点的节点) var topDepartments = trees.Where(t => t.Type == "校区" || t.Type == "一级部门").ToList(); foreach (var topDepartment in topDepartments) { List<int> subIds = new List<int>(); // 递归收集当前顶级节点下的所有子节点ID CollectSubDepartmentIds(topDepartment, subIds); // 将结果添加到字典中 subDepartmentIdsByTopDepartment[topDepartment.Id] = subIds; } return subDepartmentIdsByTopDepartment; } private void CollectSubDepartmentIds(Tree node, List<int> subDepartmentIds) { // 添加当前节点的ID(通常顶级节点不应该在这里添加,但在递归中保持一致性) // 注意:如果顶级节点的ID不应该出现在子节点列表中,则应该在此处进行条件判断 // 例如:if (node.ParentId != null) subDepartmentIds.Add(node.Id); // 但由于我们是从顶级节点开始的,所以这里直接添加是安全的(对于子节点递归调用) subDepartmentIds.Add(node.Id); // 递归处理子节点 foreach (var child in node.Child) { CollectSubDepartmentIds(child, subDepartmentIds); } } } // 示例用法 public class Program { public static void Main() { // 假设这是从数据库中获取的所有Tree对象 List<Tree> trees = new List<Tree> { // ...(填充示例数据) // 例如: // new Tree { Id = 1, Name = "顶级部门1", ParentId = null, Type = "一级部门", Child = new List<Tree> { ... } }, // new Tree { Id = 2, Name = "顶级部门2", ParentId = null, Type = "一级部门", Child = new List<Tree> { ... } }, // ... }; TreeHelper helper = new TreeHelper(); Dictionary<int, List<int>> allSubDepartmentIds = helper.GetAllSubDepartmentIds(trees); // 输出每个顶级部门及其所有子部门的ID foreach (var kvp in allSubDepartmentIds) { Console.WriteLine($"顶级部门ID: {kvp.Key}, 子部门ID: {string.Join(", ", kvp.Value)}"); } } } ``` ### 和树形结构表,关联的用户表,两个表一起解析出来数据 现在还有一个表,表结构如下: ``` public class VAccountDepartment { public string AccountID { get; set; } public string Name { get; set; } public int? DepartID { get; set; } } ``` 这里边的DepartID就是Tree里边的Id,每个tree里边的id都可能有这个数据,选中想把tree的子级也包含进行这块用户的数据。 为了将 `VAccountDepartment` 表与 `Tree` 表关联起来,并构建一个包含这些关联信息的树形结构,你需要对之前的逻辑进行一些调整。这里的关键是,你需要根据 `VAccountDepartment` 表中的 `DepartID` 来找到对应的 `Tree` 节点,并将 `VAccountDepartment` 实例附加到这些节点上。 以下是一个可能的实现方式: 1. 首先,从数据库中获取 `Tree` 和 `VAccountDepartment` 的所有数据。 2. 构建 `Tree` 的树形结构,如之前所示。 3. 遍历 `VAccountDepartment` 列表,将每个实例附加到对应的 `Tree` 节点上。 这里是一个修改后的代码示例: ```csharp using System; using System.Collections.Generic; using System.Linq; public class Tree { public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } public string Type { get; set; } public List<Tree> Child { get; set; } = new List<Tree>(); public List<VAccountDepartment> Departments { get; set; } = new List<VAccountDepartment>(); } public class VAccountDepartment { public string AccountID { get; set; } public string Name { get; set; } public int? DepartID { get; set; } } public class TreeBuilder { public List<Tree> BuildTreeWithDepartments(List<Tree> allTrees, List<VAccountDepartment> allDepartments) { // 构建树形结构 var roots = BuildTree(allTrees); // 创建一个字典,方便根据 Id 快速查找树节点 var treeLookup = roots.SelectMany(r => GetAllDescendants(r)).ToDictionary(t => t.Id); // 将 VAccountDepartment 实例附加到对应的 Tree 节点上 foreach (var dept in allDepartments) { if (dept.DepartID.HasValue && treeLookup.TryGetValue(dept.DepartID.Value, out var treeNode)) { treeNode.Departments.Add(dept); } // 如果需要处理 DepartID 为 null 的情况,可以在这里添加逻辑 } return roots; } private List<Tree> BuildTree(List<Tree> allTrees) { // 查找根节点 var roots = allTrees.Where(t => t.Type == "校区" || t.Type == "一级部门").ToList(); // 递归函数,构建树形结构 void BuildTreeRecursive(Tree parent) { foreach (var child in allTrees.Where(t => t.ParentId == parent.Id)) { parent.Child.Add(child); BuildTreeRecursive(child); } } // 对每个根节点调用递归函数 foreach (var root in roots) { BuildTreeRecursive(root); } return roots; } private IEnumerable<Tree> GetAllDescendants(Tree node) { return new[] { node }.Concat(node.Child.SelectMany(GetAllDescendants)); } } // 示例用法 public class Program { public static void Main() { // 假设这是从数据库中获取的所有 Tree 对象 List<Tree> allTrees = new List<Tree> { // ...(示例数据) }; // 假设这是从数据库中获取的所有 VAccountDepartment 对象 List<VAccountDepartment> allDepartments = new List<VAccountDepartment> { // ...(示例数据) }; TreeBuilder builder = new TreeBuilder(); List<Tree> treeStructureWithDepartments = builder.BuildTreeWithDepartments(allTrees, allDepartments); // 输出或处理 treeStructureWithDepartments // ... } } ``` 在这个示例中,`BuildTreeWithDepartments` 方法首先调用 `BuildTree` 来构建树形结构,然后创建一个字典 `treeLookup` 来存储所有树节点,以便快速查找。接下来,它遍历 `allDepartments` 列表,并将每个 `VAccountDepartment` 实例附加到对应的 `Tree` 节点上。`GetAllDescendants` 方法是一个辅助方法,用于递归地获取树的所有后代节点。 请注意,这个实现假设 `DepartID` 在 `VAccountDepartment` 表中是唯一的,并且每个 `DepartID` 都对应一个 `Tree` 节点。如果这不是情况,你可能需要添加一些额外的逻辑来处理重复或缺失的 `DepartID` 值。