递归介绍
- 递归算法在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。众所周知的数据结构的快速排序算法就是基于递归实现的。递归这是一种很重要的算法思想,在C语言,Java中普遍应用,大学期间参加算法竞赛递归算法在所难免。
- 根据我个人的理解,一个递归程序就是方法自身调用自身,一个递归函数中主要包含三个部分,第一递归出口,第二执行的逻辑代码,第三子问题递归执行。
案例介绍
递归获取菜单树
第一种: stream流实现(推荐)
/**
* stream流获取菜单树
* @param parentId
* @param list
* @return
*/
public List<Menu> selectTree(Long parentId,List<Menu> list) {
List<Menu> leve1Menus = list.stream().filter(treeEntity ->
treeEntity.getPid().equals(parentId)
).map((root) -> {
root.setChildren(getChildNode(root,list));
return root;
}).collect(Collectors.toList());
return leve1Menus;
}
/**
* 递归子节点
* @param root 当前单个菜单
* @param allListTree 表中的所有菜单集合
* @return
*/
private List<Menu> getChildNode(Menu root, List<Menu> allListTree){
List<Menu> ChildNodeList = allListTree.stream().filter((treeEntity) -> {
return treeEntity.getPid().equals(root.getId());
}).map((treeEntity)->{
treeEntity.setChildren(getChildNode(treeEntity,allListTree));
return treeEntity;
}).collect(Collectors.toList());
return ChildNodeList;
}
第二种: 普通递归实现
/**
* 递归返回菜单树
* @param list
* @return
*/
public static List<Menu> getListMenu(Long parentId,List<Menu> list) {
List menuListTree = new ArrayList<>();
for (Menu menu : list) {
if (menu.getPid() == parentId) {
//如果是根节点(存在多个根)
List<Menu> children = getListMenu(menu.getId(), list);
if (!children.isEmpty()){
menu.setChildren(children);
}
menuListTree.add(menu);
}
}
return menuListTree;
}
第三种 :
@Override
@Transactional(readOnly = true)
public List<TreeNodeV2> queryTreeNode() {
//根节点集合
List<TreeNodeV2> tops = new ArrayList<>();
//非跟节点集合
List<TreeNodeV2> subs = new ArrayList<>();
//所有节点集合
List<TreeNodeV2> all = new ArrayList<>();
List<TbContentCategory> categories = contentCategoryMapper.selectAll();
//获取根节点
for(TbContentCategory category : categories) {
TreeNodeV2 node = new TreeNodeV2(category.getId(),category.getParentId(), category.getName(), category.getIsParent()==1?false:true, new ArrayList<>());
if (category.getParentId() == 0) {
tops.add(node);
} else {
subs.add(node);
}
all.add(node);
}
//遍历所有的非根节点
for (TreeNodeV2 node : subs) {
//根据父节点id在all找当前节点的父节点
/*
for (TreeNodeV2 n : all) {
if (n.getId() == node.getParentId()) {
n就是parent
}
}
*/
//node的父节点
TreeNodeV2 parent = all.stream().filter(n -> node.getParentId().equals(n.getId())).findFirst().orElse(null);
//将当前节点存放到parent的children中
if (parent != null) {
parent.getChildren().add(node);
}
}
return tops;
}
遍历当前节点到根节点的路径信息
private static List<Menu> pathList = new ArrayList<>();
/**
* 组装当前节点到根节点的路径信息保存到pathList
* @param menu 当前节点编号
* @param parentId 根节点编号
* @param list 所有节点数据
*/
public void assumbleParentMenu (Menu menu,Long parentId,List<Menu> list) {
if(menu.getId().equals(parentId)) {
return;
}
pathList.add(menu);
Menu menu1 = list.stream().filter(item -> item.getId().equals(menu.getPid())).findFirst().orElse(null);
assumbleParentMenu(menu1,parentId,list);
}
递归获取当前节点(不包含自身)的所有孩子节点
private static List<Long> menuIds = new ArrayList<>();
/**
* 获取当前节点(不包含自身)的所有孩子节点的物理主键
* @param menu 当前节点
* @param list 所有节点数据
*/
public static void getMenuChildren(Menu menu,List<Menu> list) {
List<Menu> collect = list.stream().filter(item -> item.getPid().equals(menu.getId())).collect(Collectors.toList());
if (!collect.isEmpty()) {
menuIds.addAll(collect.stream().map(Menu::getId).collect(Collectors.toList()));
for(Menu temp : collect) {
getMenuChildren(temp,list);
}
} else {
return;
}
}
踩坑记录–不断补充
Java 栈溢出
- 首先查看数据库的数据是否存在不断相互调用的,因为JVM中Java的执行的压栈执行,这样不断压栈,就会导致最后的栈溢出。