精华内容
下载资源
问答
  • 树形结构

    千次阅读 2018-08-16 08:24:22
    树形结构 ———其实这是很简单又很难得一些东西 1 定义 树状图是一种数据结构,它是由n(n>=1)n(n>=1)n (n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵...

    树形结构

    ———其实这是很简单又很难得一些东西

    1 定义

    树状图是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
    树(tree)是包含n(n>0)个结点的有穷集,其中:

    1)每个元素称为结点(node)

    2)有一个特定的结点被称为根结点或树根(root)

    3)除根结点之外的其余数据元素被分为m(m0)个互不相交的集合T1,T2,Tm1,其中每一个集合Ti(1<=i<=m)本身也是一棵树,被称作原树的子树(subtree)

    2 特性

    树要满足一些特性:

    1)树上两点间有且只有一条路径(不走重复路径)

    2)每个节点有零个或多个子节点

    3)没有父节点的节点称为根节点

    4)每一个非根节点有且只有一个父节点

    5)除了根节点外,每个子节点可以分为多个不相交的子树
    树1

    3 有关术语&常用语(看看就好)

    1)节点的度:一个节点含有的子树的个数称为该节点的度;

    2)叶节点或终端节点:度为0的节点称为叶节点;

    3)非终端节点或分支节点:度不为0的节点;

    4)双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;

    5)孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;

    6)兄弟节点:具有相同父节点的节点互称为兄弟节点;

    7)树的度:一棵树中,最大的节点的度称为树的度;

    8)节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

    9)树的高度或深度:树中节点的最大层次;

    10)堂兄弟节点:双亲在同一层的节点互为堂兄弟;

    11)节点的祖先:从根到该节点所经分支上的所有节点;

    12)子孙:以某节点为根的子树中任一节点都称为该节点的子孙。

    13)森林:由m(m>=0)棵互不相交的树的集合称为森林;

    4 一些种类的树

    1)二叉树

    2)线段树

    线段树1

    3)平衡树(如splay,treap,替罪羊树等)

    平衡树1

    4)trie字符树

    trie1

    5)……

    5 有关算法&数据结构

    传统“树论”:

    1)各种维护数据结构,如[NOI2005]维护数列

    2)树链剖分,如树链剖分

    3)树上乱搞(LCA,dp,倍增等),如[LNOI2014]LCA

    4)……

    展开全文
  • LayUI—tree树形结构的使用

    万次阅读 多人点赞 2019-09-01 21:46:46
    树形结构在实际开发中很长用到,比如部门管理,权限菜单等。因为用树形结构来展示会显的很清晰明了。最近写了一个个人博客小项目中用到了LayUI的树形结构,之后写了一个小案例整理一下。 官网地址:...

    树形结构在实际开发中很长用到,比如部门管理,权限菜单等。因为用树形结构来展示会显的很清晰明了。最近写了一个个人博客小项目中用到了LayUI的树形结构,之后写了一个小案例整理一下。

    官网地址:https://www.layui.com/v1/doc/modules/tree.html

    先看一下显示的效果图。

    点击节点右面会显示对应部门的详情信息,可以修改。可以自定义按钮添加部门,也可以直接用自带的方法对部门进行新增,修改和删除。可以获取选中的节点,根据项目需求(有的需要选中保存)。

    先需要引入LayUI的样式文件JS和CSS。

    案例对应的实体类Dept。

    @Entity
    public class Dept {
        private Integer id;
        private String name;    //部门名称
        private String deptName;    //部门负责人
        private String phone;   //电话号
        private String number;  //编号
        private double idx;     //排序
        @JsonIgnore
        private Dept parent;
        @JsonIgnore
        private List<Dept> children = new ArrayList<>();
    
        @Id
        @GeneratedValue
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public double getIdx() {
            return idx;
        }
    
        public void setIdx(double idx) {
            this.idx = idx;
        }
    
        @ManyToOne
        @CreatedBy
        public Dept getParent() {
            return parent;
        }
    
        public void setParent(Dept parent) {
            this.parent = parent;
        }
    
        @OneToMany(cascade=CascadeType.ALL,mappedBy="parent")
        @OrderBy(value="idx")
        public List<Dept> getChildren() {
            return children;
        }
    
        public void setChildren(List<Dept> children) {
            this.children = children;
        }
    
        public Dept(Integer id, String name, String deptName, String phone, String number, double idx, Dept parent, List<Dept> children) {
            this.id = id;
            this.name = name;
            this.deptName = deptName;
            this.phone = phone;
            this.number = number;
            this.idx = idx;
            this.parent = parent;
            this.children = children;
        }
    
        public Dept(Integer id) {
            this.id = id;
        }
    
        public Dept() {
        }
    }

    显示LayUI树形菜单,只需要一个标签容器即可。

    <div id="dept_tree">
    </div>

    在案例中还有一些其他样式,比如右边的详情信息,新增按钮等。完整代码如下。

    <style type="text/css">
        #dept_main, #dept_particulars{
            width: 48.5%;
            display: inline-block;
            vertical-align: top;
            padding: 20px;
            background: white;
            box-sizing: border-box;
        }
        #dept_tree{
            margin-top: 20px;
        }
    </style>
    
    <div id="dept_main" style="margin-right: 2%;">
        <fieldset class="layui-elem-field layui-field-title">
            <legend>所有部门</legend>
        </fieldset>
        <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="addDept"><i class="layui-icon">&#xe654;</i>添加部门</button>
        <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="gain">获取选中节点</button>
        <div id="dept_tree">
    
        </div>
    </div>
    <div id="dept_particulars">
        <fieldset class="layui-elem-field layui-field-title">
            <legend>部门详情</legend>
        </fieldset>
        <div id="dept_home">
            <div class="layui-tree-emptyText">无数据</div>
        </div>
    </div>

    JS请求数据渲染页面代码,data为请求数据源,当时直接放入的请求链接,好像不行,所以之后才写了一个方法去请求数据源。

    layui.use(['tree', 'util'], function() {
        var tree = layui.tree;
        var util = layui.util;
        tree.render({
            elem: '#dept_tree',
            data: getData(),
            id: 'treeId',
            showCheckbox: true,     //是否显示复选框
            onlyIconControl: true
        });
    });
    
    function getData(){
        var data = [];
        $.ajax({
            url: "dept/treeload",    //后台数据请求地址
            type: "post",
            async:false,
            success: function(resut){
                data = resut;
            }
        });
        return data;
    }

     tree 组件提供的有以下基础参数,可根据需要进行相应的设置。

    参数选项 说明 类型 示例值
    elem 指向容器选择器 String/Object -
    data 数据源 Array -
    id 设定实例唯一索引,用于基础方法传参使用。 String -
    showCheckbox 是否显示复选框 Boolean false
    edit 是否开启节点的操作图标。默认 false。
    • 若为 true,则默认显示“改删”图标
    • 若为 数组,则可自由配置操作图标的显示状态和顺序,目前支持的操作图标有:addupdatedel,如: 
      edit: ['add', 'update', 'del']
    Boolean/Array ['update', 'del']
    accordion 是否开启手风琴模式,默认 false Boolean false
    onlyIconControl 是否仅允许节点左侧图标控制展开收缩。默认 false(即点击节点本身也可控制)。若为 true,则只能通过节点左侧图标来展开收缩 Boolean false
    isJump 是否允许点击节点时弹出新窗口跳转。默认 false,若开启,需在节点数据中设定 link 参数(值为 url 格式) Boolean false
    showLine 是否开启连接线。默认 true,若设为 false,则节点左侧出现三角图标。 Boolean true
    text 自定义各类默认文本,目前支持以下设定:

    text: {

         defaultNodeName: '未命名' //节点默认名称

         ,none: '无数据' //数据为空时的提示文本

    }

    Object -

    因为tree指定了json数据的键名称,所以后台传递过来的数据对应的键名不一样时需要做一下处理,或者实体类中的属性名就和tree的JSON数据的键名称一样。

    键名:

    属性选项 说明 类型 示例值
    title 节点标题 String 未命名
    id 节点唯一索引,用于对指定节点进行各类操作 String/Number 任意唯一的字符或数字
    children 子节点。支持设定选项同父节点 Array [{title: '子节点1', id: '111'}]
    href 点击节点弹出新窗口对应的 url。需开启 isJump 参数 String 任意 URL
    spread 节点是否初始展开,默认 false Boolean true
    checked 节点是否初始为选中状态(如果开启复选框的话),默认 false Boolean true
    disabled 节点是否为禁用状态。默认 false Boolean false

    后台请求数据的方法。

    @RequestMapping(value = "/treeload")
    @ResponseBody
    public Object treeload(){
        Sort sort = Sort.by("idx");
        List<Dept> dpet = deptService.findByParentIsNull(sort);    //查找所有菜单
        List<HashMap<String, Object>> result = new ArrayList<>();    //定义一个map处理json键名问题
        return fun(dpet, result);
    }
    
    private Object fun(List<Dept> dpet, List<HashMap<String, Object>> result) {
        for(Dept d : dpet){
            HashMap<String, Object> map = new HashMap<>();
            map.put("id", d.getId());
            map.put("title", d.getName());
            map.put("spread", true);      //设置是否展开
            List<HashMap<String, Object>> result1 = new ArrayList<>();
            List<Dept> children = d.getChildren();    //下级菜单
            //这里可以根据自己需求判断节点默认选中
            /*if(m.getParent() != null || m.getChildren().size() == 0){
                map.put("checked", true);    //设置为选中状态
            }*/
            map.put("children", fun(children, result1));
            result.add(map);
        }
        return result;
    }

    因为这里新建的实体类字段名和tree指定了json数据的键名称不一样,所以这里用了一个fun递归方法处理的。中间可以根据项目需求,根据条件判断是否需要选中该节点。

    返回的JSON数据格式

    [
    	{
    		"children": [		//子节点
    			{
    				"children": [
    					{
    						"children": [],
    						"id": 30,
    						"title": "测试",
    						"spread": true
    					}, {
    						"children": [],
    						"id": 31,
    						"title": "开发",
    						"spread": true
    					}, {
    						"children": [
    							{
    								"children": [],
    								"id": 36,
    								"title": "测试节点",
    								"spread": true
    							}
    						],
    						"id": 32,
    						"title": "测试",
    						"spread": true
    					}
    				],
    				"id": 2,
    				"title": "技术部",
    				"spread": true
    			}, {
    				"children": [],
    				"id": 19,
    				"title": "财务部",
    				"spread": true
    			}
    		],
    		"id": 1,	//节点id
    		"title": "某某公司",	//节点名称
    		"spread": true
    	}, {
    		"children": [],
    		"id": 33,
    		"title": "测试",
    		"spread": true
    	}
    ]

    设置节点点击回调方法(在加载数据方法tree.render中添加以下代码)。

    click: function (obj) {
        var id = obj.data.id;
        $("#dept_home").load("dept/show?id="+id);
    }

    把请求过来的详情页面load到右边的div中显示。后台请求方法

    @RequestMapping(value = "/show")
    public void show(DeptForm form, ModelMap map) throws InstantiationException, IllegalAccessException {
        Dept model = new Dept();
        Integer id = form.getId();
        Integer parentId = 0;
        if(id!=null) {
            model = deptService.findById(id);
            parentId = model.getParent()==null?0:model.getParent().getId();
        }
        map.put("parentId", parentId);
        map.put("model", model);
    }

    DeptForm类为一个接收类,其中字段和实体类中一样。根据请求传递过来的id,查询这条数据的详细信息,之后把查询的当前部门详情数据及父级节点id(用于下拉树TreeSelect)传递给详情页面。

    show.html详情页面代码。

    <meta charset="UTF-8" />
    <style type="text/css">
        .myData .layui-form-item{
            margin: 20px 100px 10px 45px;
        }
        .myData .layui-form-label{
            width: 85px;
        }
        .layui-input-block {
            margin-left: 120px;
        }
    </style>
    <form class="layui-form myData" action="save" method="post" lay-filter="stuform">
        <input type="hidden" name="id" data-th-value="${model.id}" />
    
        <div class="layui-form-item">
            <label class="layui-form-label">上级部门:</label>
            <div class="layui-input-block">
                <input type="text" name="parentId" id="tree" lay-filter="tree" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">部门名称:</label>
            <div class="layui-input-block">
                <input type="text" name="name" lay-verify="required" th:value="${model.name}" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">部门负责人:</label>
            <div class="layui-input-block">
                <input type="text" name="deptName" th:value="${model.deptName}" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">电话:</label>
            <div class="layui-input-block">
                <input type="text" name="phone" th:value="${model.phone}" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">编号:</label>
            <div class="layui-input-block">
                <input type="text" name="number" th:value="${model.number}" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">排序:</label>
            <div class="layui-input-block">
                <input type="text" name="idx" value="0" th:value="${model.idx}" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label"></label>
            <div class="layui-input-block">
                <button lay-submit class="layui-btn layui-btn-radius layui-btn-normal" lay-filter="btnSub">
                    <i class="layui-icon">&#xe642;</i>修改并保存
                </button>
            </div>
        </div>
    </form>
    
    <script th:inline="javascript">
        layui.use(["treeSelect", "form", "tree"], function () {
            var form = layui.form;
            var tree = layui.tree;
            form.render('select');
            var treeSelect = layui.treeSelect;
            treeSelect.render({
                // 选择器
                elem: '#tree',
                // 数据
                data: 'dept/treeSelect?id='+[[${model.id==null ? 0 : model.id}]],
                // 异步加载方式:get/post,默认get
                type: 'post',
                // 占位符
                placeholder: '上级菜单',
                // 是否开启搜索功能:true/false,默认false
                search: true,
                // 一些可定制的样式
                style: {
                    folder: {
                        enable: true
                    },
                    line: {
                        enable: true
                    }
                },
                // 加载完成后的回调函数
                success: function (d) {
                    // 选中节点,根据id筛选
                    treeSelect.checkNode('tree', [[${model.parent == null? parentId: model.parent.id}]]);
                    treeSelect.refresh('tree');
                }
            });
    
            form.on('submit(btnSub)', function (data) {
                $.post('dept/save', data.field, function (result) {
                    if (result.success) {
                        tree.reload('treeId', {data: getData()});
                    }
                    layer.msg(result.msg, {offset: 'rb'});
                });
                return false;
            });
        });
    </script>
    

    上级部门使用的是LayUI下拉树显示的,下拉树数据请求方法。关于下拉树的使用,可以访问LayUI下拉树TreeSelect的使用

    @RequestMapping(value="/treeSelect")
    @ResponseBody
    public Object treeSelect(Integer id) {
        Sort sort = Sort.by("idx");
        Specification<Dept> spec = buildSpec1();
        List<Dept> list = deptService.findAll(spec,sort);
        return buildTree(list, id);
    }
    
    private Object buildTree(List<Dept> list, Integer id) {
        List<HashMap<String, Object>> result=new ArrayList<>();
        for (Dept dept : list) {
            if(dept.getId() != id) {
                HashMap<String, Object> node=new HashMap<>();
                node.put("id", dept.getId());
                node.put("name",dept.getName());
                node.put("open", false);
                node.put("checked", false);
                if(dept.getChildren().size() != 0) {
                    node.put("children",buildTree(dept.getChildren(), id));
                }
                result.add(node);
            }
        }
    
        return result;
    }
    
    public Specification<Dept> buildSpec1() {
        Specification<Dept> specification = new Specification<Dept>() {
    
            private static final long serialVersionUID = 1L;
    
            @Override
            public Predicate toPredicate(Root<Dept> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                HashSet<Predicate> rules=new HashSet<>();
                Predicate parent = cb.isNull(root.get("parent"));
                rules.add(parent);
                return cb.and(rules.toArray(new Predicate[rules.size()]));
            }
    
        };
        return specification;
    }

    显示的效果。

    上面修改并保存后台方法(因为修改和新增共用的一个方法,用id区分的)。

    @Override
    public Object save(DeptForm form) {
        try {
            Dept model = new Dept();
            Integer id = form.getId();
            if(id != null) {
                model = deptService.findById(id);
            }
            //父级菜单id
            Integer parentId = form.getParentId();
            if(parentId == null) {
                model.setParent(null);
            }else {
                model.setParent(new Dept(parentId));
            }
            BeanUtils.copyProperties(form, model,"id", "parent");
            deptService.save(model);
            return new AjaxResult("数据保存成功!");
        } catch (Exception e) {
            return new AjaxResult(false,"数据保存失败");
        }
    }

    设置节点操作(在加载数据方法tree.render中添加以下代码)。

    edit: ['add', 'update', 'del'], //操作节点的图标
    operate: function(obj){
        var type = obj.type; //得到操作类型:add、edit、del
        var data = obj.data; //得到当前节点的数据
        var elem = obj.elem; //得到当前节点元素
    
        var id = data.id;
        var name = data.title;
        if(type === 'add'){ //增加节点
            $.post("dept/save", {parentId: id, name: "未命名"}, function (result) {
                tree.reload('treeId', {data: getData()});
            })
            //返回 key 值
            return ;
        } else if(type === 'update'){ //修改节点
            $.post("dept/update", {id: id, name: name}, function () {
                tree.reload('treeId', {data: getData()});
            })
        } else if(type === 'del'){ //删除节点
            $.post("dept/delete", {id: id}, function () {
                tree.reload('treeId', {data: getData()});
            });
        };
    }

    其中operate为操作节点回调方法。

    obj.type为操作类型,add为新增,update为修改,edl为删除。obj.data为操作节点后的数据。

    新增节点后,向后台发送请求添加节点,save方法和上面修改方法一样,id为新建节点的父级节点id。

    修改节点,同样,向后台发送修改请求,并传递对象的id,和修改后的数据作为参数。后台响应方法。

    @RequestMapping(value = "/update")
    @ResponseBody
    public Object update(DeptForm form) {
        try {
            Dept model = deptService.findById(form.getId());
            model.setName(form.getName());
            deptService.save(model);
            return new AjaxResult("数据保存成功!");
        } catch (Exception e) {
            return new AjaxResult(false,"数据保存失败");
        }
    }

    删除节点同理,传递删除节点的id。删除请求方法。

    @RequestMapping(value="/delete")
    @ResponseBody
    public Object delete(Integer id) {
    	try {
    		deptService.deleteById(id);
    		return new AjaxResult("数据删除成功");
    	} catch (Exception e) {
    		return new AjaxResult(false,"数据删除失败");
    	}
    }

    使用按钮操作树形菜单。

    现在页面中定义两个按钮,给按钮添加lay-demo=""属性,并设置属性值,JS通过这个属性值,绑定点击事件。

    <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="addDept"><i class="layui-icon">&#xe654;</i>添加部门</button>
    <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="gain">获取选中节点</button>

    绑定添加部门和获取选中节点按钮的点击事件的JS代码。

    util.event('lay-demo', {
        addDept: function(othis){
            $.get('dept/edit', function(data) {
                layer.open({
                    type: 1,
                    title: '新增',
                    area: ['530px'],
                    content: data,
                    btn: ['提交', '退出'],
                    yes: function () {
                    },
                    success: function (layero, index) {
                        layui.use('form', function () {
                            var form = layui.form;
                            layero.addClass('layui-form');
                            var submitBtn = layero.find('.layui-layer-btn0');
                            submitBtn.attr('lay-filter', 'formVerify').attr('lay-submit', '');
                            layero.keydown(function (e) {
                                if (e.keyCode == 13) {
                                    submitBtn.click();
                                }
                            });
    
                            form.on('submit(formVerify)', function (data) {
                                $.post('dept/save', data.field, function (result) {
                                    if (result.success) {
                                        layer.close(index);
                                        tree.reload('treeId', {data: getData()});
                                    }
                                    layer.msg(result.msg, {offset: 'rb'});
                                });
                                return false;
                            });
                        });
                    }
                })
            })
        },
        gain: function () {
            var checkData = tree.getChecked('treeId');
            var str = JSON.stringify(checkData);
            $.post('dept/checkedGain', {data: str}, function () {
            });
            layer.alert(JSON.stringify(checkData), {shade:0});
        }
    });

    添加部门按钮点击事件,先发送请求到后台,跳转到eidt新增页面,edit.html新增页面代码,和上面的show.html显示部门详情页面差不多。上级部门同样使用的LayUI下拉树显示的,下拉树数据请求方法,和上面的详情页面下拉树请求方法一致。LayUI下拉树TreeSelect的使用。新增后的保存方法也和上面的保存方法一致。

    后台请求方法代码,跳转到edit页面。

    @RequestMapping(value = "/edit")
    public void edit(){
        
    }

    edit.html页面完整代码如下。

    <meta charset="UTF-8" />
    <style type="text/css">
        .myData .layui-form-item{
            margin: 20px 100px 10px 45px;
        }
        .myData .layui-form-label{
            width: 85px;
        }
        .layui-input-block {
            margin-left: 120px;
        }
    </style>
    <form class="layui-form myData" action="save" method="post" lay-filter="stuform">
    
        <div class="layui-form-item">
            <label class="layui-form-label">上级部门:</label>
            <div class="layui-input-block">
                <input type="text" name="parentId" id="tree2" lay-filter="tree2" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">部门名称:</label>
            <div class="layui-input-block">
                <input type="text" name="name" lay-verify="required" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">部门负责人:</label>
            <div class="layui-input-block">
                <input type="text" name="deptName" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">电话:</label>
            <div class="layui-input-block">
                <input type="text" name="phone" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">编号:</label>
            <div class="layui-input-block">
                <input type="text" name="number" class="layui-input" />
            </div>
        </div>
        <div class="layui-form-item" >
            <label class="layui-form-label">排序:</label>
            <div class="layui-input-block">
                <input type="text" name="idx" value="0" class="layui-input" />
            </div>
        </div>
    </form>
    
    <script th:inline="javascript">
        layui.use(["treeSelect", "form"], function () {
            var form = layui.form;
            form.render('select');
            var treeSelect = layui.treeSelect;
            treeSelect.render({
                // 选择器
                elem: '#tree2',
                // 数据
                data: 'dept/treeSelect',
                // 异步加载方式:get/post,默认get
                type: 'post',
                // 占位符
                placeholder: '上级菜单',
                // 是否开启搜索功能:true/false,默认false
                search: true,
                // 一些可定制的样式
                style: {
                    folder: {
                        enable: true
                    },
                    line: {
                        enable: true
                    }
                },
                // 加载完成后的回调函数
                success: function (d) {
                }
            });
        });
    </script>
    

    页面效果。

    获取选中节点按钮点击事件。如果项目需要保存数据时,就需要获取到选中节点的数据了。这里可以获取到选中节点的数据,之后当参数传递到后台。传递到后台是一个JSON数据的字符串,需要转换一下,这里给推荐大家两个很好用的JSON转换工具net.sf.json.JSONObjectAlibaba Fastjson。这里用的是Alibaba Fastjson,需要引入以下依赖。

    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>1.2.47</version>
    </dependency>

    这里用于输出,新建了一个和tree的json数据的键名称一样的工具类DeptTree,代码如下。

    import java.util.ArrayList;
    import java.util.List;
    
    public class DeptTree {
        private Integer id;
        private String title;
        private boolean checked;
        private boolean spread;
        private List<DeptTree> children = new ArrayList<>();
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public List<DeptTree> getChildren() {
            return children;
        }
    
        public void setChildren(List<DeptTree> children) {
            this.children = children;
        }
    
        public boolean isChecked() {
            return checked;
        }
    
        public void setChecked(boolean checked) {
            this.checked = checked;
        }
    
        public boolean isSpread() {
            return spread;
        }
    
        public void setSpread(boolean spread) {
            this.spread = spread;
        }
    }

    后台接收到传递过来的JSON数据字符串,转换后输出方法。

    @RequestMapping(value = "/checkedGain")
    @ResponseBody
    public void checkedGain(String data){
        List<DeptTree> array2 = JSONArray.parseArray(data, DeptTree.class);
        treeData(array2);
    }
    
    //递归输出选中数据
    private void treeData(List<DeptTree> array2) {
        for (DeptTree tree: array2){
            System.out.println(tree.getTitle()+"==="+tree.getId());
            if(tree.getChildren() != null){
                treeData(tree.getChildren());
            }
        }
    }

    选中节点,点击获取选中节点数据。

    后台对应方法接收到数据,转换后输出结果。

    数据拿到了,之后保存方法就简单了。

    后台方法代码基本都在上面了,页面全部代码。

    <style type="text/css">
        #dept_main, #dept_particulars{
            width: 48.5%;
            display: inline-block;
            vertical-align: top;
            padding: 20px;
            background: white;
            box-sizing: border-box;
        }
        #dept_tree{
            margin-top: 20px;
        }
    </style>
    
    <div id="dept_main" style="margin-right: 2%;">
        <fieldset class="layui-elem-field layui-field-title">
            <legend>所有部门</legend>
        </fieldset>
        <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="addDept"><i class="layui-icon">&#xe654;</i>添加部门</button>
        <button class="layui-btn layui-btn-sm layui-btn-radius layui-btn-normal" lay-demo="gain">获取选中节点</button>
        <div id="dept_tree">
    
        </div>
    </div>
    <div id="dept_particulars">
        <fieldset class="layui-elem-field layui-field-title">
            <legend>部门详情</legend>
        </fieldset>
        <div id="dept_home">
            <div class="layui-tree-emptyText">无数据</div>
        </div>
    </div>
    
    <script type="text/javascript">
        layui.use(['tree', 'util', 'layer'], function() {
            var tree = layui.tree;
            var util = layui.util;
            var layer = layui.layer;
            tree.render({
                elem: '#dept_tree',
                data: getData(),
                id: 'treeId',
                showCheckbox: true,     //时候显示复选框
                onlyIconControl: true,
                edit: ['add', 'update', 'del'], //操作节点的图标
                click: function (obj) {
                    var id = obj.data.id;
                    $("#dept_home").load("dept/show?id="+id);
                },
                operate: function(obj){
                    var type = obj.type; //得到操作类型:add、edit、del
                    var data = obj.data; //得到当前节点的数据
                    var elem = obj.elem; //得到当前节点元素
    
                    var id = data.id;
                    var name = data.title;
                    if(type === 'add'){ //增加节点
                        $.post("dept/save", {parentId: id, name: "未命名"}, function (result) {
                            tree.reload('treeId', {data: getData()});
                        })
                        //返回 key 值
                        return ;
                    } else if(type === 'update'){ //修改节点
                        $.post("dept/update", {id: id, name: name}, function () {
                            tree.reload('treeId', {data: getData()});
                        })
                    } else if(type === 'del'){ //删除节点
                        $.post("dept/delete", {id: id}, function () {
                            tree.reload('treeId', {data: getData()});
                        });
                    };
                }
            });
    
            util.event('lay-demo', {
                addDept: function(othis){
                    $.get('dept/edit', function(data) {
                        layer.open({
                            type: 1,
                            title: '新增',
                            area: ['530px'],
                            content: data,
                            btn: ['提交', '退出'],
                            yes: function () {
                            },
                            success: function (layero, index) {
                                layui.use('form', function () {
                                    var form = layui.form;
                                    layero.addClass('layui-form');
                                    var submitBtn = layero.find('.layui-layer-btn0');
                                    submitBtn.attr('lay-filter', 'formVerify').attr('lay-submit', '');
                                    layero.keydown(function (e) {
                                        if (e.keyCode == 13) {
                                            submitBtn.click();
                                        }
                                    });
    
                                    form.on('submit(formVerify)', function (data) {
                                        $.post('dept/save', data.field, function (result) {
                                            if (result.success) {
                                                layer.close(index);
                                                tree.reload('treeId', {data: getData()});
                                            }
                                            layer.msg(result.msg, {offset: 'rb'});
                                        });
                                        return false;
                                    });
                                });
                            }
                        })
                    })
                },
                gain: function () {
                    var checkData = tree.getChecked('treeId');
                    var str = JSON.stringify(checkData);
                    $.post('dept/checkedGain', {data: str}, function () {
                    });
                    layer.alert(JSON.stringify(checkData), {shade:0});
                }
            });
        });
    
        function getData(){
            var data = [];
            $.ajax({
                url: "dept/treeload",    //后台数据请求地址
                type: "post",
                async:false,
                success: function(resut){
                    data = resut;
                }
            });
            return data;
        }
        
    </script>

    如果对你有帮助,点赞关注一下呗^_^,留下你的足迹。

    展开全文
  • C/C++实现树形结构之二叉树

    万次阅读 2018-09-19 07:46:45
    C/C++实现树形结构之二叉树 树的概念 树形结构是一类非常重要的非线性数据结构,树中节点的位置具有明确的层次关系。并且结点之间有分支,非常类似于真正的树。而我们这里着重讲述的是二叉树。 二叉树是树形结构的一...

    C/C++实现树形结构之二叉树

    树的概念

    树形结构是一类非常重要的非线性数据结构,树中节点的位置具有明确的层次关系。并且结点之间有分支,非常类似于真正的树。而我们这里着重讲述的是二叉树。

    二叉树是树形结构的一种重要的类型,在实际应用中有着非常重要的作用和意义。二叉树是n个节点的有限集合,他的每个节点至多只有两棵子树。当然也可以是空集。或者是由一个根节点及两棵互不相交的分别成为这个根的左子树和右子树的二叉树组成。

    从以上概念中,我们可以看出,树的定义是递归的。根据这个定义我们也可以导出二叉树的五种形态(1)空二叉树 (2)仅有根节点 (3)只有左子树 (4)只有右子树 (5)既有左子树又有右子树。

    二叉树的概念

    性质1:
    在二叉树的第i层上至多有2^(i-1)个节点

    性质2:
    深度为k的二叉树至多有2^k-1个结点(k>=1)

    性质3:
    对任何一棵二叉树T,若其终端结点数为n0,度数为2的结点数为n2,则n0=n2+1

    性质四可以从前面的性质中推出。可以自己尝试下。

    二叉树的逻辑结构

    二叉树的逻辑结构为:一共三个域,其中一个指向它的左孩子的根结点,另一个指向它右孩子的根结点。数据域存储数据。

    typedef struct node {
    	DataType data;
    	node* lchild, *rchild;
    } BinTNode;
    typedef BinTNode * BinTree;
    

    根据广义表生成二叉树

    在前面一篇文章中,我曾给大家介绍过广义表,广义表也是一种树形结构,你们可以把它看成是线性结构像非线性结构的过度。传送门:
    https://blog.csdn.net/qq_27180763/article/details/82751971

    这段代码是利用了广义表来建立一个树的。该算法稍微简单一些,但是要求用广义表表示二叉树的理解要深刻。它与普通的表示二叉树的广义表形式有些不同,因为他有左右子树之分。括号左边的结点是在左子树上,括号右边的结点是在右子树上。
    算法中使用了一个指针数组来模拟栈的存储结点的双亲指针,根据读入广义表中的字符分四种不同情况来处理。

    BinTNode* CreateTree(const char* str)
    {
    	BinTNode* St[100];
    	BinTNode* p = NULL;
    	int top, k=0, j = 0;
    	top = -1;
    	char chr = str[j];
    	BinTNode* b = NULL;
    	while(chr != '\0')
    	{
    		switch (chr) {
    			case '(':top++; St[top] = p; k = 1; break;
    			case ')':top--; break;
    			case ',':k = 2; break;
    			default: 
    				p = (BinTNode*)malloc(sizeof(BinTNode));
    				p->data = chr;
    				p->lchild = p->rchild = NULL;
    				if (b == NULL) b = p;
    				else {
    					switch (k) {
    						case 1:St[top]->lchild = p; break;
    						case 2:St[top]->rchild = p; break;
    					}
    				}
    		}
    		j++;
    		chr= str[j];
    	}
    	return b;
    }
    

    按完全二叉树的层次顺序依次输入结点信息建立二叉链表的算法

    该算法的基本思想是:首先对一般的二叉树添加若干个虚结点,使其成为完全二叉树。然后依次的输入节点的信息。若输入的节点信息不是虚结点’@‘,则建立一个新结点。若为第一个结点,则为根节点。否则将新节点作为左孩子或者是右孩子链接到它的双亲结点上。如此重复,直到遇到结束符号’#‘

    //按照完全二叉树的层次顺序建立二叉链表
    BinTree CreateBinTree(BinTree bt)
    {
    	BinTNode* Q[100];
    	BinTNode* s;
    	int front, rear;
    	char ch;
    	ch = getchar();
    	bt = NULL;
    	front = 1;
    	rear = 0;
    	while (ch != '#')
    	{
    		s = NULL;
    		if (ch!= '@')
    		{
    			s = (BinTNode*)malloc(sizeof(BinTNode));
    			s->data = ch;
    			s->lchild = s->rchild = NULL;
    		}
    		rear++;
    		Q[rear] = s;
    		if (rear == 1)bt = s;
    		else {
    			if (s != NULL && Q[front] != NULL)
    			{
    				if (rear % 2 == 0) { Q[front]->lchild = s; }
    				else Q[front]->rchild = s;
    			}
    			if (rear % 2 != 0)front++;
    		}
    		ch = getchar();
    	}
    	return bt;
    }
    

    递归实现的遍历算法

    //前序遍历算法
    void Preorder(BinTree bt)
    {
    	if (bt != NULL)
    	{
    		printf("%c", bt->data);
    		Preorder(bt->lchild);
    		Preorder(bt->rchild);
    	}
    }
    
    //中序遍历算法
    void Midorder(BinTree bt)
    {
    	if (bt != NULL)
    	{
    		Midorder(bt->lchild);
    		printf("%c", bt->data);
    		Midorder(bt->rchild);
    	}
    }
    
    //后序遍历算法
    void Postorder(BinTree bt)
    {
    	if (bt != NULL)
    	{
    		Postorder(bt->lchild);
    		Postorder(bt->rchild);
    		printf("%c", bt->data);
    	}
    }
    
    

    非递归实现的遍历算法

    void Inorder1(BinTree bt)
    {
    	BinTNode* ST[100];
    	int top = 0;
    	ST[top] = bt;
    	do {
    		while (ST[top] != NULL) { 
    			top = top + 1;
    			ST[top] = ST[top - 1]->lchild;
    		}
    		top = top - 1; 
    		if (top >= 0)
    		{
    			printf("%c", ST[top]->data);
    			ST[top] = ST[top]->rchild;
    		}
    	} while (top != -1);
    }
    
    展开全文
  • 如何把一段表格数据转换成树形结构数据呢?#什么是树形结构数据展示在页面上的效果Java代码实现树形结构数据1.建立树形结构树的展示模型2.实现逻辑结束 #什么是树形结构数据 展示在页面上的效果 Java代码实现树形...

    1、什么是树形结构数据

    在这里插入图片描述

    1.1、展示在页面上的效果

    这里是很简单的二级树的效果

    2、Java实现树形结构数据

    2.1、bean对象

    tree的domain

    • 模型中树的属性主要有4个,实际情况可能有所不同,但这4个是必须的。id(节点ID),name(节点介绍),fatherId(父节点ID),ListchilNode(子节点内容)

    2.2、示例代码

     piblic  List<TreeData> getTree(){
      // 储存所有的子节点的数据
      List<TreeData> tempList = new ArrayList<>(); 
      // 储存最顶级父节点的数据    
      List<TreeData> resultList = new ArrayList<>(); 
            
      //循环顶级父节点数据,目的是在每一个父级树节点中装填对应的子节点数据
      List<TreeData> data_tree = new ArrayList<TreeData>();   
      // 递归装填数据
     for (TreeData treeData : resultList) {    
             data_tree.add(getChildNode(tempList, treeData));   
          }
    }
    
    private TreeData getChildNode(List<TreeData> tempList, TreeData fatherTree) {
            for (TreeData treeData : tempList) {
                if (treeData.getFatherId() == fatherTree.getId()) {
                    fatherTree.getChildNode().add(treeData);                             
                    getChildNode(tempList, treeData);//注意这里
                 }
                return fatherTree;
            }
           return fatherTree;}
    

    如上一个简单的树形结构的数据就产生了,当然实际的业务会比这个复杂一些,但基础的思路的不变的。当然也会有很多更加简介的代码实现的方式。

    3、结束

    • 一个人走的快,一群人走的远,一起学习,一起进步。
    展开全文
  • 工作中不时会遇见对树形结构数据的处理,有时候只需要遍历并获取其中一个属性值就行了(这部分内容请参考笔者的另一篇博客JS遍历树形结构方法),有时候我们则需要根据某些条件去过滤并得到新的树形结构数据。...
  • 主要代码 import java.io.File; import java.io.IOException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel...
  • 实际开发中经常会遇到需要对三级甚至是无限级树型结构进行过滤并且过滤之后仍要保留树形结构的情况,此时采用递归的方式可以满足要求,不但可以完成过滤需求并保留树形结构。 2、核心思想 如果当前节点包含过滤条件...
  • 树形dp+树形结构总结

    万次阅读 多人点赞 2017-10-18 10:17:54
     最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的。 树形dp一般都是用子树更新父亲(即从下向上更新),那么首先应该考虑的是一个一个子树的更新父亲还是把所有...
  • 前端页面实现树形结构

    万次阅读 2017-12-05 17:16:57
    树形结构
  • XML树形结构

    2020-02-29 23:28:44
    分析XML元素构成的树形结构
  • Java树形结构

    万次阅读 2017-12-23 20:00:30
    mysql取表结构,java树形结构,递归
  • MySQL树形结构设计

    千次阅读 2019-07-16 17:08:30
    文章目录MySQL树形结构设计树形结构举例树形数据举例为什么这样设计 MySQL树形结构设计 树形结构举例 假设要设计这样一个结构的数据库表 德源公司 王某公司 王XX负责人 郫县打样店铺 李XX店长 值班人员1 代理...
  • MyBatis树形结构查询

    万次阅读 热门讨论 2016-11-24 14:59:18
    前言 对于树形结构的数据库设计通常是基于继承关系设计的,也就是通过父ID关联来实现的,还有就是基于左右值编码设计。本文以继承关系设计的树形结构来讨论下MyBatis树形结构查询。以深度为二的树为例,要将这种...
  • 树形结构list构建(树形实体)

    千次阅读 2017-04-17 15:52:24
    树形结构list构建(树形实体) 树形sql查出是按树形顺序排列的list(结果集),正真要形成树形结构的对象还要组织下(Tree(自包含结构)),   在设计树形数据的时候根节点的标志比如id=-1,或他的parentId=null,...
  • 整理树形结构数据

    千次阅读 2020-06-30 19:22:36
    整理树形结构数据 树形结构是一种常见的数据组织结构: 今天恰好需要完成一个接口,将数据组织长树形结构,下面是简化的代码: import lombok.AllArgsConstructor; import lombok.Data; import lombok....
  • 树形结构基本概念

    2019-01-08 20:14:02
    树形结构  前面说过的数据结构都属于线性数据结构,也就是数据都是在同一个线性结构里面存储的。树形结构指的就是存储数据的结构为树形。类似于下面的图,因为看起来很像一颗倒着的树,所以称为树形结构。 基本...
  • 树形结构的数据库表设计

    万次阅读 多人点赞 2018-04-23 15:11:30
    树形结构的数据库表Schema设计 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门、栏目结构、商品分类等等,通常而言,这些树状结构需要借助于数据库完成持久化。然而目前的各种基于...
  • TreeNode 树形结构

    千次阅读 2019-12-06 14:39:31
    TreeNode 树形结构 package java2019; public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int val){ this.val=val; } }
  • 有项目实践经验的同学可能碰到过这样的问题:怎样才能高效地在mysql数据库里遍历树形结构。网络上有一些方法,但也各有优缺点。这是我实现的一套算法,希望能抛砖引玉,提供一个新的思路。 主要目的是给树形结构的...
  • 文件夹树形结构

    千次阅读 2018-11-15 10:05:52
    大家都见过树形结构,所谓的树形结构就是如同树枝一般一层下面有一层。文件夹的树形结果就如同此图, 列出效果: A |--&gt;B |--&gt;|--&gt;b_1 |--&gt;|--&gt;|--&gt;b_...
  • mysql存储树形结构的数据

    万次阅读 2017-12-03 23:46:58
    JSON格式的树形结构数据需要保存到mysql中。 树形图如下: 分析成文本如图: 存到表中的结构为: 需求 一般树形结构的数据使用需求有两点: 显示整棵树的数据 select * from treeNodes 给出某个点,显示...
  • 【概述】 树是一种非线性的、递归定义的有序数据结构,能...树与二叉树是最基本的树形结构,掌握好树与二叉树,对后续树形结构的学习有极大的帮助。 关于树:点击这里 关于二叉树:点击这里 【相关算法】 常见...
  • Vue树形结构操作

    万次阅读 2018-07-09 22:49:21
    树形结构是一种常用的数据结构,使用Vue怎么来渲染呢?要把树结构的每一个节点都渲染成dom,需要对树结构进行递归遍历。Vue组件可以通过name选项的设置来递归的调用自己,因此渲染起来很方便。 本文简单实现了一下...
  • vue手写树形结构

    2020-03-19 17:12:51
    今天项目中有一个三级树形功能,写完自己有想写一个根据数据自动生成的树形结构。 主要的思路:就是树形结构是一个重复的循环的,因此需要遍历自己的组件。只是生成一级的数据,而下面的数据没有生成并且报错了。 !...
  • 非线性结构元素的前驱和后继的个数不是为1的,这一节讲的树形结构元素的前驱个数为1,但是元素的后继个数不是为1了(可以有多个后继),所以说树形机构元素的关系是一对多或者多对多的。树型结构的特点是节点之间是...
  • elementUI中el-table树形结构一键折叠/展开 1.业务需求: vue项目,权限菜单管理中菜单table的树形结构,一般情况下如果使用的是elementUI那么可以使用里面el-table的树形结构,实际场景需要一键折叠/展开功能。 2...
  • es6过滤树形结构数组

    2020-12-02 11:31:24
    项目中遇到一个需求是左侧树形结构有不同类型的数据,产品要求只显示某一个类型的数据,这样我就就要对从后端返回的属性数据进行递归过滤; 树形结构数组如下: 简单一点,我就写两层树形结构! const arr = [ ...
  • Mybatis返回树形结构

    千次阅读 2019-09-10 18:00:25
    下面介绍一种使用mybatis来返回树形结构的好方法 表结构展示 表里面需要记录父级Id Mybatis xml语句 第一次用过org_id找到符合的数据,返回给orgTreeMap,关键就在这个resultMap里面,有一个集合,集合又去调用了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,459
精华内容 11,383
关键字:

树形结构