【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)

前言:

最近在项目中开发了一个组织树的功能,正好使用了深度优先遍历和广度优先遍历进行结点遍历,觉得非常有意思这里做一下笔记记录一下。

提示:部门表设计可以查看以下链接:
MYSQL8使用CTE实现递归遍历


深度优先遍历(DFS)

定义:

深度优先遍历全称叫Depth First Search(简称DFS),例如查询一棵树从顶级节点往下找所有子节点,深度优先遍历会一条路走到底,直到遇到“死胡同”才会返回到i上一节点(回溯),如果上一节点没有其他路可以探索,则会继续原路返回探索别的路线,直到遍历完最后退出

【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第1张图片
回溯之后从上一节点依次递推继续探索,最终路线图如下:
【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第2张图片

项目应用

一、项目需求:

组织架构功能,公司下还有多个部门,每个部门下有对应的员工,要求展示公司部门关系以及部门与员工的关系,要求将整个公司的组织架构下所有的结构和员工返回,一个公司组织架构如下:
【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第3张图片

二、设计思路

先构建部门树形关系,再查询所有员工根据部门进行分组,使用深度优先遍历DFS挂载每个部门下的所有员工。以下是部门和员工表结构设计和表数据:

1.创建用户表user_info结构和数据
CREATE TABLE `user_info`  (
  `id` bigint NOT NULL COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '姓名',
  `department_id` bigint NULL DEFAULT 0 COMMENT '部门id',
  `delete_flag` int NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES (0, 'Jerry', 4, 0);
INSERT INTO `user_info` VALUES (1, 'Jone', 1, 0);
INSERT INTO `user_info` VALUES (2, 'Jack', 1, 0);
INSERT INTO `user_info` VALUES (3, 'Tom', 2, 0);
INSERT INTO `user_info` VALUES (4, 'Sandy', 3, 0);
INSERT INTO `user_info` VALUES (5, 'Billie', 4, 0);

SET FOREIGN_KEY_CHECKS = 1;

【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第4张图片

2.创建部门表department结构和数据
CREATE TABLE `department`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '部门名称',
  `parent_id` bigint NULL DEFAULT NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
  `delete_flag` int NULL DEFAULT 0 COMMENT '1无效 0有效',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of department
-- ----------------------------
INSERT INTO `department` VALUES (1, '我的公司', NULL, '2022-04-23 22:38:49', '2022-04-23 23:03:55', 0);
INSERT INTO `department` VALUES (2, '技术部', 1, '2022-04-23 23:04:00', '2022-04-23 23:04:03', 0);
INSERT INTO `department` VALUES (3, '财务部', 1, '2022-04-23 23:17:19', '2022-04-23 23:17:22', 0);
INSERT INTO `department` VALUES (4, '大数据部', 2, '2022-04-23 23:30:26', '2022-04-23 23:30:28', 0);
INSERT INTO `department` VALUES (5, '商品部', 2, '2022-04-23 23:30:50', '2022-04-23 23:30:51', 0);
INSERT INTO `department` VALUES (6, '工程部', 1, '2022-04-23 23:31:12', '2022-04-23 23:31:14', 0);
INSERT INTO `department` VALUES (7, '测试部', 2, '2022-04-23 23:31:34', '2022-04-23 23:31:36', 0);
INSERT INTO `department` VALUES (8, '大数据研发一部', 4, '2022-04-23 23:55:05', '2022-04-24 22:34:17', 0);
INSERT INTO `department` VALUES (9, '大数据开发一部', 4, '2022-04-24 22:34:25', '2022-04-24 22:34:27', 0);
INSERT INTO `department` VALUES (10, '测试一部', 7, '2022-04-24 22:48:24', '2022-04-24 23:10:59', 0);

SET FOREIGN_KEY_CHECKS = 1;

【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第5张图片

三、代码实现:

//部门实体
public class Department {
    private Long id;
    private String name;
    private Long parentId;
    private Date createTime;
    private Date updateTime;
    private Integer deleteFlag;
    private List<Department> childList;
    private List<UserInfo> users;
}
//用户实体
public class UserInfo {
    private Long id;
    private String name;
    private Long departmentId;
}
public class DFSTestMain {
    public static void main(String[] args) throws SQLException {
        List<Department> departmentList = getDepartmentList();
        List<UserInfo> userList = getUserList();

        Department depTree = buildDepartment(departmentList);
        Map<Long, List<UserInfo>> userInfoMap = userList.stream().collect(Collectors.groupingBy(UserInfo::getDepartmentId));

        dfsLoadUsers(depTree , userInfoMap);
        System.out.println(JSON.toJSONString(depTree));
    }
	//获取用户列表
    private static List<UserInfo> getUserList() throws SQLException {
        List<UserInfo> userInfoList = new ArrayList<>();
        String userSql = "SELECT * FROM user_info WHERE delete_flag = 0";
        Connection con = JdbcUtil.getCon();
        Statement userStat = con.createStatement();
        ResultSet userRs = userStat.executeQuery(userSql);

        while (userRs.next()) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(userRs.getLong(1));
            userInfo.setName(userRs.getString(2));
            userInfo.setDepartmentId(userRs.getLong(3));
            userInfoList.add(userInfo);
        }
        return userInfoList;
    }
	//获取部门列表
    private static List<Department> getDepartmentList() throws SQLException {
        List<Department> departmentList = new ArrayList<>();
        String depSql = "SELECT * FROM department WHERE delete_flag = 0";
        Connection con = JdbcUtil.getCon();
        Statement depStat = con.createStatement();
        ResultSet depRs = depStat.executeQuery(depSql);

        while (depRs.next()) {
            Department department = new Department();
            department.setId(depRs.getLong(1));
            department.setName(depRs.getString(2));
            department.setParentId(depRs.getLong(3));
            department.setCreateTime(depRs.getDate(4));
            department.setUpdateTime(depRs.getDate(5));
            department.setDeleteFlag(depRs.getInt(6));
            departmentList.add(department);
        }
        return departmentList;
    }

    /**
     * TODO 建立部门树
     *
     * @param departmentList
     * @return
     */
    private static Department buildDepartment(List<Department> departmentList) {
        Map<Long, Department> depMapById = departmentList.stream().collect(Collectors.toMap(Department::getId, dep -> dep));
        Department topDep = null;
        for (Department dep : departmentList) {
            Long parentId = dep.getParentId();
            //TODO 无父节点为顶节点
            if (null == parentId) {
                topDep = dep;
                continue;
            }
            //TODO 无父节点为顶节点
            Department parentDep = depMapById.get(parentId);
            if (null == parentDep) {
                topDep = dep;
                continue;
            }
            List<Department> parentDepChildList = parentDep.getChildList();
            if (CollectionUtils.isEmpty(parentDepChildList)) {
                parentDep.setChildList(Lists.newArrayList(dep));
            } else {
                parentDepChildList.add(dep);
            }
        }
        return topDep;
    }

	//深度优先遍历将员工挂载到每个部门节点
    private static void dfsLoadUsers(Department tree, Map<Long, List<UserInfo>> deptIdUsersMap) {
        if (tree == null) {
            return;
        }

        List<Department> childList = tree.getChildList();
        if (CollectionUtils.isEmpty(childList)) {
            tree.setUsers(new ArrayList<>());
            return;
        }
        List<UserInfo> userInfos = deptIdUsersMap.get(tree.getId());
        if (!CollectionUtils.isEmpty(userInfos)) {
            tree.setUsers(userInfos);
        } else {
            tree.setUsers(new ArrayList<>());
        }
        for (Department next : childList) {
            dfsLoadUsers(next, deptIdUsersMap);
        }
    }
}

