广度优先算法和深度优先算法-树形结构(层级结构)-Java

广度优先算法和深度优先算法-树形结构(层级结构)-Java

目录




内容

1、前言

在日常应用中,我们经常需要封装树形结构数据,比如省市县3级数据,目录菜单按钮权限数据,分销商数据等等。虽然这些目标数据没有树形结构中的根,但是在生成的时候我们可以构建一个虚拟的顶级根节点,下面结合具体应用,分别使用深度优先和广度优先封装层级数据。

层级数据封装实现:

  1. 递归
  2. 栈+深度优先算法
  3. 队列+广度优先算法

下面我们以常用的权限控制系统中权限数据的封装讲解,权限控制系统相关详细内容参考我的其他博文或者自行百度。

前端效果图:
在这里插入图片描述

权限数据示例:

[
		{
			"id": "1346425092905299974",
			"name": "员工管理",
			"url": "/user",
			"type": 0,
			"orderNum": 1,
			"pid": "0",
			"children": [
				{
					"id": "1346425092905299975",
					"name": "列表",
					"url": "/list",
					"type": 1,
					"orderNum": 1,
					"pid": "1346425092905299974",
					"children": [
						{
							"id": "1346425092905299976",
							"name": "修改",
							"url": "",
							"type": 2,
							"orderNum": 1,
							"pid": "1346425092905299975"
						},
						{
							"id": "1346425092905299977",
							"name": "分配角色",
							"type": 2,
							"orderNum": 2,
							"pid": "1346425092905299975"
						}
					]
				}
			]
		},
		{
			"id": "1346425092905299978",
			"name": "公司设置",
			"url": "/companySetup",
			"type": 0,
			"orderNum": 2,
			"pid": "0",
			"children": [
				{
					"id": "1346425092905299979",
					"name": "列表",
					"url": "/list",
					"type": 1,
					"orderNum": 1,
					"pid": "1346425092905299978",
					"children": [
						{
							"id": "1346425092905299980",
							"name": "添加",
							"perms": "sys:companySetup:save",
							"type": 2,
							"orderNum": 1,
							"pid": "1346425092905299979"
						},
						{
							"id": "1346425092905299981",
							"name": "修改",
							"perms": "sys:companySetup:update",
							"type": 2,
							"orderNum": 2,
							"pid": "1346425092905299979"
						},
						{
							"id": "1346425092905299982",
							"name": "删除",
							"perms": "sys:companySetup:delete",
							"type": 2,
							"orderNum": 3,
							"pid": "1346425092905299979"
						},
						{
							"id": "1346425092905299983",
							"name": "分配权限",
							"perms": "sys:companySetup:assignPerms",
							"type": 2,
							"orderNum": 4,
							"pid": "1346425092905299979"
						}
					]
				}
			]
		},
		{
			"id": "1346425092905299984",
			"name": "权限设置",
			"url": "/perm",
			"type": 0,
			"orderNum": 3,
			"pid": "0"
		}
	]

具体生成代码如下:

2、递归

  • 步骤:
  1. 查询全部权限列表
  2. 构建虚拟顶级根权限,id为“0”
  3. 查询子级权限,条件权限列表中权限pid为“0”的权限,同时在权限列表中移除(提高效率,做为递归结束条件)
  4. 构建子级权限列表存放 步骤3 符合条件的权限
  5. 如果子级权限列表不为空,设置子级权限列表为父级权限的子权限列表children
  6. 遍历子级权限列表,重复步骤3-6
  7. 直到权限列表为空,结束递归
  • 代码如下:

       public List<PermissionEntity> queryLevelPermsRecursive(Map<String, Object> params) {
      		Integer type = null;
      		if (params.containsKey("type")){
      			type = (Integer) params.get("type");
      		}
      		List<PermissionEntity> list = new LambdaQueryChainWrapper<>(permissionDao)
      				.le(type != null, PermissionEntity::getType, type)
      				.list();
      		if(CollectionUtils.isEmpty(list)) {
      			return null;
      		}
      		PermissionEntity permissionEntity = new PermissionEntity();
      		permissionEntity.setPid("f");
      		permissionEntity.setId("0");
      		levelPermsRecursive(permissionEntity, list);
      		return permissionEntity.getChildren();
      	}
      	private void levelPermsRecursive(PermissionEntity vo, List<PermissionEntity> districtEntityVoList) {
      		// 1、如果是指定层级或者集合为空结束递归调用
      		if ( districtEntityVoList == null || districtEntityVoList.size() == 0) {
      			return;
      		}
      		// 2、遍历查找集合中pid和目标id相同的元素
      		List<PermissionEntity> children = new LinkedList<>();
      		Iterator<PermissionEntity> iterator = districtEntityVoList.iterator();
      		while (iterator.hasNext()) {
      			PermissionEntity next = iterator.next();
      			if (StringUtils.equals(vo.getId(), next.getPid())) {
      				// 2.1、找到元素,加入子集合;同时删除在原有集合中移除
      				children.add(next);
      				iterator.remove();
      			}
      		}
      		if (children.size() > 0) {
      			// 3、子集合不为空,递归封装层级数据
      			vo.setChildren(children);
      			for (PermissionEntity child: children) {
      				levelPermsRecursive(child, districtEntityVoList);
      			}
      		}
      	}
    
  • 注意事项:

    • 递归一定要有结束条件,这里找到符合条件的权限对象,原权限列表中移除,最后权限列表会为空
    • 想要权限列表最终 为空,权限表设计一定要合理,除顶层权限pid为某个指定值(这里设置"0")外,其他权限pid一定是表中个权限的id,既权限一定要有父级权限

