為什么使用樹:
樹結合了兩種數據結構的有點:一種是有序數組,樹在查找數據項的速度和在有序數組中查找一樣快;另一種是鏈表,樹在插入數據和刪除數據項的速度和鏈表一樣。既然這樣,我就要好好去學了....
(最主要討論的是二叉樹中的二叉搜索樹,即一個節點的左子節點關鍵值小於這個節點,右子節點的關鍵值大於這個節點)
設計前的思考:
樹——>元素(節點)
class Node { public int iData ; public float fData ; public Node left ; public Node right ; //方法 public Node(int iData,float fData){} public void displayNode(){} }
class Tree { Node root ;//樹根 //方法 public void insert(){} public void displayTree(){} public void find(){} public void delete(){} }
插入數據:

1 //插入子節點 2 public void insert(int iData ,float fData) 3 { 4 Node newNode = new Node(iData,fData) ; 5 6 if(root == null) 7 root = newNode ; 8 else 9 { 10 Node current = root ; 11 Node parent ; 12 while(true)//尋找插入的位置 13 { 14 parent = current ; 15 if(iData<current.iData) 16 { 17 current = current.left ; 18 if(current == null) 19 { 20 parent.left = newNode ; 21 return ; 22 } 23 } 24 else 25 { 26 current =current.right ; 27 if(current == null) 28 { 29 parent.right = newNode ; 30 return ; 31 } 32 } 33 } 34 } 35 }
遍歷樹:


1 //中序遍歷方法 2 public void inOrder(Node localRoot) 3 { 4 if(localRoot != null) 5 { 6 inOrder(localRoot.left) ;//調用自身來遍歷左子樹 7 localRoot.displayNode() ;//訪問這個節點 8 inOrder(localRoot.right) ;//調用自身來遍歷右子樹 9 } 10 }
查找某個節點:

1 //查找某個節點 2 public Node find(int iData) 3 { 4 Node current = root ; 5 while(current.iData != iData) 6 { 7 if(current.iData<iData) 8 current = current.right ; 9 else 10 current = current.left ; 11 if(current == null) 12 return null ; 13 } 14 return current ; 15 }
查找樹中關鍵字的最大值和最小值:
最大值:不斷地尋找右子節點
最小值:不斷地尋找左子節點


1 //查找關鍵字最小的節點 2 public Node findMinNode() 3 { 4 Node current , last ; 5 last = null ; 6 current = root ; 7 if(current.left == null) 8 return current ; 9 else 10 { 11 while(current != null) 12 { 13 last = current ; 14 current = current.left ; 15 } 16 return last ; 17 } 18 }
刪除某個節點:
思考:
1).先找到要刪除的節點:
1 public boolean delete(int key) 2 { 3 //先找到需要刪除的節點 4 Node current = root ; 5 Node parent = root ; 6 boolean isLeftChild = false ; 7 8 while(current.iData != key)//顯然,當current.iData == key 時,current 就是要找的節點 9 { 10 parent = current ; 11 if(key < current.iData) 12 { 13 isLeftChild = true ; 14 current = current.left ; 15 } 16 else 17 { 18 isLeftChild = false ; 19 current = current.right ; 20 } 21 if(current == null)//找不到key時返回false 22 return false ; 23 } 24 //continue ........ 25 }
2).再考慮要刪除的節點是怎樣的節點,經分析,有三種情況:葉節點、有一個節點的節點、有兩個節點的節點
A).如果刪除的是一個葉子節點,直接刪除即可
//接上................ //分情況考慮刪除的節點 //刪除的節點為葉節點時 if(current.left == null && current.right == null) { if(current == root) root = null ; else if(isLeftChild) parent.left = null ; else parent.right = null ; } //continue...........
B).如果刪除的節點有一個節點時:分兩種情況,刪除的節點只有一個左子節點,或者只有一個右子節點
//接上....... //刪除的節點有一個子節點 else if(current.right == null)//刪除的節點只有一個左子節點時 { if(current == root)//要刪除的節點為根節點 root = current.left ; else if(isLeftChild)//要刪除的節點是一個左子節點 parent.left = current.left ; else parent.right = current.left ;//要刪除的節點是一個右子節點 } else if(current.left == null)//刪除的節點只有一個右子節點時 { if(current == root)//要刪除的節點為根節點 root = current.right ; else if(isLeftChild)//要刪除的節點是一個左子節點 parent.left = current.right ; else parent.right = current.right ;//要刪除的節點是一個右子節點 } //continue.......
c).如果刪除的節點有兩個節點時:
這種情況就比較復雜,需要去尋找一個節點去替代要刪除的節點。這個節點應該是什么節點呢?
據書本介紹,最合適的節點是后繼節點,即比要刪除的節點的關鍵值次高的節點是它的后繼節點。
說得簡單一些,后繼節點就是比要刪除的節點的關鍵值要大的節點集合中的最小值。
以上面的為例,40的后繼節點為74,10的后繼節點是13,19的后繼節點時26
以下是尋找后繼節點的代碼:
1 //返回后繼節點 2 private Node getSuccessor(Node delNode) 3 { 4 Node successorParent = delNode ;//后繼節點的父節點 5 Node successor = delNode ;//后繼節點 6 Node current = delNode.right ;//移動到位置節點位置 7 while(current != null) 8 { 9 successorParent = successor ; 10 successor = current ; 11 current = current.left ; 12 } 13 if(successor != delNode.right) 14 { 15 successorParent.left = successor.right ; 16 successor.right = delNode.right ; 17 } 18 return successor ; 19 }
找到了后繼節點,接着就要討論如何用后繼節點替代葯刪除的節點
a)如果后繼節點是剛好是要刪除節點的右子節點(此時可以知道,這個右子節點沒有左子點,如果有,就不該這個右子節點為后繼節點)
//要刪除的節點為左子節點時 parent.left = successor ; successor.left = current.left ; //要刪除的節點是右子節點時 parent.right = successor ; successor.left = current.left ;
b)如果后繼節點為要刪除節點的右子節點的左后代:
//假如要刪除的節點為右子節點 successorParent.left = successor.right ;//第一步 successor.right = current.right ;//第二步 parent.right = successor ; successor.left = current.left ; //假設要刪除的節點為左子節點 successorParent.left = successor.right ; successor.right = current.right ; parent.left = successor ; successor.left = current.left ;
注意:第一步和第二步在getSuccessor()方法的最后的if語句中完成
以下是刪除的節點有連個節點的代碼:
1 //接上 2 //刪除的節點有兩個子節點 3 else 4 { 5 Node successor = getSuccessor(current) ;//找到后繼節點 6 if(current == root) 7 root = successor ; 8 else 9 if(isLeftChild) 10 parent.left = successor ; 11 else 12 parent.right = successor ; 13 successor.left = current.left ; 14 } 15 //continue....
綜合上述,給出delete()方法的代碼:

