在上一篇中,我們說到了二叉樹的性質,存儲以及定義的結點,有了這些之后,我們便可以來創建一棵二叉查找樹了。
首先,我們知道,按照我們定義的存儲結構,如果我們知道了整棵樹的根結點,那么我們就可以訪問到整棵樹的所有結點了,因此,將二叉樹的類寫成如下形式:
1 /** 2 * 二叉查找樹 3 * @author Alfred 4 */ 5 public class BSTree { 6 //隨機化的構造二叉查找樹 7 private Random rand = null; 8 //根結點 9 private TreeNode rootNode = null; 10 11 /** 12 * 以int數組A來創建二叉查找樹 13 * @param A int數組 14 */ 15 public BSTree(int[] A){ 16 rand = new Random(); 17 createBSTree(A); 18 } 19 20 /** 21 * 創建二叉查找樹 22 * @param A int數組 23 */ 24 private void createBSTree(int[] A){} 25 }
代碼里邊包含一個Random類的對象(這個對象時我自己加上去的,可以沒有,原因之后解釋)和一個根結點,我們假設用一個整數的數組來創建一棵二叉查找樹。我們知道,二叉查找樹是要滿足二叉查找樹的性質的,那么該如何構造一個滿足二叉查找樹性質的樹呢。在這里,我們使用插入的方法,將整數數組里邊的整數一個一個的插入到我們要構建的二叉查找樹里面,在插入元素的時候,要保證滿足二叉查找樹的性質,因此我們要處理兩個問題:
1.假如插入的元素已在數中存在,則該如何處理。
2.如何插入才能最大程度上避免出現最壞的情況。
對於第一個問題,在這里的處理是將相同的結點都存在一個位置上,即合並為樹中的一個結點,看下上篇的結點定義便知,定義中的dataNum就是記錄的是相同結點的個數。
對於第二個問題,首先來說一下最壞的情況是啥,想象一下,最壞的情況莫過於數組已排好序,構建的二叉查找樹退化為一個直鏈。那該如何處理呢?如果我們在插入的時候隨機的選擇一個數,而不是按照既定的順序來獲取數組中的數,那么我們就可以做到盡可能的避免最壞情況的發生了。
好了,插入的代碼如下:
1 /** 2 * 插入一個整數 3 * @param z 整數 4 */ 5 public void TreeInsert(int z){ 6 TreeNode parentNode = null; 7 TreeNode searchNode = rootNode; 8 TreeNode insertNode = new TreeNode(z); 9 //while循環找到要插入的點的父結點 10 while(searchNode != null){ 11 parentNode = searchNode; 12 if(insertNode.getKey() < searchNode.getKey()){ 13 searchNode = searchNode.getLeft(); 14 }else if(insertNode.getKey() == searchNode.getKey()){ 15 //如果是key值相同的話,直接插入,偷懶在這里... 16 searchNode.incNumByOne(); 17 return; 18 }else{ 19 searchNode = searchNode.getRight(); 20 } 21 } 22 23 insertNode.setParent(parentNode); 24 if(parentNode == null){ 25 rootNode = insertNode; 26 }else if(insertNode.getKey() < parentNode.getKey()){ 27 //插入左結點 28 parentNode.setLeft(insertNode); 29 }else if(insertNode.getKey() == parentNode.getKey()){ 30 //因為上面插入了,所以這里就不會執行了。 31 parentNode.incNumByOne(); 32 System.out.println("this is not supposed to be executed."); 33 }else{ 34 //插入右結點 35 parentNode.setRight(insertNode); 36 } 37 }
插入的代碼很簡單,第一個while循環找到,我們要插入的結點的父結點,然后接下來將其插入。在這里要注意的地方是,假如樹中已有一個或多個元素與待插入的元素相同,那么這里的處理是將其放到相同的結點位置上,即只將這個重復結點的dataNum自加一下即可,在TreeNode里邊定義的偷懶的方法,這里就派上用場了。
那么如何實現隨機呢,我也沒想到什么特別好的方法,可以參考我寫的下一段代碼:
1 /** 2 * 創建二叉查找樹 3 * @param A int數組 4 */ 5 private void createBSTree(int[] A){ 6 //先構建一個存儲數組下標的List 7 List<Integer> index = new LinkedList<Integer>(); 8 int i = 0; 9 for(i = 0; i < A.length; i++){ 10 index.add(i); 11 } 12 //隨機構造二叉樹 13 for(i = 0; i < A.length; i++){ 14 int j = 0; 15 if(index.size() > 1){ 16 //隨機產生一個數組下標值 17 j = rand.nextInt(index.size() - 1); 18 } 19 //插入二叉樹 20 TreeInsert(A[index.get(j)]); 21 //移除下標 22 index.remove(j); 23 } 24 }
上面的代碼中,先創建了一個存儲數組下標的List,然后在插入的時候,隨機的從這個List里面選擇一個下標,取出該數組下標的數,插入到二叉查找樹中,然后將次下標從List中移除,這樣下一個循環就不會產生此次產生的下標了。這樣就實現了隨機化的構造二叉查找樹。
好了,有了上面寫的幾個方法,BSTree這個類就完成了二叉查找樹的插入操作和構建二叉查找樹的任務了。