菜单树的三种实现形式(递归,for, 队列)

菜单树在软件开发中经常用于展示层级结构的数据,例如网站导航菜单或文件系统。在实际开发中,我们常常需要考虑不同的数据结构和算法来实现这种层级关系的展示。本文将介绍菜单树的三种常见实现方式:递归、for循环和队列,帮助读者更好地理解和选择合适的方法。

 准备

示例数据库

create table menu
(
    id        int auto_increment
        primary key,
    name      varchar(100) not null,
    parent_id int          null
);

create index parent_id
    on menu (parent_id);
 

Mapper

@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
    @Select("SELECT id, name, parent_id FROM menu WHERE parent_id = #{parentId}")
    List<Menu> findMenusByParentId(@Param("parentId") Integer parentId);

}

Service

public interface MenuService {
    List<Menu> findMenusByParentId(Integer parentId);
}

 Impl

@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
    @Autowired
    private MenuMapper menuMapper;
    @Override
    public List<Menu> findMenusByParentId(Integer parentId) {
        return menuMapper.findMenusByParentId(parentId);
    }
}

1. 递归实现

@RestController
@RequestMapping("/menus")
public class MenuController {

    @Autowired
    private MenuService menuService;

    // 返回根菜单列表及其子菜单
    @GetMapping("/tree")
    public List<Menu> getMenuTree(Integer parentId) {
        List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单
        buildMenuTree(rootMenus); // 构建树形结构
        return rootMenus;
    }

    // 递归方法,构建菜单树
    private void buildMenuTree(List<Menu> menus) {
        for (Menu menu : menus) {
            List<Menu> children = menuService.findMenusByParentId(menu.getId());
            if (!children.isEmpty()) {
                menu.setChildren(children); // 设置子菜单列表
                buildMenuTree(children); // 递归构建子树
            }
        }
    }
}

2.For实现

@RestController
@RequestMapping("/treemenus")
public class MenuNotRecursionUseForController {

    @Resource
    private MenuService menuService;
    @GetMapping("/tree")
    public List<Menu> getMenuTree(Integer parentId) {
        List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单列表

        // 创建一个 Map 用于存放每个菜单的 ID 和对应的菜单对象
        Map<Integer, Menu> menuMap = new HashMap<>();
        for (Menu menu : rootMenus) {
            menu.setChildren(new ArrayList<>()); // 初始化子菜单列表
            menuMap.put(menu.getId(), menu); // 将菜单对象放入 map 中,以便后续快速访问
        }

        // 遍历第二层菜单,逐层构建树形结构
        for (Menu menu : rootMenus) {
            // 查询当前菜单的所有子菜单(第二层菜单)
            List<Menu> children = menuService.findMenusByParentId(menu.getId());
            for (Menu child : children) {
                // 将第二层菜单添加到父菜单的子菜单列表中
                Menu parentMenu = menuMap.get(child.getParentId());
                if (parentMenu != null) {
                    parentMenu.getChildren().add(child);

                    // 查询当前第二层菜单的所有子菜单(第三层菜单)
                    List<Menu> grandchildren = menuService.findMenusByParentId(child.getId());
                    if (child.getChildren() == null) {
                        child.setChildren(new ArrayList<>()); // 初始化第三层子菜单列表
                    }
                    for (Menu grandchild : grandchildren) {
                        // 将第三层菜单添加到当前第二层菜单的子菜单列表中
                        child.getChildren().add(grandchild);
                    }
                }
            }
        }

        // 返回构建好的根菜单列表
        return rootMenus;
    }


}

3. 队列实现

@RestController
@RequestMapping("/treemenustwo")
public class MenuNotRecursionUseQueueController {
        @Autowired
        private MenuService menuService;

        @GetMapping("/tree")
        public List<Menu> getMenuTree(Integer parentId) {
            List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单列表

            // 使用队列来进行广度优先搜索
            Queue<Menu> queue = new LinkedList<>(rootMenus);

            while (!queue.isEmpty()) {
                Menu currentMenu = queue.poll(); // 出队当前菜单

                // 查询当前菜单的所有子菜单
                List<Menu> children = menuService.findMenusByParentId(currentMenu.getId());

                // 将子菜单加入当前菜单的 children 列表中
                currentMenu.setChildren(children);

                // 将子菜单加入队列,以便后续处理其子菜单
                queue.addAll(children);
            }

            return rootMenus; // 返回构建好的树形结构
        }

    }

PostMan测试

请求: http://localhost:8099/treemenus/tree?parentId=0

[
    {
        "id": 1,
        "name": "菜单1",
        "parentId": 0,
        "children": [
            {
                "id": 2,
                "name": "子菜单1-1",
                "parentId": 1,
                "children": [
                    {
                        "id": 4,
                        "name": "子菜单1-1-1",
                        "parentId": 2,
                        "children": null
                    }
                ]
            },
            {
                "id": 3,
                "name": "子菜单1-2",
                "parentId": 1,
                "children": []
            }
        ]
    },
    {
        "id": 5,
        "name": "菜单2",
        "parentId": 0,
        "children": [
            {
                "id": 6,
                "name": "子菜单2-1",
                "parentId": 5,
                "children": [
                    {
                        "id": 8,
                        "name": "子菜单2-1-1",
                        "parentId": 6,
                        "children": null
                    }
                ]
            },
            {
                "id": 7,
                "name": "子菜单2-2",
                "parentId": 5,
                "children": []
            }
        ]
    }
]

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值