递归&图解Json

一、递归思想

  • 什么是递归?

    递归是指在函数的定义中使用函数自身的方法。也就是一个函数在执行过程中直接或间接地调用自身,通过不断地将原问题分解为规模更小但结构相似的子问题,直到子问题简单到可以直接求解,然后再逐步回溯得到原问题的解。

  • 递归在数据结构方面可以解决的问题

    树形结构操作

    遍历:树的前序、中序、后序遍历都可以使用递归实现。例如,在二叉树中,前序遍历是先访问根节点,然后递归遍历左子树和右子树。

    查找:在树中查找特定节点,递归地在子树中进行查找操作。

    插入与删除:在树结构中插入或删除节点,递归地找到合适的位置进行操作。

    链表操作

    反转链表:递归地反转链表的剩余部分,然后调整指针方向。

    查找链表中的元素:递归地在链表的后续节点中查找目标元素。

  • 递归的几个重要条件

  1. 必须要在一个方法中直接或间接调用自己。
  2. 递归必须要有结束条件,否则会无限递归。递归的结束条件决定递归最终的返回结果或数据状态。
  3. 递归的返回值永远是返回给上一层的调用者。

二、递归案例

  • 使用递归计算0-100的和。

如果使用递归计算可以这样设计参数,第一个参数是当前值,第二个参数是累加值,这两个数执行累加,然后继续调用函数,将下一个值和累加值继续传递,直接最后的值为1,则累加1返回,注意,这里就是递归的结束条件。

java 复制代码
    @Test
    public void test() {
        System.out.println(sum(100,0));
    }

    public static Integer sum(Integer max,Integer total){
        if (max == 1){
            return total + 1;
        }
        total = total + max;
        max--;
        Integer sum = sum(max, total);
        return sum;
    }
  • 使用递归组装数据结构
java 复制代码
@Data
@Builder
public class Node {

    private String id;
    private String parentId;
    private List<Node> children;
}
java 复制代码
 public static void main(String[] args) throws Exception {

        List<Node> nodes = new ArrayList<>();
        nodes.add(Node.builder().id("A").build());
        nodes.add(Node.builder().id("B").parentId("A").build());
        nodes.add(Node.builder().id("C").parentId("A").build());
        nodes.add(Node.builder().id("D").parentId("B").build());
        nodes.add(Node.builder().id("E").parentId("B").build());
        nodes.add(Node.builder().id("F").parentId("C").build());
        nodes.add(Node.builder().id("G").parentId("C").build());
        nodes.add(Node.builder().id("H").parentId("D").build());
        nodes.add(Node.builder().id("I").parentId("D").build());
        nodes.add(Node.builder().id("J").parentId("D").build());
        nodes.add(Node.builder().id("K").parentId("E").build());
        nodes.add(Node.builder().id("L").parentId("E").build());
        nodes.add(Node.builder().id("M").parentId("F").build());
        nodes.add(Node.builder().id("N").parentId("I").build());

        //入口节点
        List<Node> rootList = nodes.stream().filter(item -> item.getParentId() == null)
                .collect(Collectors.toList());

        for (Node root : rootList) {
            root.setChildren(getChildren(root,nodes));
        }

        System.out.println(JSONObject.toJSONString(rootList));

    }

    private static List<Node> getChildren(Node root, List<Node> nodes) {
        //获取直接子节点
        List<Node> returnChildList = new ArrayList<>();
        List<Node> childList = nodes.stream().filter(item -> item.getParentId() != null && item.getParentId().equals(root.getId()))
                .collect(Collectors.toList());
        if (childList.isEmpty()){
            //递归结束条件,路径中最后一层无子节点时结束本次递归
            return childList;
        }
        //直接子节点递归获取
        for (Node childItem : childList) {
            List<Node> children = getChildren(childItem, nodes);
            //判断这个节点是否命中禁用节点,如果命中,则将其子节点提升
            childItem.setChildren(children);
            returnChildList.add(childItem);
        }

        return returnChildList;
    }

这里的getChildren方法接收一个node 节点 以及所有node节点,作用是返回这个node节点的所有子节点,

List childList = nodes.stream().filter(item -> item.getParentId() != null && item.getParentId().equals(root.getId()))

.collect(Collectors.toList());

这里是获取了node节点的所有子节点,但是每一个子节点依然还要获取它的子节点,这就产生了递归。那么递归必须要有结束条件,这里的结束条件就是当某一个子节点没有子节点时递归就结束了。这里的递归返回值是当前参数中node节点的子节点,这样,通过递归,每一层都是返回的都是带着自己子节点的节点。

