java、js中實現無限層級的樹形結構(類似遞迴)
js中:
var zNodes=[
{id:0,pId:-1,name:"Aaaa"},
{id:1,pId:0,name:"A"},
{id:11,pId:1,name:"A1"},
{id:12,pId:1,name:"A2"},
{id:13,pId:1,name:"A3"},
{id:2,pId:0,name:"B"},
{id:21,pId:2,name:"B1"},
{id:22,pId:2,name:"B2"},
{id:23,pId:2,name:"B3"},
{id:3,pId:0,name:"C"},
{id:31,pId:3,name:"C1"},
{id:32,pId:3,name:"C2"},
{id:33,pId:3,name:"C3"},
{id:34,pId:31,name:"x"},
{id:35,pId:31,name:"y"},
{id:36,pId:31,name:"z"},
{id:37,pId:36,name:"z1123"} ,
{id:38,pId:37,name:"z123123123"}
];
function treeMenu(a){
this.tree=a||[];
this.groups={};
};
treeMenu.prototype={
init:function(pid){
this.group();
return this.getDom(this.groups[pid]);
},
group:function(){
for(var i=0;i<this.tree.length;i++){
if(this.groups[this.tree[i].pId]){
this.groups[this.tree[i].pId].push(this.tree[i]);
}else{
this.groups[this.tree[i].pId]=[];
this.groups[this.tree[i].pId].push(this.tree[i]);
}
}
},
getDom:function(a){
if(!a){return ''}
var html='\n<ul >\n';
for(var i=0;i<a.length;i++){
html+='<li><a href="#">'+a[i].name+'</a>';
html+=this.getDom(this.groups[a[i].id]);
html+='</li>\n';
};
html+='</ul>\n';
return html;
}
};
var html=new treeMenu(zNodes).init(0);
alert(html);
java:
先看兩個圖例,有個直觀上的認識:
圖一,銀行分支機構樹形結構選單
圖二,樹形結構表格
二、詳細設計方案
讓我們先看一段程式碼片段:
檔案一,branchTree.html (Ext樹形控制元件頁面)
Js程式碼- 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串:
Js程式碼- {
- 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字串
如圖所示:
三、原始碼實現(Java語言版)
實現這樣一顆樹,需要設計三個類:樹類(MultipleTree.java)、節點類(Node.java)、孩子列表類(Children.java);為了方便演示,還需要構造一些假的層次資料,因此還需要建一個構造假資料的類(VirtualDataGenerator.java),以下程式碼拷貝出來之後可直接執行測試:
- 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.toString());
- // 對多叉樹進行橫向排序
- root.sortChildren();
- // 輸出有序的樹形選單的JSON字串
- System.out.println(root.toString());
- // 程式輸出結果如下(無序的樹形選單)(格式化後的結果):
- // {
- // id : '100000',
- // text : '廊坊銀行總行',
- // children : [
- // {
- // id : '110000',
- // text : '廊坊分行',
- // children : [
- // {
- // id : '113000',
- // text : '廊坊銀行開發區支行',
- // leaf : true
- // },
- // {
- // id : '111000',
- // text : '廊坊銀行金光道支行',
- // leaf : true
- // },
- // {
- // id : '112000',
- // text : '廊坊銀行解放道支行',
- // children : [
- // {
- // id : '112200',
- // text : '廊坊銀行三大街支行',
- // leaf : true
- // },
- // {
- // id : '112100',
- // text : '廊坊銀行廣陽道支行',
- // leaf : true
- // }
- // ]
- // }
- // ]
- // }
- // ]
- // }
- // 程式輸出結果如下(有序的樹形選單)(格式化後的結果):
- // {
- // id : '100000',
- // text : '廊坊銀行總行',
- // children : [
- // {
- // id : '110000',
- // text : '廊坊分行',
- // children : [
- // {
- // id : '111000',
- // text : '廊坊銀行金光道支行',
- // leaf : true
- // },
- // {
- // id : '112000',
- // text : '廊坊銀行解放道支行',
- // children : [
- // {
- // id : '112100',
- // text : '廊坊銀行廣陽道支行',
- // leaf : true
- // },
- // {
- // id : '112200',
- // text : '廊坊銀行三大街支行',
- // leaf : true
- // }
- // ]
- // },
- // {
- // id : '113000',
- // text : '廊坊銀行開發區支行',
- // leaf : true
- // }
- // ]
- // }
- // ]
- // }
- }
- }
- /**
- * 節點類
- */
- class Node {
- /**
- * 節點編號
- */
- public String id;
- /**
- * 節點內容
- */
- public String text;
- /**
- * 父節點編號
- */
- public String parentId;
- /**
- * 孩子節點列表
- */
- private Children children = new Children();
- // 先序遍歷,拼接JSON字串
- public String toString() {
- String result = "{"
- + "id : '" + id + "'"
- + ", text : '" + text + "'";
- if (children != null && children.getSize() != 0) {
- result += ", children : " + children.toString();
- } else {
- result += ", leaf : true";
- }
- return result + "}";
- }
- // 兄弟節點橫向排序
- public void sortChildren() {
- if (children != null && children.getSize() != 0) {
- children.sortChildren();
- }
- }
- // 新增孩子節點
- public void addChild(Node node) {
- this.children.addChild(node);
- }
- }
- /**
- * 孩子列表類
- */
- class Children {
- private List list = new ArrayList();
- public int getSize() {
- return list.size();
- }
- public void addChild(Node node) {
- list.add(node);
- }
- // 拼接孩子節點的JSON字串
- public String toString() {
- String result = "[";
- for (Iterator it = list.iterator(); it.hasNext();) {
- result += ((Node) it.next()).toString();
- result += ",";
- }
- result = result.substring(0, result.length() - 1);
- result += "]";
- return result;
- }
- // 孩子節點排序
- public void sortChildren() {
- // 對本層節點進行排序
- // 可根據不同的排序屬性,傳入不同的比較器,這裡傳入ID比較器
- Collections.sort(list, new NodeIDComparator());
- // 對每個節點的下一層節點進行排序
- for (Iterator it = list.iterator(); it.hasNext();) {
- ((Node) it.next()).sortChildren();
- }
- }
- }
- /**
- * 節點比較器
- */
- class NodeIDComparator implements Comparator {
- // 按照節點編號比較
- public int compare(Object o1, Object o2) {
- int j1 = Integer.parseInt(((Node)o1).id);
- int j2 = Integer.parseInt(((Node)o2).id);
- return (j1 < j2 ? -1 : (j1 == j2 ? 0 : 1));
- }
- }
- /**
- * 構造虛擬的層次資料
- */
- class VirtualDataGenerator {
- // 構造無序的結果集列表,實際應用中,該資料應該從資料庫中查詢獲得;
- public static List getVirtualResult() {
- List dataList = new ArrayList();
- HashMap dataRecord1 = new HashMap();
- dataRecord1.put("id", "112000");
- dataRecord1.put("text", "廊坊銀行解放道支行");
- dataRecord1.put("parentId", "110000");
- HashMap dataRecord2 = new HashMap();
- dataRecord2.put("id", "112200");
- dataRecord2.put("text", "廊坊銀行三大街支行");
- dataRecord2.put("parentId", "112000");
- HashMap dataRecord3 = new HashMap();
- dataRecord3.put("id", "112100");
- dataRecord3.put("text", "廊坊銀行廣陽道支行");
- dataRecord3.put("parentId", "112000");
- HashMap dataRecord4 = new HashMap();
- dataRecord4.put("id", "113000");
- dataRecord4.put("text", "廊坊銀行開發區支行");
- dataRecord4.put("parentId", "110000");
- HashMap dataRecord5 = new HashMap();
- dataRecord5.put("id", "100000");
- dataRecord5.put("text", "廊坊銀行總行");
- dataRecord5.put("parentId", "");
- HashMap dataRecord6 = new HashMap();
- dataRecord6.put("id", "110000");
- dataRecord6.put("text", "廊坊分行");
- dataRecord6.put("parentId", "100000");
- HashMap dataRecord7 = new HashMap();
- dataRecord7.put("id", "111000");
- dataRecord7.put("text", "廊坊銀行金光道支行");
- dataRecord7.put("parentId", "110000");
- dataList.add(dataRecord1);
- dataList.add(dataRecord2);
- dataList.add(dataRecord3);
- dataList.add(dataRecord4);
- dataList.add(dataRecord5);
- dataList.add(dataRecord6);
- dataList.add(dataRecord7);
- return dataList;
- }
- }
好了,通過上面的程式碼,就可以實現多叉樹的兄弟節點橫向排序和先序遍歷了,實現了將層次資料轉換為有序無限級JSON字串的目的。
在實際的專案中,可以把上面的有效程式碼融入其中,或者在此基礎上進行一些擴充套件:
1、 實現對指定層次的排序(例如只排序第一層的節點,或者只排序某一父節點下的所有子節點)
2、 遍歷輸出樹形結構時可以加入判斷條件過濾掉某些節點
3、 實現節點的刪除功能
4、 在節點類中增加一個父節點的引用,就可以計算出某一節點所處的級別
5、 在不支援層次查詢的資料庫應用系統中使用該演算法實現相同的效果
四、思考與總結
這篇文章的重點是如何構造有序的無限級的樹形結構JSON字串,一次性生成樹形選單,而不是利用AJAX的方式,反覆向伺服器端傳送請求,一級接一級的載入樹節點。
既然可以構造無限級的JSON字串,那麼也可以根據這個思路構造無限級的XML字串,或者構造具有層次結構的UL – LI組合(用UL - LI來展示樹形結構),或者構造具有層次結構的TABLE(用TABLE來展示樹形結構)。如下所示:
(1)XML層次結構
Xml程式碼- <menuGroup id="100000" name="廊坊銀行總行">
- <menuGroup id="110000" name="廊坊分行">
- <menu id="113000" name="廊坊銀行開發區支行">
- </menu>
- <menu id="111000" name="廊坊銀行金光道支行">
- </menu>
- <menuGroup id="112000" name="廊坊銀行解放道支行">
- <menu id="112200" name="廊坊銀行三大街支行">
- </menu>
- <menu id="112100" name="廊坊銀行廣陽道支行">
- </menu>
- </menuGroup>
- </menuGroup>
- </menuGroup>
(2)UL - LI 層次結構
Html程式碼- <ul>
- <li>廊坊銀行總行</li>
- <ul>
- <li>廊坊分行</li>
- <ul>
- <li>廊坊銀行開發區支行</li>
- <li>廊坊銀行解放道支行</li>
- <ul>
- <li>廊坊銀行三大街支行</li>
- <li>廊坊銀行廣陽道支行</li>
- </ul>
- <li>廊坊銀行金光道支行</li>
- </ul>
- </ul>
- </ul>
(3)TABLE層次結構
Html程式碼- <table>
- <tr><td>廊坊銀行總行</td></tr>
- <tr><td> 廊坊分行</td></tr>
- <tr><td> 廊坊銀行開發區支行</td></tr>
- <tr><td> 廊坊銀行解放道支行</td></tr>
- <tr><td> 廊坊銀行三大街支行</td></tr>
- <tr><td> 廊坊銀行廣陽道支行</td></tr>
- <tr><td> 廊坊銀行金光道支行</td></tr>
- </table>
另外對TreeGrid樹形表格也有一定的價值:
1、 一次性構造樹形表格,實現資料分級展示
2、 通過更換比較器,實現對不同表格列的全排序(全排序指的是對所有頁的資料進行排序,而不是隻對當前頁的資料排序)
3、 實現對樹形表格的完整分頁(每次分頁時,只取固定數目的第一層節點,之後呼叫toString方法,展示出完 整條數的分級資料)
五、參考書籍
1、Mark Allen Weiss,資料結構與演算法分析(Java語言描述)
2、Bruce Eckel,Thinking In Java Third Edition
3、David Flanagan,JavaScript: The Definitive Guide, 5th Edition
4、OCA Oracle Database 11g SQL Fundamentals I Exam Guide
相關推薦
java、js中實現無限層級的樹形結構(類似遞迴)
js中: var zNodes=[{id:0,pId:-1,name:"Aaaa"}, {id:1,pId:0,name:"A"}, {id:11,pId:1,name:"A1"}, {id:12,pId:1,name:"A2"}, {id
angular.js和vue.js中實現函數去抖(debounce)
搜索輸入框 sea class 方案 get clas 電路 dia ive 問題描述 搜索輸入框中,只當用戶停止輸入後,才進行後續的操作,比如發起Http請求等。 學過電子電路的同學應該知道按鍵防抖。原理是一樣的:就是說當調用動作n毫秒後,才會執行該動作,若在這n毫秒內又
用js來實現那些數據結構(數組篇01)
指定 賦值 重要 原始類型 delete 號稱 如果 開發 實現原理 在開始正式的內容之前,不得不說說js中的數據類型和數據結構,以及一些比較容易讓人混淆的概念。那麽為什麽要從數組說起?數組在js中是最常見的內存數據結構,數組數據結構在js中擁有很多的方法,很多初學者
小程式無限層級路由方案(無框架依賴)
背景 小程式歷史棧最多隻支援10層 當小程式業務比較複雜時,就很容易超過10層。 當超過10層後,有的機型是點選無反應,有的機型會出現一些未知錯誤 為了解決這些問題,我們引入了無限層級路由方案。 方案 首先宣告一下,最初方案並不是我提出的,是我司內部一位清華學霸提出的。但他們是基於w
Nodejs學習筆記(八)--- Node.js + Express 實現上傳檔案功能(felixge/node-formidable)
目錄 前言 前面講了一個構建網站的示例,這次在此基礎上再說說web的常規功能----檔案上傳,示例以一個上傳圖片的功能為例子 上傳功能命名用formidable實現,示例很簡單! PS:最近比較忙,距上一次更新已經比較久了^_^! formidable簡介 nodejs
在SRAM、FLASH中除錯程式碼的配置方法(附詳細步驟)
因為STM32的FLASH擦寫次數有限(大概為1萬次),所以為了延長FLASH的使用時間,我們平時除錯時可以選擇在SRAM中進行硬體除錯。除此之外,SRAM 儲存器的寫入速度比在內部 FLASH 中要快得多,所以下載程式到SRAM中的速度較快。 所以我們很有必要建立兩個版本的工程配置,在SRAM中除錯程式完畢
c++中求1!+2!+3!+...+20!(不用遞迴)
c++中求1!+2!+3!+…+20!(不用遞迴) #include "stdafx.h" #include<iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) {
十進位制轉換二進位制C++實現(非遞迴)
實現十進位制轉換為二進位制非遞迴實現 以下是C++原始碼: #include<iostream> #include<stdio.h> using namespace std; //十進位制 轉換為 二進位制------非遞迴 int DecToBin(int de
VUE-開發一個樹形結構元件(元件遞迴)
需求 一個頁面,要顯示商品分類,同時每個分類下面還擁有若干子類,子類也可以有子類。 要實現全選單選,子類被逐個全選父類也要標記選中。 第一反應就是樹形結構,和遞迴呼叫。曾經在做WPF時記得有現成的元件,也學著寫過對應的後臺。這次我要自己寫一個前端的元件了。 這只是我自己花了點時間寫的一個
LintCode Binary Tree Inorder Traversal 二叉樹的中序遍歷(非遞迴)
給出一棵二叉樹,返回其中序遍歷。 Given a binary tree, return the inorder traversal of its nodes’ values. 樣例 給出二叉樹 {1,#,2,3}, 1 \
Android 實現FlowLayout流式佈局(類似熱門標籤)
今天跟大家分享一下FlowLayout,最近專案中有遇到熱門標籤這個樣的佈局(文章末尾可下載原始碼),如下圖: 一,建立FlowLayout並繼承ViewGroup FlowLayout 類主要實現onMeasure,onLayout和generateL
通過二叉樹的中序和後序遍歷序列構造二叉樹(非遞迴)
題目:通過二叉樹的中序和後序遍歷序列構造二叉樹 同樣,使用分治法來實現是完全可以的,可是在LeetCode中執行這種方法的程式碼,總是會報錯: Memory Limit Exceeded ,所以這裡還是用棧來實現二叉樹的構建。 與用先序和後序遍歷構造二叉樹的方法類似,
部分函式的遞迴與迭代(非遞迴)實現
部分函式的遞迴和迭代(非遞迴)實現 首先,說到遞迴函式,大家一般都會想到斐波那契數列和計算階乘這兩種,書本上大多也是以這兩個為例,下面就簡單介紹一下這兩個吧。 斐波那契數列: 1 1 2 3 5 8......這樣的數列叫做斐波那契數列,規律很簡單,前兩個數字的和就是第三個
中序遍歷(非遞迴)
//前序遍歷 /*前序遍歷的遞迴實現*/ void preOrder(BinTree *root){ if(NULL!=root){ cout<<root->data<&
用棧實現二叉樹的遍歷(非遞迴)
先實現棧 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef char ElemType; #define STACKSIZE 20 typedef st
用鄰接表儲存有向圖並實現DFS(遞迴+非遞迴)BFS(非遞迴)兩種遍歷
程式碼如下: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> using namespace std; #define MA
利用鄰接矩陣儲存無向圖,並實現BFS(非遞迴) DFS(遞迴+非遞迴)兩種遍歷
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> using namespace std; //------------鄰接矩陣----------- #def
演算法-一步步教你如何用c語言實現堆排序(非遞迴)
看了左神的堆排序,覺得思路很清晰,比常見的遞迴的堆排序要更容易理解,所以自己整理了一下筆記,帶大家一步步實現堆排序演算法 首先介紹什麼是大根堆:每一個子樹的最大值都是子樹的頭結點,即根結點是所有結點的最大值 堆排序是基於陣列和二叉樹思想實現的(二叉樹是腦補結構,實際是陣列) 堆排序過程 1、陣列建
MD5中Java和Js配套實現
MD5為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。用於確保資訊傳輸完整一致。是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,MD5的前身有
二叉樹的建樹、遍歷(先序、中序、後序、層次)(遞迴和非遞迴)--Java實現
什麼是樹?什麼是二叉樹? 樹:除了根節點之外的所有節點都有且只有一個父節點,根節點沒有父節點;除了葉結點以外的所有節點,都有一個或多個子節點,葉結點沒有子節點。 二叉樹:是樹的一種特殊結構,在二叉樹中,每個節點最多隻能有兩個子