四、运行结果

{
    "childList":[
        {
            "childList":[
                {
                    "childList":[
                        {
                            "createTime":"2022-04-23",
                            "deleteFlag":0,
                            "id":8,
                            "name":"大数据研发一部",
                            "parentId":4,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        },
                        {
                            "createTime":"2022-04-24",
                            "deleteFlag":0,
                            "id":9,
                            "name":"大数据开发一部",
                            "parentId":4,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        }
                    ],
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":4,
                    "name":"大数据部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[
                        {
                            "departmentId":4,
                            "id":0,
                            "name":"Jerry"
                        },
                        {
                            "departmentId":4,
                            "id":5,
                            "name":"Billie"
                        }
                    ]
                },
                {
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":5,
                    "name":"商品部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[

                    ]
                },
                {
                    "childList":[
                        {
                            "createTime":"2022-04-24",
                            "deleteFlag":0,
                            "id":10,
                            "name":"测试一部",
                            "parentId":7,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        }
                    ],
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":7,
                    "name":"测试部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[

                    ]
                }
            ],
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":2,
            "name":"技术部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[
                {
                    "departmentId":2,
                    "id":3,
                    "name":"Tom"
                }
            ]
        },
        {
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":3,
            "name":"财务部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[

            ]
        },
        {
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":6,
            "name":"工程部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[

            ]
        }
    ],
    "createTime":"2022-04-23",
    "deleteFlag":0,
    "id":1,
    "name":"我的公司",
    "parentId":0,
    "updateTime":"2022-04-23",
    "users":[
        {
            "departmentId":1,
            "id":1,
            "name":"Jone"
        },
        {
            "departmentId":1,
            "id":2,
            "name":"Jack"
        }
    ]
}

广度优先遍历(BFS)

定义:

广度优先遍历全称叫Breath First Search(简称BFS),例如查询一棵树从相邻节点进行探索,附近节点探索完毕之后会深入其他层进行查找,由近到远,需要记录之前走过的上一节点方便返回(回溯),继续探索其他节点直到结束,如图所示:
最终遍历顺序:A->B->C->D->E->F-G
【笔记】学习深度优先遍历(DFS)和广度优先遍历(BFS)_第6张图片

项目应用

一、项目需求

继续沿用上面项目的组织架构,这里采用广度优先遍历渲染整颗部门树。

二、代码实现

