N叉树识别未被完全标记的节点

533 阅读3分钟

场景

最近开发遇到一个菜单树授权的问题,菜单树由一个N叉树构建,当客户端发起请求后从数据库加载菜单实体组装层级并返回,客户端根据返回按层级展示,类似结构如下:
存在一个对菜单授权的业务场景,当对菜单授权时会将菜单对父级节点给标记上,假设用户可以看到A B C D E F G I K菜单,那么如下图黄色节点将会被标记并作为参数传入 存在一个授权停用的场景,对已授权的菜单进行停用,如果对上图被标记的黄节点进行了授权,那么授权停用后,用户只能看到,H J L M 菜单,当然此时菜单展示的层级不允许改变,此场景的问题就转变为了要在上图黄色节点中找出子节点未被完全标记的节点.最后展示给用户的是A F H I J L M节点组成的菜单

问题

根据黄色节点,找出子节点未被完全标记的节点A F I

解决

实体模型:菜单类实体模型Resource包含了两个关键元素

public class Resource {
    private String id;
    private List<Resource> children;
} 

在下面方法中: menu表示完整的菜单树对象,resIds表示被标记的黄色节点集合,markResIds表示所求结果集

    public  Integer markNode(List<Resource> menu, List<String> resIds, List<String> markResIds) {
        int markSize = 0;
        for (Resource res : menu) {
            int childrenMarkSize = 0;
            if (hasChildren(res)) {
                childrenMarkSize = markNode(res.getChildren(), resIds, markResIds);
            }
            if (resIds.contains(res.getId())) {
                markSize++;
            }
            if (hasChildren(res) && markSize != 0) {
                if (res.getChildren().size() > childrenMarkSize) {
                    markResIds.add(res.getId());
                    markSize--;
                }
            }

        }
        return markSize;
    }

    private static boolean hasChildren(Resource res) {
        return res.getChildren() != null && res.getChildren().size() > 0;
    }  

此方法使用了递归遍历N叉树
临时变量childrenMarkSize表示同一父级节点下子节点被标记的个数
临时变量markSize表示同一层级节点被标记的个数

  • 1、判断是否存在子节点,如果存在子节点,遍历子节点时,统计子节点被标记的个数childrenMarkSize并返回,使用childrenMarkSize能够判断当前节点是否被完全标记
  • 2、判断当前节点是否被标记,如果被标记,针对当前节点层级,统计被标记的个数markSize
  • 3、判断是否存在子节点,且当前节点以及兄弟节点被标记的个数不等于0(不等于0的情况表示如果当前节点子节点没有任何一个被标记,则不需要进行任何处理)的情况下,使用子节点的个数与当前节点被标记的子节点数进行比较
  • 4、如图F G H的树,当G H遍历完成后,进行当前节点为F的处理时,因为G被标记H未被标记所以childrenMarkSize=1,此时因为F在标记列表中,所以会执行markSize++操作,而F节点是未完全标记的,针对这种情况,未了避免F的父级节点A统计childrenMarkSize错误,在将F放入结果集时,需要进行markSize--操作
  • 5、求的结果集markResIds