目录树的构造

article/2025/7/30 14:19:37

概述

”树“在计算机的世界里是一个基本的数据结构,很多地方都能看到”树“的身影。最常见的应该是在各种软件和网页的菜单栏中,多层级的折叠其实就是以一棵树的形式进行展现的,如下图所示:

在树的层级和标签类别比较少,并且标签类别比较固定的时候,有时候是直接由前端同学硬编码实现。如果想要修改某个类别,则需要改动前端代码;如果想要增加类别,则需要大量改动前端代码。这种方式显然是不合理的,因此我们常常会从数据库中读取整个菜单的数据,并加以处理进行展现,再实现一些简单的接口,对数据库的数据进行增删改查,实现对菜单中类别树的动态控制。本文想要讨论的问题就是,为了实现动态加载网页中的树形结构,数据库里的数据应该如何设计和存储,又应该在返回前端之前进行怎样的数据处理呢?本文将以一个简单的层级结构为例,对这个问题进行探讨:

└── root├── level-1├── level-2│   ├── level-2.1│   └── level-2.2│       ├── level-2.2.1│       └── level-2.2.2└── level-3└── level-3.1

数据库存储

在数据结构中,如果想要在一棵树中定位某一个节点,我们需要知道它在某种特定遍历方式(层序遍历、前中后序遍历)中是第几个节点。在数据库中,我们需要思考的是存储什么信息,能够让整棵类别树的增删改查变得容易,因此我们可以从增删改查操作中思考需要存储哪些信息。
增加:增加一个节点时,首先需要知道当前节点应该挂在哪个父节点下面,因此父节点id是必要的。同时这里有一个注意点,第一层级的节点是没有父节点的,所以我们需要虚拟出一个值作为根节点,也就是第一层级所有节点的父节点。
删除:删除只需要根据节点id就可以直接进行删除。
修改:修改也只需要根据节点id就可以进行更新。
查询:查询分两部分。第一部分是查询某个节点的子节点,也就是展开操作,可以直接查询父节点id为xxx的节点,因此父节点id是必要的;第二部分是查询某个层级的所有节点,此时光有父节点id是不够的,因此必须引入一个层级id的字段。
综上,我们只需要在数据库中存储父类别的id和分类层级,即可以完成对整棵分类树的所有操作。
至于为什么是存储父类别id,而不是子类别id。可以思考一下,因为父和子之间的关系其实是一对多的关系,所以如果存储子类别id,每一个节点的信息是需要存储多条记录的,并且数据存在大量冗余;如果存储的是父类别id,子和父之间的关系是多对一,因此每一节点只需要存储一条记录,即可表征父子关系,且整个数据库不存在数据冗余。
经过一通分析,最终我们定义出如下数据库:

