Android版數據結構與算法(八):二叉排序樹
本文目錄
前兩篇文章我們學習了一些樹的基本概念以及常用操作,本篇我們了解一下二叉樹的一種特殊形式:二叉排序樹(Binary Sort Tree),又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。
一、二叉排序樹定義
二叉排序樹或者是一顆空樹,或者是具有下列性質的二叉樹:
-
若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值
-
若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值
-
它的左,右子樹也分別為二叉排序樹
也就是說二叉排序樹中左子樹結點值均小於根結點值,右子樹節點值均大於跟節點值,左右子樹同樣滿足上述約定。
如下圖,即為一顆二叉排序樹:
由二叉樹定義知道,我們通過中序遍歷二叉樹就可以按照從小到大順序排列二叉樹中所有元素。
如上圖中序遍歷結果為:35, 40, 42, 45, 50, 67
二、java代碼實現二叉排序樹核心方法
下面我們通過java代碼實現二叉排序樹中的幾個核心方法
我們先看下每個結點類定義如下:
1class TreeNode{ 2 private int data; 3 private TreeNode leftChild; 4 private TreeNode rightChild; 5 private TreeNode parent;6 7 public TreeNode(int data) { 8 this.data = data; 9 this.leftChild = null; 10 this.rightChild = null; 11 this.parent = null; 12 } 13}
很簡單,每個結點記錄自己以及左右孩子,父類的信息。
二叉排序樹的創建(增加元素方法)
創建一顆二叉排序樹就是不斷往裏面添加元素。
整體思路為:
-
判斷整棵樹根結點是否創建過,如果沒有創建那麽第一個加入進來的元素指定為根結點,方法返回。
-
如果二叉排序樹已經創建過,那麽再往裏面加入元素需要先找出其父節點,然後將要插入的元素掛載到父節點下即可。
-
經過上面過程找出其父結點,這裏只需創建節點,掛載到父節點下即可,指定為父節點左孩子還是右孩子只需比較一下元素大小即可。
源碼:
1 public TreeNode put(int data){ 2 TreeNode node = root; 3 TreeNode parent = null; 4 //判斷二叉排序樹根結點是否存在,不存在則創建 5 if (root == null){ 6 root = new TreeNode(data); 7 return root; 8 } 9 //查找其父類 10 while (node != null){ 11 parent = node;//記錄其父親節點 12 if (data > node.data){ 13 node = node.rightChild; 14 }else if (data < node.data){ 15 node = node.leftChild; 16 }else { 17 //已經存在則直接返回 18 return node; 19 } 20 } 21 //創建新節點並插入原有樹中 22 node = new TreeNode(data); 23 if (data < parent.data){ 24 parent.leftChild = node; 25 }else { 26 parent.rightChild = node; 27 } 28 node.parent = parent; 29 return node; 30 }
二叉排序樹的查找
二叉排序樹中查找比較簡單,思路為:
-
當前結點與查找的數據比較,相等則返回
-
若小於當前結點則從左子樹查找即可
-
若大於當前結點則從右子樹查找即可
重復上述過程,這裏就看出二分查找思想了
源碼:
1 public TreeNode searchNode(int data) { 2 TreeNode node = root; 3 if (node == null){ 4 return null; 5 }else { 6 while (node != null && data != node.data){ 7 if (data < node.data){ 8 node = node.leftChild; 9 }else { 10 node = node.rightChild; 11 } 12 } 13 } 14 return node; 15 }
二叉排序樹的刪除
二叉排序樹的刪除操作分4中情況:
-
若要刪除的結點無左右孩子也就是葉子結點,那麽直接刪除即可,將其父節點左或者右孩子置null即可
-
若要刪除的結點有左孩子無右孩子,則只需要將刪除結點的左孩子與其父節點建立關系即可
-
若要刪除的結點有右孩子無左孩子,則只需要將刪除結點的右孩子與其父節點建立關系即可
-
若要刪除的結點左右孩子均有,就需要選一個結點將其替換,這裏需要保證選取的結點保證比左子樹都大,右子樹都小,可以選取左子樹中最大的結點,或者右子樹中最小的結點,並且需要將選取的結點從二叉排序樹中刪除。
源碼:這裏我們選取右子樹最小的結點
1 public void deleteNode(int data){ 2 TreeNode node = searchNode(data); 3 if (node == null){ 4 throw new RuntimeException("未找到要刪除的節點"); 5 }else { 6 delete(node); 7 } 8 } 9 10 private void delete(TreeNode node) { 11 if (node == null){ 12 throw new RuntimeException("未找到要刪除的節點"); 13 }else { 14 TreeNode parent = node.parent; 15 //刪除的節點無左右孩子 16 if (node.leftChild == null && node.rightChild == null){ 17 if (parent.leftChild == node){ 18 parent.leftChild = null; 19 }else { 20 parent.rightChild = null; 21 } 22 return; 23 } 24 //刪除的節點有左無右 25 if (node.leftChild != null 26 && node.rightChild == null){ 27 if (parent.leftChild == node){ 28 parent.leftChild = node.leftChild; 29 }else { 30 parent.rightChild = node.leftChild; 31 } 32 return; 33 } 34 //刪除的節點有右無左 35 if (node.leftChild == null 36 && node.rightChild != null){ 37 if (parent.leftChild == node){ 38 parent.leftChild = node.rightChild; 39 }else { 40 parent.rightChild = node.rightChild; 41 } 42 return; 43 } 44 //刪除的結點左右都有 45 TreeNode rightMinNode = getRightMinNode(node.rightChild); 46 delete(rightMinNode); 47 node.data = rightMinNode.data; 48 } 49 } 50 51 //獲取右子樹最小的結點 52 private TreeNode getRightMinNode(TreeNode node) { 53 TreeNode minNode = node; 54 while (minNode != null && minNode.leftChild != null){ 55 minNode = minNode.leftChild; 56 } 57 System.out.println("minNode" + minNode.data); 58 return minNode; 59 }
接下來我們測試一下
測試代碼:
1 SearchBinaryTree ss = new SearchBinaryTree(); 2 int[] array = {77,88,34,55,66,2,34,67,78}; 3 for (int data : array) { 4 ss.put(data); 5 } 6 ss.midIter(ss.getRoot()); 7 System.out.println(); 8 SearchBinaryTree.TreeNode node = ss.searchNode(66); 9 System.out.println("find node:"+node.getData()); 10 ss.deleteNode(66); 11 SearchBinaryTree.TreeNode dnode = ss.searchNode(66); 12 if (dnode != null){ 13 System.out.println("find node:"+node.getData()); 14 }else { 15 System.out.println("not find node"); 16 } 17 ss.midIter(ss.getRoot());
打印信息如下:
1 2 34 55 66 67 77 78 88
2 find node:66
3 not find node
4 2 34 55 67 77 78 88
三、二叉排序樹性能問題
二叉排序樹最好的情況下其查找性能是很高的,接近二分查找法。
但是在有些情況下構建出的二叉排序樹類似一個鏈表,其查找性能為O(n),如下圖:
構建出這樣的樹肯定不是我們希望的,需要調整此樹達到平衡的效果,這裏就需要二叉平衡樹了(AVL樹),關於AVL樹會在後續篇章介紹,這裏知道二叉平衡樹有這個問題就可以了。
四、總結
本篇主要介紹了二叉平衡樹以及Java代碼實現其核心方法,希望你能掌握其與普通二叉樹的區別,以及其存在的問題,好了,本片到此為止,希望對你有用。
聲明:文章將會陸續搬遷到個人公眾號,以後也會第一時間發布到個人公眾號,及時獲取文章內容請關註公眾號
最後附上整個類的全部源碼,拷貝過去就可以用了:
1 public class SearchBinaryTree { 2 3 private TreeNode root;//二叉樹根結點 4 5 public TreeNode getRoot() { 6 return root; 7 } 8 9 //中序遍歷二叉排序樹:按照從小到大排序 10 public void midIter(TreeNode node){ 11 if (node == null){ 12 return; 13 } 14 midIter(node.leftChild); 15 System.out.print(" "+node.data); 16 midIter(node.rightChild); 17 } 18 19 public TreeNode put(int data){ 20 TreeNode node = root; 21 TreeNode parent = null; 22 //判斷二叉排序樹根結點是否存在,不存在則創建 23 if (root == null){ 24 root = new TreeNode(data); 25 return root; 26 } 27 //查找其父類 28 while (node != null){ 29 parent = node;//記錄其父親節點 30 if (data > node.data){ 31 node = node.rightChild; 32 }else if (data < node.data){ 33 node = node.leftChild; 34 }else { 35 //已經存在則直接返回 36 return node; 37 } 38 } 39 //創建新節點並插入原有樹中 40 node = new TreeNode(data); 41 if (data < parent.data){ 42 parent.leftChild = node; 43 }else { 44 parent.rightChild = node; 45 } 46 node.parent = parent; 47 return node; 48 } 49 50 public void deleteNode(int data){ 51 TreeNode node = searchNode(data); 52 if (node == null){ 53 throw new RuntimeException("未找到要刪除的節點"); 54 }else { 55 delete(node); 56 } 57 } 58 59 private void delete(TreeNode node) { 60 if (node == null){ 61 throw new RuntimeException("未找到要刪除的節點"); 62 }else { 63 TreeNode parent = node.parent; 64 //刪除的節點無左右孩子 65 if (node.leftChild == null && node.rightChild == null){ 66 if (parent.leftChild == node){ 67 parent.leftChild = null; 68 }else { 69 parent.rightChild = null; 70 } 71 return; 72 } 73 //刪除的節點有左無右 74 if (node.leftChild != null 75 && node.rightChild == null){ 76 if (parent.leftChild == node){ 77 parent.leftChild = node.leftChild; 78 }else { 79 parent.rightChild = node.leftChild; 80 } 81 return; 82 } 83 //刪除的節點有右無左 84 if (node.leftChild == null 85 && node.rightChild != null){ 86 if (parent.leftChild == node){ 87 parent.leftChild = node.rightChild; 88 }else { 89 parent.rightChild = node.rightChild; 90 } 91 return; 92 } 93 //刪除的結點左右都有 94 TreeNode rightMinNode = getRightMinNode(node.rightChild); 95 delete(rightMinNode); 96 node.data = rightMinNode.data; 97 } 98 } 99 100 private TreeNode getRightMinNode(TreeNode node) { 101 TreeNode minNode = node; 102 while (minNode != null && minNode.leftChild != null){ 103 minNode = minNode.leftChild; 104 } 105 System.out.println("minNode" + minNode.data); 106 return minNode; 107 } 108 109 public TreeNode searchNode(int data) { 110 TreeNode node = root; 111 if (node == null){ 112 return null; 113 }else { 114 while (node != null && data != node.data){ 115 if (data < node.data){ 116 node = node.leftChild; 117 }else { 118 node = node.rightChild; 119 } 120 } 121 } 122 return node; 123 } 124 125 126 public class TreeNode{ 127 private int data; 128 private TreeNode leftChild; 129 private TreeNode rightChild; 130 private TreeNode parent; 131 132 public TreeNode(int data) { 133 this.data = data; 134 this.leftChild = null; 135 this.rightChild = null; 136 this.parent = null; 137 } 138 139 public int getData() { 140 return data; 141 } 142 143 public void setData(int data) { 144 this.data = data; 145 } 146 147 public TreeNode getLeftChild() { 148 return leftChild; 149 } 150 151 public void setLeftChild(TreeNode leftChild) { 152 this.leftChild = leftChild; 153 } 154 155 public TreeNode getRightChild() { 156 return rightChild; 157 } 158 159 public void setRightChild(TreeNode rightChild) { 160 this.rightChild = rightChild; 161 } 162 163 public TreeNode getParent() { 164 return parent; 165 } 166 167 public void setParent(TreeNode parent) { 168 this.parent = parent; 169 } 170 } 171}
Android版數據結構與算法(八):二叉排序樹