多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)

Posted xiaoshu20120101

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)相关的知识,希望对你有一定的参考价值。

一、问题研究的背景和意义

在Web应用程序开发领域,基于Ajax技术的javascript树形组件已经被广泛使用,它用来在html页面上展现具有层次结构的数据项。目前市场上常见的JavaScript框架及组件库中均包含自己的树形组件,例如jQuery、Ext JS等,还有一些独立的树形组件,例如dhtmlxTree等,这些树形组件完美的解决了层次数据的展示问题。展示离不开数据,树形组件主要利用Ajax技术从服务器端获取数据源,数据源的格式主要包括JSON、XML等,而这些层次数据一般都存储在数据库中。“无限级树形结构”,顾名思义,没有级别的限制,它的数据通常来自数据库中的无限级层次数据,这种数据的存储表通常包括id和parentId这两个字段,以此来表示数据之间的层次关系。现在问题来了,既然树形组件的数据源采用JSON或XML等格式的字符串来组织层次数据,而层次数据又存储在数据库的表中,那么如何建立起树形组件与层次数据之间的关系,换句话说,如何将数据库中的层次数据转换成对应的层次结构的JSON或XML格式的字符串,返回给客户端的JavaScript树形组件?这就是我们要解决的关键技术问题。本文将以目前市场上比较知名的Ext JS框架为例,讲述实现无限级树形结构的方法,该方法同样适用于其它类似的JavaScript树形组件。


Ext JS框架是富客户端开发中出类拔萃的框架之一。在Ext的UI组件中,树形组件无疑是最为常用的组件之一,它用来实现树形结构的视图。TreeNode用来实现静态的树形结构,AsyncTreeNode用来实现动态的异步加载树形结构,后者最为常用,它通过接收服务器端返回来的JSON格式的数据,动态生成树形结构节点。动态生成树有两种思路:一种是一次性生成全部树节点,另一种是逐级加载树节点(利用Ajax,每次点击节点时查询下一级节点)。对于大数据量的树节点来说,逐级加载是比较合适的选择,但是对于小数据量的树节点来说,一次性生成全部节点应该是最为合理的方案。在实际应用开发中,一般不会遇到特别大数据量的场景,所以一次性生成全部树节点是我们重点研究的技术点,也就是本文要解决的关键技术问题。本文以基于Ext JS的应用系统为例,讲述如何将数据库中的无限级层次数据一次性在界面中生成全部树节点(例如在界面中以树形方式一次性展示出银行所有分支机构的信息),同时对每一个层次的节点按照某一属性和规则排序,展示出有序的树形结构。


解决一次性构造无限级树形结构的问题,可以拓展出更多的应用场景,例如树形结构表格TreeGrid,一次性生成树形表格,对树形表格进行完整分页,对表格列进行全排序;或者可以利用本文的思路扩展出其他的更复杂的应用场景。


先看两个图例,有个直观上的认识:

图一,银行分支机构树形结构

多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)_树形结构

图二,树形结构表格

多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)_json字符串_02


二、详细设计方案

让我们先看两段代码片段:

文件一,branchTree.html (Ext树形组件页面)

Ext.onReady(
function()
var tree = new Ext.tree.TreePanel(
height: 300,
width: 400,
animate:true,
enableDD:true,
containerScroll: true,
rootVisible: false,
frame: true,
// getBranch.do请求服务器返回多级树形结构的JSON字符串
loader: new Ext.tree.TreeLoader(dataUrl:getBranch.do),
root : new Ext.tree.AsyncTreeNode(id:0,text:根结点)
);
tree.expandAll();

);


文件二,branchTreeJSON.jsp (接收getBranch.do请求,返回多级树形结构的JSON字符串)

<%
// 读取银行分支机构的层次数据
List result = DataAccess.getBankInfoList();
// 将层次数据转换为多叉树对象(本文下面会详细介绍该数据结构的实现方法)
Node root = ExtTreeHelper.createExtTree(result);
%>
[
<%=root.toString()%> <!-- 以JSON的形式返回响应数据,Ext.tree.TreeLoader会根据此数据生成树形结构 -->
]

以上两个程序文件是一次性生成无限级树形结构所必须的,其中最为关键的部分就是如何生成一个无限级的树形结构JSON字符串,返回给客户端的Ext树形组件。对于银行分支机构来说,需要返回类似如下的JSON串:


