java數(shù)據(jù)結(jié)構(gòu)之樹基本概念解析及代碼示例
Java中樹的存儲結(jié)構(gòu)實現(xiàn) 一、樹 樹與線性表、棧、隊列等線性結(jié)構(gòu)不同,樹是一...節(jié)點與節(jié)點之間的父子關(guān)系,可以為每個節(jié)點增加一個parent域,用以記錄該節(jié)點的父點
樹是一種抽象數(shù)據(jù)類型(ADT)或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>0)個有限節(jié)點組成一個具有層次關(guān)系的集合。把 它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。
樹定義和基本術(shù)語
定義
樹(Tree)是n(n≥0)個結(jié)點的有限集T,并且當(dāng)n>0時滿足下列條件:
(1)有且僅有一個特定的稱為根(Root)的結(jié)點;
(2)當(dāng)n>1時,其余結(jié)點可以劃分為m(m>0)個互不相交的有限集T1、T2、…、Tm,每個集Ti(1≤i≤m)均為樹,且稱為樹T的子樹(SubTree)。
特別地,不含任何結(jié)點(即n=0)的樹,稱為空樹。
如下就是一棵樹的結(jié)構(gòu):
基本術(shù)語
結(jié)點:存儲數(shù)據(jù)元素和指向子樹的鏈接,由數(shù)據(jù)元素和構(gòu)造數(shù)據(jù)元素之間關(guān)系的引用組成。
孩子結(jié)點:樹中一個結(jié)點的子樹的根結(jié)點稱為這個結(jié)點的孩子結(jié)點,如圖1中的A的孩子結(jié)點有B、C、D
雙親結(jié)點:樹中某個結(jié)點有孩子結(jié)點(即該結(jié)點的度不為0),該結(jié)點稱為它孩子結(jié)點的雙親結(jié)點,也叫前驅(qū)結(jié)點。雙親結(jié)點和孩子結(jié)點是相互的,如圖1中,A的孩子結(jié)點是B、C、D,B、C、D的雙親結(jié)點是A。
兄弟結(jié)點:具有相同雙親結(jié)點(即同一個前驅(qū))的結(jié)點稱為兄弟結(jié)點,如圖1中B、B、D為兄弟結(jié)點。
結(jié)點的度:結(jié)點所有子樹的個數(shù)稱為該結(jié)點的度,如圖1,A的度為3,B的度為2.
樹的度:樹中所有結(jié)點的度的最大值稱為樹的度,如圖1的度為3.
葉子結(jié)點:度為0的結(jié)點稱為葉子結(jié)點,也叫終端結(jié)點。如圖1的K、L、F、G、M、I、J
分支結(jié)點:度不為0的結(jié)點稱為分支結(jié)點,也叫非終端結(jié)點。如圖1的A、B、C、D、E、H
結(jié)點的層次:從根結(jié)點到樹中某結(jié)點所經(jīng)路徑的分支數(shù)稱為該結(jié)點的層次。根結(jié)點的層次一般為1(也可以自己定義為0),這樣,其它結(jié)點的層次是其雙親結(jié)點的層次加1.
樹的深度:樹中所有結(jié)點的層次的最大值稱為該樹的深度(也就是最下面那個結(jié)點的層次)。
有序樹和無序樹:樹中任意一個結(jié)點的各子樹按從左到右是有序的,稱為有序樹,否則稱為無序樹。
樹的抽象數(shù)據(jù)類型描述
數(shù)據(jù)元素:具有相同特性的數(shù)據(jù)元素的集合。
結(jié)構(gòu)關(guān)系:樹中數(shù)據(jù)元素間的結(jié)構(gòu)關(guān)系由樹的定義確定。
基本操作:樹的主要操作有
(1)創(chuàng)建樹IntTree(&T)
創(chuàng)建1個空樹T。
(2)銷毀樹DestroyTree(&T)
(3)構(gòu)造樹CreatTree(&T,deinition)
(4)置空樹ClearTree(&T)
將樹T置為空樹。
(5)判空樹TreeEmpty(T)
(6)求樹的深度TreeDepth(T)
(7)獲得樹根Root(T)
(8)獲取結(jié)點Value(T,cur_e,&e)
將樹中結(jié)點cur_e存入e單元中。
(9)數(shù)據(jù)賦值A(chǔ)ssign(T,cur_e,value)
將結(jié)點value,賦值于樹T的結(jié)點cur_e中。
(10)獲得雙親Parent(T,cur_e)
返回樹T中結(jié)點cur_e的雙親結(jié)點。
(11)獲得最左孩子LeftChild(T,cur_e)
返回樹T中結(jié)點cur_e的最左孩子。
(12)獲得右兄弟RightSibling(T,cur_e)
返回樹T中結(jié)點cur_e的右兄弟。
(13)插入子樹InsertChild(&T,&p,i,c)
將樹c插入到樹T中p指向結(jié)點的第i個子樹之前。
(14)刪除子樹DeleteChild(&T,&p,i)
刪除樹T中p指向結(jié)點的第i個子樹。
(15)遍歷樹TraverseTree(T,visit())
樹的實現(xiàn)
樹是一種遞歸結(jié)構(gòu),表示方式一般有孩子表示法和孩子兄弟表示法兩種。樹實現(xiàn)方式有很多種、有可以由廣義表的遞歸實現(xiàn),也可以有二叉樹實現(xiàn),其中最常見的是將樹用孩子兄弟表示法轉(zhuǎn)化成二叉樹來實現(xiàn)。
下面以孩子表示法為例講一下樹的實現(xiàn):
樹的定義和實現(xiàn)
package datastructure.tree; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; /** * 樹的定義和實現(xiàn) * @author Administrator * */ public class Tree { private Object data; private List<Tree> childs; public Tree(){ data = null; childs = new ArrayList(); childs.clear(); } public Tree(Object data) { this.data = data; childs = new ArrayList(); childs.clear(); } /** * 添加子樹 * @param tree 子樹 */ public void addNode(Tree tree) { childs.add(tree); } /** * 置空樹 */ public void clearTree() { data = null; childs.clear(); } /** * 求樹的深度 * 這方法還有點問題,有待完善 * @return 樹的深度 */ public int dept() { return dept(this); } /** * 求樹的深度 * 這方法還有點問題,有待完善 * @param tree * @return */ private int dept(Tree tree) { if(tree.isEmpty()) { return 0; } else if(tree.isLeaf()) { return 1; } else { int n = childs.size(); int[] a = new int[n]; for (int i=0; i<n; i++) { if(childs.get(i).isEmpty()) { a[i] = 0+1; } else { a[i] = dept(childs.get(i)) + 1; } } Arrays.sort(a); return a[n-1]; } } /** * 返回遞i個子樹 * @param i * @return */ public Tree getChild(int i) { return childs.get(i); } /** * 求第一個孩子 結(jié)點 * @return */ public Tree getFirstChild() { return childs.get(0); } /** * 求最后 一個孩子結(jié)點 * @return */ public Tree getLastChild() { return childs.get(childs.size()-1); } public List<Tree> getChilds() { return childs; } /** * 獲得根結(jié)點的數(shù)據(jù) * @return */ public Object getRootData() { return data; } /** * 判斷是否為空樹 * @return 如果為空,返回true,否則返回false */ public Boolean isEmpty() { if(childs.isEmpty() && data == null) return true; return false; } /** * 判斷是否為葉子結(jié)點 * @return */ public Boolean isLeaf() { if(childs.isEmpty()) return true; return false; } /** * 獲得樹根 * @return 樹的根 */ public Tree root() { return this; } /** * 設(shè)置根結(jié)點的數(shù)據(jù) */ public void setRootData(Object data) { this.data = data; } /** * 求結(jié)點數(shù) * 這方法還有點問題,有待完善 * @return 結(jié)點的個數(shù) */ public int size() { return size(this); } /** * 求結(jié)點數(shù) * 這方法還有點問題,有待完善 * @param tree * @return */ private int size(Tree tree) { if(tree.isEmpty()) { return 0; } else if(tree.isLeaf()) { return 1; } else { int count = 1; int n = childs.size(); for (int i=0; i<n; i++) { if(!childs.get(i).isEmpty()) { count += size(childs.get(i)); } } return count; } } }
樹的遍歷
樹的遍歷有兩種
前根遍歷
(1).訪問根結(jié)點;
(2).按照從左到右的次序行根遍歷根結(jié)點的第一棵子樹;
后根遍歷
(1).按照從左到右的次序行根遍歷根結(jié)點的第一棵子樹;
(2).訪問根結(jié)點;
Visit.Java
package datastructure.tree; import datastructure.tree.btree.BTree; /** * 對結(jié)點進(jìn)行操作的接口,規(guī)定樹的遍歷的類必須實現(xiàn)這個接口 * @author Administrator * */ public interface Visit { /** * 對結(jié)點進(jìn)行某種操作 * @param btree 樹的結(jié)點 */ public void visit(BTree btree); }
order.java
package datastructure.tree; import java.util.List; /** * 樹的遍歷 * @author Administrator * */ public class Order { /** * 先根遍歷 * @param root 要的根結(jié)點 */ public void preOrder(Tree root) { if(!root.isEmpty()) { visit(root); for (Tree child : root.getChilds()) { if(child != null) { preOrder(child); } } } } /** * 后根遍歷 * @param root 樹的根結(jié)點 */ public void postOrder(Tree root) { if(!root.isEmpty()) { for (Tree child : root.getChilds()) { if(child != null) { preOrder(child); } } visit(root); } } public void visit(Tree tree) { System.out.print("\t" + tree.getRootData()); } }
測試:
要遍歷的樹如下:
package datastructure.tree; import java.util.Iterator; import java.util.Scanner; public class TreeTest { /** * @param args */ public static void main(String[] args) { Tree root = new Tree("A"); root.addNode(new Tree("B")); root.addNode(new Tree("C")); root.addNode(new Tree("D")); Tree t = null; t = root.getChild(0); t.addNode(new Tree("L")); t.addNode(new Tree("E")); t = root.getChild(1); t.addNode(new Tree("F")); t = root.getChild(2); t.addNode(new Tree("I")); t.addNode(new Tree("H")); t = t.getFirstChild(); t.addNode(new Tree("L")); System.out.println("first node:" + root.getRootData()); //System.out.println("size:" + root.size()); //System.out.println("dept:" + root.dept()); System.out.println("is left:" + root.isLeaf()); System.out.println("data:" + root.getRootData()); Order order = new Order(); System.out.println("前根遍歷:"); order.preOrder(root); System.out.println("\n后根遍歷:"); order.postOrder(root); } }
結(jié)果:
first node:A
is left:false
data:A
前根遍歷:
A BL E C F DI L H
后根遍歷:
B LE C F D IL H A
結(jié)束語:
以上就是本文關(guān)于java數(shù)據(jù)結(jié)構(gòu)之樹基本概念解析及代碼示例的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
java實現(xiàn)遍歷樹形菜單兩種實現(xiàn)代碼分享
如有不足之處,歡迎留言指出。
相關(guān)文章
詳解Spring?Security?捕獲?filter?層面異常返回我們自定義的內(nèi)容
Spring?的異常會轉(zhuǎn)發(fā)到?BasicErrorController?中進(jìn)行異常寫入,然后才會返回客戶端。所以,我們可以在?BasicErrorController?對?filter異常進(jìn)行捕獲并處理,下面通過本文給大家介紹Spring?Security?捕獲?filter?層面異常,返回我們自定義的內(nèi)容,感興趣的朋友一起看看吧2022-05-05java Unicode和UTF-8之間轉(zhuǎn)換實例
這篇文章主要介紹了java Unicode和UTF-8之間轉(zhuǎn)換實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09SpringBoot整合MyBatis實現(xiàn)樂觀鎖和悲觀鎖的示例
這篇文章主要介紹了SpringBoot整合MyBatis實現(xiàn)樂觀鎖和悲觀鎖的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09基于jmeter實現(xiàn)跨線程組傳遞token過程圖解
這篇文章主要介紹了基于jmeter實現(xiàn)跨線程組傳遞token,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04