递归是什么
递归是程序调用自身的编程技巧,和死循环的区别在于它有边界条件,不满足继续执行,满足那么返回。举个例子:两个镜子,镜面相对,这时不论看哪个镜子都会看到镜子里面有镜子无限循环。
在哪里用
理论上具有无限层级关系的数据结构或者算法计算:
- 数据表中具有ParentId的,例如:角色、公司 、菜单 、文件夹 ;
- 斐波那契数列又名兔子数列等。
如果这些数据保存在数据库中如何查询
查询数据库中ParentId为’db063c8c-3c4b-435b-82ca-c951f7302c3d’的所有子记录
以下sql为递归公用表达式
with cte as
(
select * from t_Role where ParentId='db063c8c-3c4b-435b-82ca-c951f7302c3d' --定位点成员
union all
select a.* from t_Role a join cte b on a.ParentId=b.Id --递归成员
)
select * from cte
注解:这个sql核心功能实现由定位点成员和递归成员构成
- 定位点成员:查询递归开始的边界条件
- 递归成员:用满足边界条件的结果根据递归成员的条件接继续查询,且将查询结果与之合并
详细用法看前面两篇文章
那么问题来了,怎么定义和获取无限极数据结构
如何在程序中将上述sql查询结果填充到下面的模型呢
无限极结构定义:
public class Tree
{
public Guid Id { get; set; }
public string Title { get; set; }
public Guid ParentId { get; set; }
public bool Checked { get; set; }
public List<Tree> Children { get; set; }
}
毫无疑问要用递归
在平时有部分基础好整体对技术的应用差的技术人员会在方法外定义一个静态变量,来保存递归结果,这样不合理,不建议用。
那么想想要什么条件才能解决这个问题,一步步分析。
最后输出的结果是List<Tree> trees;
也就是我们在递归方法返回和进入递归方法时都需要用到这个变量实例,那么方法的参数传递默认是值传递(值类型、引用类型参数在进入方法时都是值传递),值传递会丢失原来的数据,所以我们要用一个引用传递的变量,那么ref
关键字闪亮登场。
ok,那就来贴代码
public List<Tree> GetChildTrees(Guid parentRoleId, List<Guid> checkIds = null)
{
List<RoleInfo> roleInfos = null;//parentRoleId作为条件执行上述sql查询的结果;
if (roleInfos.IsNullOrEmpty())
return null;
List<Tree> parentTrees = roleInfos.Select(x => new Tree
{
Id = x.Id,
Title = x.Name,
ParentId = x.ParentId,
Checked = checkIds.IsNotNull() ? checkIds.Contains(x.Id) : false,
}).ToList();
GetTree(ref parentTrees);
return parentTrees;
void GetTree(ref List<Tree> trees)
{
if (trees.IsNullOrEmpty())
return;
List<Tree> tempTrees;
foreach (var tree in trees)
{
tempTrees = allTrees.FindAll(x => x.ParentId == tree.Id);
tree.Children = tempTrees;
GetTree(ref tempTrees);
}
}
}
总结
递归是什么,哪些场景用到
在数据库中如何查询指定ParentId的所有子集
递归公用表达式怎么用,原理是什么
如何在代码数据结构中优雅的表示递归结构
才疏学浅,还望指点