public class BFSTestMain {
    public static void main(String[] args) throws SQLException {
        List<Department> departmentList = getDepartmentList();
        List<UserInfo> userList = getUserList();

        Map<Long, List<Department>> departmentMap = departmentList.stream().collect(Collectors.groupingBy(Department::getParentId));
        Map<Long, List<UserInfo>> userInfoMap = userList.stream().collect(Collectors.groupingBy(UserInfo::getDepartmentId));
        LinkedList<Department> queue = new LinkedList<>();

        //根节点
        Department root = departmentMap.get(0L).get(0);
        queue.add(root);

        while (!queue.isEmpty()){
            Department node = queue.remove();
            Long id = node.getId();
            //获取子节点
            List<Department> childrenList = departmentMap.get(id);
            node.setChildList(childrenList);

            if (!CollectionUtils.isEmpty(childrenList)){
                queue.addAll(childrenList);
            }
        }

        dfsLoadUsers(root , userInfoMap);
        System.out.println(JSON.toJSONString(root));
    }

    private static List<UserInfo> getUserList() throws SQLException {
        List<UserInfo> userInfoList = new ArrayList<>();
        String userSql = "SELECT * FROM user_info WHERE delete_flag = 0";
        Connection con = JdbcUtil.getCon();
        Statement userStat = con.createStatement();
        ResultSet userRs = userStat.executeQuery(userSql);

        while (userRs.next()) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(userRs.getLong(1));
            userInfo.setName(userRs.getString(2));
            userInfo.setDepartmentId(userRs.getLong(3));
            userInfoList.add(userInfo);
        }
        return userInfoList;
    }

    private static List<Department> getDepartmentList() throws SQLException {
        List<Department> departmentList = new ArrayList<>();
        String depSql = "SELECT * FROM department WHERE delete_flag = 0 ";
        Connection con = JdbcUtil.getCon();
        Statement depStat = con.createStatement();
        ResultSet depRs = depStat.executeQuery(depSql);

        while (depRs.next()) {
            Department department = new Department();
            department.setId(depRs.getLong(1));
            department.setName(depRs.getString(2));
            department.setParentId(depRs.getLong(3));
            department.setCreateTime(depRs.getDate(4));
            department.setUpdateTime(depRs.getDate(5));
            department.setDeleteFlag(depRs.getInt(6));
            departmentList.add(department);
        }
        return departmentList;
    }

    private static void dfsLoadUsers(Department tree, Map<Long, List<UserInfo>> deptIdUsersMap) {
        if (tree == null) {
            return;
        }

        List<Department> childList = tree.getChildList();
        if (CollectionUtils.isEmpty(childList)) {
            tree.setUsers(new ArrayList<>());
            return;
        }
        List<UserInfo> userInfos = deptIdUsersMap.get(tree.getId());
        if (!CollectionUtils.isEmpty(userInfos)) {
            tree.setUsers(userInfos);
        } else {
            tree.setUsers(new ArrayList<>());
        }
        for (Department next : childList) {
            dfsLoadUsers(next, deptIdUsersMap);
        }
    }
}

运行结果

{
    "childList":[
        {
            "childList":[
                {
                    "childList":[
                        {
                            "createTime":"2022-04-23",
                            "deleteFlag":0,
                            "id":8,
                            "name":"大数据研发一部",
                            "parentId":4,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        },
                        {
                            "createTime":"2022-04-24",
                            "deleteFlag":0,
                            "id":9,
                            "name":"大数据开发一部",
                            "parentId":4,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        }
                    ],
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":4,
                    "name":"大数据部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[
                        {
                            "departmentId":4,
                            "id":0,
                            "name":"Jerry"
                        },
                        {
                            "departmentId":4,
                            "id":5,
                            "name":"Billie"
                        }
                    ]
                },
                {
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":5,
                    "name":"商品部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[

                    ]
                },
                {
                    "childList":[
                        {
                            "createTime":"2022-04-24",
                            "deleteFlag":0,
                            "id":10,
                            "name":"测试一部",
                            "parentId":7,
                            "updateTime":"2022-04-24",
                            "users":[

                            ]
                        }
                    ],
                    "createTime":"2022-04-23",
                    "deleteFlag":0,
                    "id":7,
                    "name":"测试部",
                    "parentId":2,
                    "updateTime":"2022-04-23",
                    "users":[

                    ]
                }
            ],
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":2,
            "name":"技术部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[
                {
                    "departmentId":2,
                    "id":3,
                    "name":"Tom"
                }
            ]
        },
        {
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":3,
            "name":"财务部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[

            ]
        },
        {
            "createTime":"2022-04-23",
            "deleteFlag":0,
            "id":6,
            "name":"工程部",
            "parentId":1,
            "updateTime":"2022-04-23",
            "users":[

            ]
        }
    ],
    "createTime":"2022-04-23",
    "deleteFlag":0,
    "id":1,
    "name":"我的公司",
    "parentId":0,
    "updateTime":"2022-04-23",
    "users":[
        {
            "departmentId":1,
            "id":1,
            "name":"Jone"
        },
        {
            "departmentId":1,
            "id":2,
            "name":"Jack"
        }
    ]
}

你可能感兴趣的:(算法学习,深度优先,学习,宽度优先,java,sql)