这个图形化Json工具可以让Json字符串图形化展示,可以更好的理解递归。

https://json.0xfab1.net/editor

通过可视化图可以看到,Json crack非常直观的将结果可视化了。

  • 节点检索

当把K、L节点分别修改为EK、EL,且想检索带E的节点。

此时最终检索出来的节点关系是这样的:

实现节点检索的步骤:

  1. 获取node节点的直接子节点,此时遍历获取到所有子节点,继续获取子节点的子节点。
  2. 判断每个子节点是否包含检索关键字,如果包含,则此节点符合条件,添加至返回结果。
  3. 如果不包含,则丢弃此节点,返回此节点的子节点,因为子节点是递归获取到的,如果子节点有值,说明子节点肯定是包含了搜索关键字。
java 复制代码
    private static String keyWord = "E";

    public static void main(String[] args) throws Exception {

        List<Node> nodes = new ArrayList<>();
        nodes.add(Node.builder().id("A").build());
        nodes.add(Node.builder().id("B").parentId("A").build());
        nodes.add(Node.builder().id("C").parentId("A").build());
        nodes.add(Node.builder().id("D").parentId("B").build());
        nodes.add(Node.builder().id("E").parentId("B").build());
        nodes.add(Node.builder().id("F").parentId("C").build());
        nodes.add(Node.builder().id("G").parentId("C").build());
        nodes.add(Node.builder().id("H").parentId("D").build());
        nodes.add(Node.builder().id("I").parentId("D").build());
        nodes.add(Node.builder().id("J").parentId("D").build());
        nodes.add(Node.builder().id("KE").parentId("E").build());
        nodes.add(Node.builder().id("LE").parentId("E").build());
        nodes.add(Node.builder().id("M").parentId("F").build());
        nodes.add(Node.builder().id("N").parentId("I").build());

        //入口节点
        List<Node> rootList = nodes.stream().filter(item -> item.getParentId() == null)
                .collect(Collectors.toList());

        List<Node> res = new ArrayList<>();

        for (Node root : rootList) {
            List<Node> children = getChildren(root, nodes);
            String id = root.getId();
            if (id.contains(keyWord)){
                root.setChildren(children);
                res.add(root);
            }else {
                //返回子节点
                res.addAll(children);
            }
        }

        System.out.println(JSONObject.toJSONString(res));

    }

    private static List<Node> getChildren(Node root, List<Node> nodes) {
        //获取直接子节点
        List<Node> returnChildList = new ArrayList<>();
        List<Node> childList = nodes.stream().filter(item -> item.getParentId() != null && item.getParentId().equals(root.getId()))
                .collect(Collectors.toList());
        if (childList.isEmpty()){
            //递归结束条件,路径中最后一层无子节点时结束本次递归
            return childList;
        }
        //直接子节点递归获取
        for (Node childItem : childList) {
            List<Node> children = getChildren(childItem, nodes);
            String id = childItem.getId();
            if (id.contains(keyWord)){
                childItem.setChildren(children);
                returnChildList.add(childItem);
            }else {
                //返回子节点
                returnChildList.addAll(children);
            }
        }

        return returnChildList;
    }
相关推荐
林的快手8 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
css趣多多17 小时前
初步安装和使用vant组件库,使用css变量定制vant主题样式 ,小程序的API Promise化,调用promise化之API
json
Web极客码1 天前
WordPress“更新失败,响应不是有效的JSON响应”问题的修复
json·github·wordpress
gywl3 天前
Spring Web MVC入门
spring·json·mvc·注解·cookie·session
运维小文4 天前
Mongodb数据管理
数据库·mongodb·json·非关系数据库·文档型数据库
玉阳软件yuyangdev_cn4 天前
铁塔电单车协议对接电单车TCP json协议对接成熟充电桩系统搭建低速充电桩TCP 接口规范
网络·tcp/ip·json·铁塔协议
m0_748232925 天前
SpringCloud系列教程:微服务的未来 (五)枚举处理器、JSON处理器、分页插件实现
spring cloud·微服务·json
SendSi5 天前
Unity使用反射进行Protobuf(CS/SC)协议,json格式
unity·json·游戏引擎
CURRY30_HJH5 天前
MYSQL直接在SQL提取json字符串中的内容-----将13位时间戳转换成标准的日期格式【记录SQL常用函数】
数据库·sql·json
MrJson-架构师6 天前
json-schema 的编辑器
编辑器·json