節點類:java
/** * 節點類 */ public class TreeNode { /** * 節點編號 */ public String id; /** * 節點內容 */ public String text; /** * 父節點編號 */ public String parentId; /** * 孩子節點列表 */ //private Children children = new Children(); private List<TreeNode> children = new ArrayList<TreeNode>(); // 添加孩子節點 public void addChild(TreeNode node) { //this.children.addChild(node); this.children.add(node); } public List<TreeNode> getChildren() { return children; } }
使用遞歸的方式遍歷樹並生成json字符串node
//使用遞歸的方式來遍歷樹 private void travelByRecursion(TreeNode node, StringBuilder builder) { //dealnode //System.out.print("{" + "\"id\" : \"" + node.id + "\"" + ", \"text\" : \"" + node.text + "\""); builder.append("{" + "\"id\" : \"" + node.id + "\"" + ", \"text\" : \"" + node.text + "\""); List<TreeNode> children = node.getChildren(); if (children != null && !children.isEmpty()) { //System.out.print(", \"children\" : ["); builder.append(", \"children\" : ["); for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) { TreeNode n = it.next(); travelByRecursion(n, builder); if (it.hasNext()) { //System.out.print(","); builder.append(','); } } //System.out.print("]"); builder.append(']'); } else { //System.out.print(", \"leaf\" : true"); builder.append(", \"leaf\" : true"); } //System.out.print("}"); builder.append('}'); }
使用循環的方式遍歷樹並生成json字符串json
//使用循環的方式來遍歷樹 private void travelByCycle(TreeNode node, StringBuilder builder) { //定義一個先入後出的棧存放節點信息 Stack<TreeNode> stack = new Stack<TreeNode>(); //定義一個先入後出的棧存放節點的閉合符號 Stack<String> closeSymbolStack = new Stack<String>(); //定義一個散列表存放同一層節點的最後一個節點的信息(用於判斷是否輸出逗號),key爲node的id,value爲上一節點的id Map<String, String> latestNode = new HashMap<String, String>(); stack.push(node); TreeNode lpNode; //根節點確定是同一層節點的最後一個節點,因此須要加入散列表 latestNode.put(node.id, null); //int i=0; //closeSymbolStack.push("]}"); //System.out.println(node.id); //i++; while (!stack.isEmpty()) { lpNode = stack.pop(); //dealnode builder.append("{" + "\"id\" : \"" + lpNode.id + "\"" + ", \"text\" : \"" + lpNode.text + "\""); List<TreeNode> children = lpNode.getChildren(); if (children != null && !children.isEmpty()) { builder.append(", \"children\" : ["); boolean firstNode = true; TreeNode currentNode = null; for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) { currentNode = it.next(); if (firstNode) { //棧是先進後出的,因此在同一層級中第一個入棧的節點其實是最後一個被讀取的節點也就是最後一個節點 //System.out.println(currentNode.id + "," + lpNode.id); latestNode.put(currentNode.id, lpNode.id); firstNode = false; } stack.push(currentNode); } //往閉合符號棧中壓入該節點對應的閉合符號 //判斷該節點是否爲同一層節點的最後一個節點 //若是不是同一層節點的最後一個節點則須要多輸出一個逗號 if (latestNode.containsKey(lpNode.id)) { //System.out.println(lpNode.id); closeSymbolStack.push("]}"); //i++; } else { closeSymbolStack.push("]},"); } } else { builder.append(", \"leaf\" : true}"); //判斷是滯是不是同一層節點的最後一個葉子節點 if (!latestNode.containsKey(lpNode.id)) { //若是不是是同一層節點的最後一個葉子節點則輸出逗號 builder.append(','); } else { //若是是是同一層節點的最後一個葉子節點則輸出閉合符號 if (!closeSymbolStack.isEmpty()) { builder.append(closeSymbolStack.pop()); } //若是是同一層節點的最後一個葉子節點則判斷上一節點是不是最後一個節點,若是是則輸出閉合符號,循環處理直到上一節點不是同層次的最後一個節點或上一節點爲空 String tempId = lpNode.id; String parendNodeId = latestNode.get(tempId); while (parendNodeId != null && latestNode.containsKey(parendNodeId)) { //System.out.println(tempId + "," + parendNodeId); if (!closeSymbolStack.isEmpty()) { builder.append(closeSymbolStack.pop()); } tempId = parendNodeId; parendNodeId = latestNode.get(tempId); } } } } if (!closeSymbolStack.isEmpty()) { for (Iterator<String> it = closeSymbolStack.iterator(); it.hasNext();) { builder.append(it.next()); } } //System.out.println("i is " + i); }
實現難度的比較app
從以上代碼能夠看出,遞歸實現的代碼很是簡潔,自然就是爲生成json或xml這種格式的字符串設計的,而經過循環的方式遍歷的代碼顯得比較煩瑣,特別是爲了實現生成json字符串的功能,使用了兩個輔助變量closeSymbolStack和latestNode後顯得更加煩瑣,說實話爲了使用循環的方式來生成json我花了大概一天的時間,而遞歸只有了幾分鐘而已。jvm
2. 性能比較性能
我分別對深度只有4的樹形結構進行了幾回循環測試,測試結果以下測試
循環次數 | 遞歸實現耗時(毫秒) | 循環實時耗時(毫秒) |
5000 | 40 | 70 |
10000 | 70 | 101 |
100000 | 306 | 394 |
1000000 | 1287 | 1638 |
10000000 | 10963 | 13621 |
而後又分別不一樣的樹的深度進行了測試,測試結果以下ui
樹的深度 | 遞歸實現耗時(毫秒) | 循環實時耗時(毫秒) |
1000 | 2 | 5 |
2000 | 13 | 14 |
3000 | 7 | 14 |
4000 | 12 | 19 |
5000 | java.lang.StackOverflowError | 24 |
10000 | java.lang.StackOverflowError | 36 |
100000 | java.lang.StackOverflowError | 475 |
能夠看出,若是樹的深度不深的話,使用遞歸的性能會比使用循環的好一些,但若是樹的深度比較深,樹的深度超過5000後,使用遞歸的方式因爲調用棧太深致使jvm拋出異常,而使用循環的方式依然正常。this