一、基本概況
在我的項目中,常常會用到樹形結構的數據,最為明顯的就是左邊菜單欄,類似於window folder一樣的東西。
而我之前一直是借助前端封裝好的ZTree等工具實現展示,而后台則通常使用遞歸進行數據的查找。通常,我們在設計數據庫表的時候,一般會使用三個字段:id,name,pid。如下圖所示:
二、代碼實現
首先是建立實體類:
<span style="font-family:KaiTi_GB2312;font-size:18px;"> private String id; private String name; private String pid;</span>
編寫實體類的get和set方法。
然后,我們通常會有以下的幾個方法(通常情況,封裝粒度不同,方法的實現個數和內容也不同):
1,找到所有的父節點
<span style="font-family:KaiTi_GB2312;font-size:18px;">public List<TreeEntity> findAllParents() { String sql = "select * from test where pid is null or pid='' "; List<TreeEntity> treeList = null; try { conn = DbUtil.getConnection(); pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); treeList = new ArrayList<TreeEntity>(); while (rs.next()) { TreeEntity myTree = new TreeEntity(); myTree.setId(rs.getString("id")); myTree.setName(rs.getString("name")); myTree.setPid(rs.getString("pid")); treeList.add(myTree); } } catch (SQLException e) { e.printStackTrace(); } finally { DbUtil.close(pstmt); DbUtil.close(conn); } return treeList; }</span>
2,根據父節點找到所有的孩子
<span style="font-family:KaiTi_GB2312;font-size:18px;">public List<TreeEntity> findChildByPid(String pid) { String sql = "select * from test where pid='" + pid + "'"; List<TreeEntity> treeList = null; try { conn = DbUtil.getConnection(); pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); treeList = new ArrayList<TreeEntity>(); while (rs.next()) { TreeEntity myTree = new TreeEntity(); myTree.setId(rs.getString("id")); myTree.setName(rs.getString("name")); myTree.setPid(rs.getString("pid")); treeList.add(myTree); } } catch (SQLException e) { e.printStackTrace(); } finally { DbUtil.close(pstmt); DbUtil.close(conn); } return treeList; }</span>
備注:這兩個方法可以合並,這里是為了讓自己更好的理解,而寫了兩個方法。可以判斷傳入的pid的值,確定其查找的是父節點,還是根據父節點查找子節點。
3,查看是否存在子節點
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public boolean HasChild(String pid) { boolean flag = false; String sql = "select * from test where pid='" + pid + "'"; int count = 0; try { conn = DbUtil.getConnection(); pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); while(rs.next()){ count++; } if (count > 0) { flag = true; } } catch (SQLException e) { e.printStackTrace(); } finally { DbUtil.close(pstmt); DbUtil.close(conn); } return flag; }</span>
4,使用遞歸拼接父節點的子節點
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public void BindChildByParent(String pid, String prefix) { if (this.HasChild(pid)) { // 得到當前父節點下的所有孩子 List<TreeEntity> list = this.findChildByPid(pid); // 循環打印當前父節點下的孩子 for (int i = 0; i < list.size(); i++) { System.out.println("|----"+prefix+list.get(i).getName()); if (this.HasChild(list.get(i).getId())) { this.BindChildByParent(list.get(i).getId(),"--"); } } } }</span>
5,打印樹
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public void TreeHtml() { // 找到所有的父節點 List<TreeEntity> treeList1 = this.findAllParents(); if (treeList1 != null) { for (int i = 0; i < treeList1.size(); i++) { TreeEntity tree = treeList1.get(i); // 打印父節點 System.out.println("|--" + tree.getName()); // 綁定孩子 this.BindChildByParent(tree.getId(), ""); } } else { System.out.println("沒有數據!"); } }</span>
6,main方法調用,及實現結果
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public static void main(String[] args) { Tree tree = new Tree(); tree.TreeHtml(); }</span>
三、代碼思考
最近,由於考試,看了數據結構 這本書。首先,我是在想,大家都用的這種方法,到底好在哪兒了,還有就是,為什么在我們的數據庫設計中,樹的度的概念沒有體現出來。其次是,對於樹的遍歷,有非遞歸的方式,我想也許,我也可以不用遞歸,就實現樹形結構的數據查找。於是乎,請看下文:
為什么我不想用遞歸:
1,經過查證,系統使用遞歸算法,需要系統堆棧處理。當樹的深度很大時,由於系統支撐不住,會呈現死亡狀態。
2,遞歸算法的運行效率較低,無論是耗費的計算時間還是占用的存儲空間都比非遞歸算法要多。
3,最為直接的原因:很長一段時間里,我都不能理解遞歸算法,我總在想,可不可以用我會的,我喜歡的 方式,去解決我面臨的問題?
遞歸的好處:
結構清晰,可讀性強,而且容易用數學歸納法來證明算法的正確性,因此它為設計算法、調試程序帶來很大方便。
五、總結
事實證明,對於樹結構的數據搜索,完全可以不使用遞歸。我總算完成了我自己的夢想,終於,我可以不用遞歸,也可以實現樹結構的查找了。更為高興的是,事實證明,采用非遞歸的方式,在我接觸到的項目中,它有更大的優勢。
下一篇播客,介紹怎么用非遞歸的方式查找樹結構的數據!至此,我好像覺得自己又變得不一樣了的感覺,我把數據結構這本書的內容,完全結合到自己的項目中,並且用這些東西,去改造去理解我的代碼。開心,不過還有圖,我不知道怎么用的,關於圖,我想到了非關系型數據庫,再去驗證吧!