3、栈+深度优先算法

  • 步骤
  1. 查询全部权限列表
  2. 构建虚拟顶级根权限,id为“0”
  3. 构建栈结构,这里使用Java中LinkedList的类型,把根权限放入栈
  4. 取出队列中最上面的元素(弹栈)
  5. 查询子级权限,条件权限列表中权限pid为“0”的权限,同时在权限列表中移除(提高效率,做为结束条件)
  6. 构建子级权限列表存放 步骤3 符合条件的权限
  7. 如果子级权限列表不为空,设置子级权限列表为父级权限的子权限列表children
  8. 同时吧步骤7中自己权限列表压入栈中(压栈)
  9. 重复步骤4-8,直到队列为空或者权限列表为空
  • 代码实现:

     public List<PermissionEntity> levelPermsStack(Map<String, Object> params) {
     //        long start = System.currentTimeMillis();
     		Integer type = null;
     		if (params.containsKey("type")){
     			type = (Integer) params.get("type");
     		}
     		List<PermissionEntity> list = new LambdaQueryChainWrapper<>(permissionDao)
     				.le(type != null, PermissionEntity::getType, type)
     				.list();
     		if(CollectionUtils.isEmpty(list)) {
     			return null;
     		}
     		PermissionEntity permissionEntity = new PermissionEntity();
     		permissionEntity.setPid("f");
     		permissionEntity.setId("0");
     		LinkedList<PermissionEntity> stack = new LinkedList<>();
     		stack.addLast(permissionEntity);
    
     		while (!stack.isEmpty() && list.size() > 0) {
     			PermissionEntity m = stack.pop();
     			List<PermissionEntity> children = new LinkedList<>();
     			Iterator<PermissionEntity> iterator = list.iterator();
     			while (iterator.hasNext()) {
     				PermissionEntity next = iterator.next();
     				if (StringUtils.equals(m.getId(), next.getPid())) {
     					// 2.1、找到元素,加入子集合;同时删除在原有集合中移除
     //                    next.setParent(m);
     					children.add(next);
     					iterator.remove();
     				}
     			}
     			if (children.size() > 0) {
     				m.setChildren(children);
     				stack.addAll(children);
     			}
     		}
     //        long end = System.currentTimeMillis();
     //        System.out.println(end - start);
     		return permissionEntity.getChildren();
     	}
    
  • 知识点:

    • LinedList 有pop取最上面的元素,addAll或者push 吧最新的元素压入栈(压栈),符合先入后出(FILO)或者后入先出,可以作为栈使用
    • 深度优先既先获取子级,在获取子子级,以此类推,优先深度(纵向)

4、队列+广度优先算法

  • 步骤
  1. 查询全部权限列表
  2. 构建虚拟顶级根权限,id为“0”
  3. 构建队列,这里使用Java中LinkedList的类型,把根权限放入队列
  4. 取出队列中最前面的元素
  5. 查询子级权限,条件权限列表中权限pid为“0”的权限,同时在权限列表中移除(提高效率,做为递归结束条件)
  6. 构建子级权限列表存放 步骤3 符合条件的权限
  7. 如果子级权限列表不为空,设置子级权限列表为父级权限的子权限列表children
  8. 同时吧步骤7中自己权限列表放入队列中
  9. 重复步骤4-8,直到队列为空或者权限列表为空
  • 代码实现:

     public List<PermissionEntity> levelPermsQueue(Map<String, Object> params) {
     //        long start = System.currentTimeMillis();
     		Integer type = null;
     		if (params.containsKey("type")){
     			type = (Integer) params.get("type");
     		}
     		List<PermissionEntity> list = new LambdaQueryChainWrapper<>(permissionDao)
     				.le(type != null, PermissionEntity::getType, type)
     				.list();
     		if(CollectionUtils.isEmpty(list)) {
     			return null;
     		}
     		PermissionEntity pe = new PermissionEntity();
     		pe.setPid("f");
     		pe.setId("0");
     		LinkedList<PermissionEntity> queue = new LinkedList<>();
     		queue.addLast(pe);
    
    
     		while (CollectionUtils.isNotEmpty(queue) && CollectionUtils.isNotEmpty(list)) {
    
     			PermissionEntity p = queue.removeFirst();
    
     			Iterator<PermissionEntity> iterator = list.iterator();
     			LinkedList<PermissionEntity> children = new LinkedList<>();
     			while (iterator.hasNext()) {
     				PermissionEntity next = iterator.next();
     				if (StringUtils.equals(p.getId(), next.getPid())){
     					children.add(next);
     					iterator.remove();
    
     				}
     			}
     			if (children.size() > 0) {
     				p.setChildren(children);
     				queue.addAll(children);
     			}
     		}
     //        long end = System.currentTimeMillis();
     //        System.out.println(end - start);
     		return pe.getChildren();
     	}
    
  • 知识点:

    • LinedList 有removeFirst取最前面的元素,addLast 吧最新的元素放入末尾,符合先入先出(FIFO),可以作为队列使用
    • 广度优先既先获取所有的子级,在获取所有的子子级,以此类推,优先广度(横向)

5、比较

  • 递归:适合数据量少且层级不深
  • 深度优先和广度优先看具体需求,具有相同的时间复杂度。

关于递归、树形结构、广度优先算法、深度优先算法更详细的知识,兴趣的小伙伴自行查阅相关文档。

后记

  欢迎交流,本人QQ:806797785

后端JAVA源代码地址:https://gitee.com/gaogzhen/ihrm-parent    // 后端项目
前端项目源代码地址:https://gitee.com/gaogzhen/ihrm-vue    // 前端后台管理系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值