1 //刪除某個節點 2 public boolean delete(int key) 3 { 4 //先找到需要刪除的節點 5 Node current = root ; 6 Node parent = root ; 7 boolean isLeftChild = false ; 8 9 while(current.iData != key)//顯然,當current.iData == key 時,current 就是要找的節點 10 { 11 parent = current ; 12 if(key < current.iData) 13 { 14 isLeftChild = true ; 15 current = current.left ; 16 } 17 else 18 { 19 isLeftChild = false ; 20 current = current.right ; 21 } 22 if(current == null)//找不到key時返回false 23 return false ; 24 } 25 26 //分情況考慮刪除的節點 27 //刪除的節點為葉節點時 28 if(current.left == null && current.right == null) 29 { 30 if(current == root) 31 root = null ; 32 else 33 if(isLeftChild) 34 parent.left = null ; 35 else 36 parent.right = null ; 37 } 38 //刪除的節點有一個子節點 39 else 40 if(current.right == null)//刪除的節點只有一個左子節點時 41 { 42 if(current == root)//要刪除的節點為根節點 43 root = current.left ; 44 else 45 if(isLeftChild)//要刪除的節點是一個左子節點 46 parent.left = current.left ; 47 else 48 parent.right = current.left ;//要刪除的節點是一個右子節點 49 } 50 else 51 if(current.left == null)//刪除的節點只有一個右子節點時 52 { 53 if(current == root)//要刪除的節點為根節點 54 root = current.right ; 55 else 56 if(isLeftChild)//要刪除的節點是一個左子節點 57 parent.left = current.right ; 58 else 59 parent.right = current.right ;//要刪除的節點是一個右子節點 60 } 61 //刪除的節點有兩個子節點 62 else 63 { 64 Node successor = getSuccessor(current) ;//找到后繼節點 65 if(current == root) 66 root = successor ; 67 else 68 if(isLeftChild) 69 parent.left = successor ; 70 else 71 parent.right = successor ; 72 successor.left = current.left ; 73 } 74 return true ; 75 }
進一步考慮:
刪除那么復雜,那刪除是必要的嗎?我們可以給每個節點定義一個標志,該標志用於記錄該節點是否已經刪除了,
顯示樹時,先判斷該節點是否已經刪除,如果沒有,則顯示。
這樣的結果是,節點其實是沒有刪除的,這樣顯然逃避責任了。當樹中沒有那么多的刪除操作時,這也不失為一種好方法,例如:
已經離職的員工的檔案要永久地保存在員工的記錄中。
OVER.....( ^_^ )