其中parent_id代表父类别的id,第一层级的父类别id为0;category_level是代表当前类别所在层级,从1开始计数。
建表语句如下:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for category_info
-- ----------------------------
DROP TABLE IF EXISTS `category_info`;
CREATE TABLE `category_info` (`id` int NOT NULL AUTO_INCREMENT,`category_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '分类名称',`parent_id` int NOT NULL DEFAULT '0' COMMENT '当前分类父类的id,如果无父类则为0',`category_level` int NOT NULL COMMENT '分类层级',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COMMENT='分类信息表';-- ----------------------------
-- Records of category_info
-- ----------------------------
BEGIN;
INSERT INTO `category_info` VALUES (1, 'level-1', 0, 1);
INSERT INTO `category_info` VALUES (2, 'level-2', 0, 1);
INSERT INTO `category_info` VALUES (3, 'level-3', 0, 1);
INSERT INTO `category_info` VALUES (4, 'level-2.1', 2, 2);
INSERT INTO `category_info` VALUES (5, 'level-2.2', 2, 2);
INSERT INTO `category_info` VALUES (6, 'level-3.1', 3, 2);
INSERT INTO `category_info` VALUES (7, 'level-2.2.1', 5, 3);
INSERT INTO `category_info` VALUES (8, 'level-2.2.2', 5, 3);
COMMIT;SET FOREIGN_KEY_CHECKS = 1;

树形结构生成

数据库的数据有了,那么我们该怎么返回什么样的数据给前端进行渲染呢?众所周知,json是一种常见的,用于前后端交互的数据格式,因此我们可以构造出一棵json树,交由前端同学进行数据绑定。代码如下:

# -*- coding: utf-8 -*-
import json# categories_list从db获取,此处是为了方便展示
category_list = [{'id': 1, 'category_name': 'level-1', 'parent_id': 0, 'category_level': 1},{'id': 2, 'category_name': 'level-2', 'parent_id': 0, 'category_level': 1},{'id': 3, 'category_name': 'level-3', 'parent_id': 0, 'category_level': 1},{'id': 4, 'category_name': 'level-2.1', 'parent_id': 2, 'category_level': 2},{'id': 5, 'category_name': 'level-2.2', 'parent_id': 2, 'category_level': 2},{'id': 6, 'category_name': 'level-3.1', 'parent_id': 3, 'category_level': 2},{'id': 7, 'category_name': 'level-2.2.1', 'parent_id': 5, 'category_level': 3},{'id': 8, 'category_name': 'level-2.2.2', 'parent_id': 6, 'category_level': 3}]def get_categories_tree():# 从db读取category数据# categories_list = db.get_all_categories()# 构建map: category_id -> category对象categories_map = {}for category in category_list:category["sub_categories"] = []categories_map[category["id"]] = category# 将父子类别连接形成树结构categories_tree = []for category in category_list:# 第一层级类别直接加入数组if category["parent_id"] == 0 and category["category_level"] == 1:categories_tree.append(category)# 如果是其它层级的类别,则将当前类别添加到父类别中的sub_categories中else:categories_map[category["parent_id"]]["sub_categories"].append(category)return categories_treeif __name__ == '__main__':category_tree = get_categories_tree()print(json.dumps(category_tree))

构造树的整体思想是:先建立好id->对象的映射关系,并增加用于存储子类别对象的数组;然后遍历数据,将当前节点添加到父类别的子类别数组中。其实就是我们在数据库存储阶段讨论到的,为何不存储子类别id而是父类别id。从存储角度而言,存储父类别id是更优的方案;但是从可视化角度而言,给每个类别加上子类别数组更容易渲染。因此我们通过以上转化,达成了存储和可视化两者皆优的目标。

最后我们查看一下输出的json树是长什么样的:

符合预期,任务完成~

更新

有序目录树的构造一文基于此问题进行了优化总结。


http://chatgpt.dhexx.cn/article/coXb868w.shtml

相关文章

兴安雪学运维之:目录树详解

极北之地,兴安之雪,老骥伏枥转战Linux运维,最近根据授课和大略看了FHS3.0,对Linux的目录有了初步的了解,怕人老忘性差,作以记录。 一、目录结构图 Linux的目录是一个倒置的树状结构,最顶层的目录…

【数据结构】B/B-树(目录树)

引言 关于B树的性质 一、B树的结构 二、B树的实现 #include<iostream> using namespace std; #if 1 //5分支Btree #define M 5 //奇数 #define MAXSIZE (M-1) //最多元素个数 #define MINSIZE (M/2) //最少元素个数 //B树 class Btree { public://关键码类型using KeyTy…

【Mybatis】Mybatis将String类型的0存到数据库中的number类型字段中,变成了空;

一、问题 Mybatis将String类型的0存到数据库中的number类型字段中&#xff0c;变成了空&#xff1b; 二、分析 自己写了一个自动写代码的脚本&#xff0c;带入springBatch后&#xff0c;读取文件时&#xff0c;少了序列号0-9的记录&#xff08;10笔一提交&#xff09;&#…

java取数据库number转String

2019独角兽企业重金招聘Python工程师标准>>> BigDecimal bigDecimal(BigDecimal)value; Long lbigDecimal.longValue(); String sbigDecimal.toString(); 转载于:https://my.oschina.net/u/2285090/blog/514110

字符串存入数据库date类型字段

有时候为了计算方便等原因需要将时间以date格式存入数据库&#xff0c;但是前台传过来的数据类型都是字符串&#xff0c;如果将字符串直接插入date类型的字段中会抛&#xff1a;ORA-01861: 文字与格式字符串不匹配。 前台页面有一个表单&#xff0c;如下所示&#xff1a; <…

2018年SCI论文--整合GEO数据挖掘完整复现 八 :STRING数据库构建蛋白质相互作用网络(PPI),cytoscape软件筛选hub基因

文章目录 论文地址STRING数据库PPI网络构建输入差异基因listPPI图保存结果 cytoscape软件筛选hub基因、功能模块输入“string_interactions”文件用cytohHubba插件&#xff0c;筛选top10 Hub基因生存分析用MCODE插件&#xff0c;筛选功能模块 论文地址 STRING数据库 PPI网络构…

string数据库使用和实践第三部分数据处理 流程-参数--后续分析

流程 1.首先要获取蛋白质列表&#xff08;单个/多个&#xff09; 格式&#xff1a;蛋白名称为一个占一行&#xff0c;或者氨基酸序列的通用格式。ID类别&#xff1a;可以为一种&#xff0c;可以为多种混合 2.在对应的数据框中输入蛋白质列表或者上传列表文件后&#xff0c;选择…

spring boot String类型json 存入数据库

效果图&#xff1a; 如图&#xff0c;string类型的json字符串&#xff0c;存入数据库&#xff0c;主要就是解析成map&#xff0c;遍历插入&#xff0c;不多说上干货&#xff1a; PostMapping(value "/currentConfig")public String saveSvConfig(RequestParam(&quo…

jpa @Convert list转String存入数据库

1.问题 list通过jpa直接存入数据库会报错这里需要进行转换 2.代码 1.dto对象 Entity Data Accessors(chain true) public class GameMatch {/*** 主键*/IdGeneratedValue(strategy GenerationType.IDENTITY)private Integer id;/*** 游戏id*/private Integer gameId;/*** …

string数据库使用和实践的第二部分网页展示http://string-db.org/

主页 protein-mode 该模式中&#xff0c;string数据库能够预测到特定物种中的某一个蛋白质的相互作用关系 通过该模式可以获取最多的特异性&#xff0c;但是覆盖面就会较小一些&#xff0c;原因是该模式中&#xff0c;string不会准确的去获取其他物种的直系同源物&#xff0c;取…

蛋白相互作用数据库,STRING使用指南

对于基因组数据分析而言的话&#xff0c;我们能用到网络分析的就是蛋白相互作用分析(protein-protein ineraction, PPI)分析了。 蛋白相互作用分析的数据库有很多&#xff0c;至于为什么选择STRING&#xff0c;还是在于其强大的可视化&#xff0c;以及自定义功能。这样我们可以…

五大常见的数据类型之 String

前言 我们都知道 Redis 提供了丰富的数据类型&#xff0c;常见的有五种&#xff1a;String&#xff08;字符串&#xff09;&#xff0c;Hash&#xff08;哈希&#xff09;&#xff0c;List&#xff08;列表&#xff09;&#xff0c;Set&#xff08;集合&#xff09;、Zset&…

数据库String字符串

char(n) varchar(n) tinyint tinytext text mediumtext longtextchar(0-255)varchar(0-21844) // 63533定长字符串 char()变长字符串 varchar(n) tinytext text mediumtext longtext(4GBtext)-- char varchar tinytext text mediumtext longtext -- 23767-1 21845-1 16383-1…

STRING:蛋白质相互作用(PPI网络)数据库简介

欢迎关注微信公众号《生信修炼手册》! 研究蛋白之间的相互作用网络&#xff0c;有助于挖掘核心的调控基因&#xff0c;目前已经有很多的蛋白质相互作用的数据库&#xff0c;而string绝对是其中覆盖的物种最多&#xff0c;相互作用信息做大的一个&#xff0c;网址如下 https://…

string数据库使用和实践第一部分string数据库介绍

背景 为什么要寻找蛋白质互做关系&#xff1f; 因为只有正确地发现和注释细胞中的所有功能性的相互作用关系&#xff0c;才能对细胞的功能进行系统层面的学习和理解。 大家在收集和展现蛋白质相互作用的信息上&#xff0c;一直在努力地跟上相互作用关系探索的步伐 近年来&#…

解决虚拟机桥接模式无法上网的问题

1.检查IP地址以及网关等信息是否正确 vim /etc/network/interfaces这里设置的是静态ip, auto lo iface lo inet loopback auto eth0 iface eth0 inet stastic address 192.168.43.40 # 设置IP netmask 255.255.255.0 # 设置子网掩码 gateway 192.168.43.19 # 设置网关 首先…

VM虚拟机桥接模式无法联网解决办法

1.桥接模式的意义&#xff1a; 桥接模式----使虚拟机客户机可以和主机在同一网段&#xff0c;这样&#xff0c;和主机同局域网内的其他主机就也可以ping到虚拟机了 2.问题描述&#xff1a; 1.主机和虚拟机客户机相互之间ping不通 2.将虚拟机改为网络模式为NAT模式自动获取I…

虚拟机配置桥接模式(bridge)

使用的Vmware虚拟机软件 需要使用bridge&#xff0c;桥接模式 首先&#xff0c;需要给主机电脑设置IP&#xff0c; 看主机使用的是有线还是无线&#xff0c;使用哪个就设置那个&#xff0c;可以先通过ipconfig/all来查看ip&#xff0c;然后根据实际信息配置网络适配器 手动配置…

虚拟机桥接模式下的网络设置

虚拟机共有三种网络模式&#xff0c;每种网络模式具体的原理参考&#xff1a; Vmware虚拟机三种网络模式详解 - 星朝 - 博客园原文来自http://note.youdao.com/share/web/file.html?id236896997b6ffbaa8e0d92eacd13abbf&typenote 我怕链https://www.cnblogs.com/jpfss/p/…

Virtualbox虚拟机桥接模式配置

VirtualBox提供了多种网络连接方式&#xff0c;不同的网络连接方式决定了虚拟机是否可以联网&#xff0c;以及是否可以和宿主机互相ping通 亲测之后总结一些在配置NAT模式和仅主机模式所踩的坑&#xff0c;最后选择了桥接模式 NAT模式&#xff1a;宿主机ping不通虚拟机&#…