id: 100000,
text: 廊坊银行总行,
children: [

id: 110000,
text: 廊坊分行,
children: [

id: 113000,
text: 廊坊银行开发区支行,
leaf: true
,

id: 112000,
text: 廊坊银行解放道支行,
children: [

id: 112200,
text: 廊坊银行三大街支行,
leaf: true
,

id: 112100,
text: 廊坊银行广阳道支行,
leaf: true

]
,

id: 111000,
text: 廊坊银行金光道支行,
leaf: true

]

]

同时还需要对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序,以展示出有序的树形结构。

现在可以把问题概括为:

1、 把数据库中的层次数据转换成多级树形结构的JSON格式的字符串

2、 对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序

下面介绍解决问题的思路:

在数据结构这门课中,我们都学过树,无限级树形结构就可以抽象成一种多叉树结构,即每个节点下包含多个子节点的树形结构,首先就需要把数据库中的层次数据转换成多叉树结构的对象树,也就是构造出一棵多叉树。

有了数据结构,还要实现相应的算法,我们需要实现两种算法:

1、兄弟节点横向排序算法,对隶属于同一个父节点下面的所有直接子节点按照某一节点属性和规则进行排序,保持兄弟节点横向有序;

2、先序遍历算法,递归打印出无限级JSON字符串。

概括起来分为三步:

1、 构造无序的多叉树结构

2、 实现兄弟节点横向排序方法

3、 实现先序遍历方法,打印出JSON字符串


如图所示:

多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)_json字符串_03


三、源代码实现(Java版)

实现这样一颗树,需要设计两个类:树类(MultipleTree)、节点类(Node);排序时还需要一个比较器类(NodeIDComparator);为了方便演示,还需要构造一些假的层次数据,因此还需要建一个构造假数据的类(VirtualDataGenerator),以下代码拷贝出来之后可直接运行测试:

package test;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;

/**
* 多叉树类
*/
public class MultipleTree
public static void main(String[] args)
// 读取层次数据结果集列表
List dataList = VirtualDataGenerator.getVirtualResult();

// 节点列表(映射表,用于临时存储节点对象)
HashMap nodeList = new HashMap();
// 根节点
Node root = null;
// 将结果集存入映射表(后面将借助映射表构造多叉树)
for (Iterator it = dataList.iterator(); it.hasNext();)
Map dataRecord = (Map) it.next();
Node node = new Node();
node.id = (String) dataRecord.get("id");
node.text = (String) dataRecord.get("text");
node.parentId = (String) dataRecord.get("parentId");
nodeList.put(node.id, node);

// 构造无序的多叉树
Set entrySet = nodeList.entrySet();
for (Iterator it = entrySet.iterator(); it.hasNext();)
Node node = (Node) ((Map.Entry) it.next()).getValue();
if (node.parentId == null || node.parentId.equals(""))
root = node;
else
((Node) nodeList.get(node.parentId)).addChild(node);


// 输出无序的树形结构的JSON字符串
System.out.println(root);
// 对多叉树进行横向排序
root.sortChildren();
// 输出有序的树形结构的JSON字符串
System.out.println(root);

// 程序输出结果如下:
//
// 无序的树形结构(格式化后的结果,可使用JSON格式化工具查看,例如 http://jsonviewer.stack.hu/ 在线查看器):
//
// id : 100000,
// text : 廊坊银行总行,
// children : [
//
// id : 110000,
// text : 廊坊分行,
// children : [
//
// id : 113000,
// text : 廊坊银行开发区支行,
// leaf : true
// ,
//
// id : 111000,
// text : 廊坊银行金光道支行,
// leaf : true
// ,
//
// id : 112000,
// text : 廊坊银行解放道支行,
想用java实现一个无限级树型菜单,数据库是这样设计的:id,pid(父id),name.用递归调用,要一次性加载.

BZOJ1812riv(多叉树转二叉树,树形DP)

[bzoj1812][IOI2006]riv_多叉树转二叉树_树形dp

选课 树形DP+多叉树转二叉树+dfs求解答案

多叉树转二叉树+树形dp(codevs 1746 贪吃的九头龙 2002noi)

刷题总结——选课(ssoj树形dp+记忆化搜索+多叉树转二叉树)