当用线性表作为表的组织形式时,可以有三种查找法。其中以二分查找效率最高。但由于二分查找要求表中
节点按关键字有序,且不能用链表作存储结构,因此,当表的插入或删除操作频繁时,为维护表的有序性,
势必要移动表中很多节点。这时由移动节点引起的额外时间开销,就会抵消二分查找的优点。也就是说,
二分查找只适用于静态查找表。
若要对动态查找表进行高效率的查找,可采用下面介绍的几种特殊的二叉树或树作为表的组织形式。不妨将它们统称为树表。
二叉排序树
1、二叉排序树的定义
二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树(Binary Search Tree)。其定义为:二叉排序树或者是空树,或者是满足如下性质的二叉树:
①若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
②若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
③左、右子树本身又各是一棵二叉排序树。
上述性质简称二叉排序树性质(BST性质),故二叉排序树实际上是满足BST性质的二叉树。
2、二叉排序树的特点
由BST性质可得:
(1) 二叉排序树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字。
(2) 二叉排序树中,各结点关键字是惟一的。
注意:
实际应用中,不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉排序树定义中BST性质(1)里的"小于"改为"大于等于",或将BST性质(2)里的"大于"改为"小于等于",甚至可同时修改这两个性质。
(3) 按中序遍历该树所得到的中序序列是一个递增有序序列。
java版源代码:
1 package arrange; 2 3 public class BinarySortTree { 4 BSTreeNode rootNode;// 创建根节点 5 // 创建二叉查找树 6 7 public void createBSTree(int[] A) { 8 rootNode = new BSTreeNode(A[0], null, null);// 初始化根节点 9 10 for (int i = 1; i < A.length; i++) {// 逐个取出数组A中的元素用来构造二叉查找树 11 BSTreeNode tmpNode = rootNode; // 为什么rootNode每次循环后都能增加新的值 ? 12 // System.out.println(tmpNode.data); 13 while (true) { 14 if (tmpNode.data >= A[i]) {// 小于等于根节点 15 if (tmpNode.left == null) {// 如果左孩子为空,这把当前数组元素插入到左孩子节点的位置 16 tmpNode.left = new BSTreeNode(A[i], null, null); // 给节点的左子树赋值 17 break; 18 } 19 20 tmpNode = tmpNode.left; 21 // 如果不为空的话,则把左孩子节点用来和当前数组元素作比较 22 } else {// 大于根节点 23 if (tmpNode.right == null) {// 如果右孩子为空,这把当前数组元素插入到左孩子节点的位置 24 tmpNode.right = new BSTreeNode(A[i], null, null); 25 break; 26 } 27 tmpNode = tmpNode.right;// 如果不为空的话,则把右孩子节点用来和当前数组元素作比较 28 } 29 } 30 } 31 32 } 33 34 // 中序遍历二叉查找树(中序遍历之后便可以排序成功) 35 public void inOrderBSTree(BSTreeNode x) { 36 if (x != null) { 37 inOrderBSTree(x.left);// 先遍历左子树 38 System.out.print(x.data + ",");// 打印中间节点 39 inOrderBSTree(x.right);// 最后遍历右子树 40 } 41 } 42 43 // 查询二叉排序树--递归算法 44 public BSTreeNode searchBSTree1(BSTreeNode x, BSTreeNode k) { 45 if (x == null || k.data == x.data) { 46 return x;// 返回查询到的节点 47 } 48 if (k.data < x.data) {// 如果k小于当前节点的数据域 49 return searchBSTree1(x.left, k);// 从左孩子节点继续遍历 50 } else {// 如果k大于当前节点的数据域 51 return searchBSTree1(x.right, k);// 从右孩子节点继续遍历 52 } 53 } 54 55 // 查询二叉排序树--非递归算法 56 public BSTreeNode searchBSTree2(BSTreeNode x, BSTreeNode k) { 57 while (x != null && k.data != x.data) { 58 if (x.data > k.data) { 59 x = x.left;// 从左孩子节点继续遍历 60 } else { 61 x = x.right;// 从右孩子节点继续遍历 62 } 63 } 64 return x; 65 } 66 67 // 查找二叉查找树的最小节点 68 public BSTreeNode searchMinNode(BSTreeNode x) { 69 while (x.left != null) { 70 x = x.left; 71 } 72 return x; 73 } 74 75 // 查找二叉查找树的最大节点 76 public BSTreeNode searchMaxNode(BSTreeNode x) { 77 while (x.right != null) { 78 x = x.right; 79 } 80 return x; 81 } 82 83 // 插入节点进入二叉查找树 84 public void insert(BSTreeNode k) { 85 BSTreeNode r = rootNode; 86 BSTreeNode p, q = null; 87 p = r; 88 while (p != null) {// while语句可以找到k节点所要插入的位置的父亲节点q 89 q = p; 90 if (p.data > k.data) { 91 p = p.left; 92 } else { 93 p = p.right; 94 } 95 } 96 if (q == null) {// 二叉查找树为空树的情况下,直接插入到根节点,这里的q为已知的k的父亲节点 97 r.data = k.data; 98 } else if (q.data > k.data) {// 插入到父亲节点q的左边 99 q.left = k; 100 } else {// 插入到父亲节点q的右边 101 q.right = k; 102 } 103 } 104 105 // 删除二叉查找树中指定的节点 106 public void delete(BSTreeNode k) {// 分三种情况删除 107 if (k.left == null && k.right == null) {// 第一种情况--没有子节点的情况下 108 BSTreeNode p = parent(k); 109 if (p.left == k) {// 其为父亲节点的左孩子 110 p.left = null; 111 } else if (p.right == k) {// 其为父亲节点的右孩子 112 p.right = null; 113 } 114 } else if (k.left != null && k.right != null) {// 第二种情况--有两个孩子节点的情况下 115 BSTreeNode s = successor(k);// k的后继节点 116 delete(s); 117 k.data = s.data; 118 } else {// 第三种情况--只有一个孩子节点的情况下 119 BSTreeNode p = parent(k); 120 if (p.left == k) { 121 if (k.left != null) { 122 p.left = k.left; 123 } else { 124 p.left = k.right; 125 } 126 } else if (p.right == k) { 127 if (k.left != null) { 128 p.right = k.left; 129 } else { 130 p.right = k.right; 131 } 132 } 133 134 } 135 } 136 137 // 查找节点的前驱节点 138 public BSTreeNode predecessor(BSTreeNode k) { 139 if (k.left != null) { 140 return searchMaxNode(k.left);// 左子树的最大值 141 } 142 BSTreeNode y = parent(k); 143 while (y != null && k == y.left) {// 向上找到最近的一个节点,其父亲节点的右子树包涵了当前节点或者其父亲节点为空 144 k = y; 145 y = parent(y); 146 } 147 return y; 148 } 149 150 // 查找节点的后继节点 151 public BSTreeNode successor(BSTreeNode k) { 152 if (k.right != null) { 153 return searchMinNode(k.right);// 右子树的最小值 154 } 155 BSTreeNode y = parent(k); 156 while (y != null && k == y.right) {// 向上找到最近的一个节点,其父亲节点的左子树包涵了当前节点或者其父亲节点为空 157 k = y; 158 y = parent(y); 159 } 160 return y; 161 } 162 163 // 求出父亲节点,在定义节点类BSTreeNode的时候,没有申明父亲节点,所以这里专门用parent用来输出父亲节点(主要是不想修改代码了,就在这里加一个parent函数吧) 164 public BSTreeNode parent(BSTreeNode k) { 165 BSTreeNode p = rootNode; 166 BSTreeNode tmp = null; 167 while (p != null && p.data != k.data) {// 最后的p为p。data等于k.data的节点,tmp为p的父亲节点 168 if (p.data > k.data) { 169 tmp = p;// 临时存放父亲节点 170 p = p.left; 171 } else { 172 tmp = p;// 临时存放父亲节点 173 p = p.right; 174 } 175 } 176 return tmp; 177 } 178 179 /** 180 * @param args 181 */ 182 public static void main(String[] args) { 183 // TODO Auto-generated method stub 184 int[] A = { 23, 12, 43, 2, 87, 54 }; 185 BSTreeNode searchNode1 = null;// 递归查找到的结果 186 BSTreeNode searchNode2 = null;// 非递归查找到的结果 187 BSTreeNode searchMinNode = null;// 最小节点 188 BSTreeNode searchMaxNode = null;// 最大节点 189 BSTreeNode k = null, l = null, p = null, q = null, m = null, n = null;// 申明6个节点k,l,p,q,m,n 190 191 System.out.print("打印出数组A中的元素"); 192 for (int i = 0; i < A.length; i++) 193 System.out.print(A[i] + ","); 194 195 BinarySortTree bsTree = new BinarySortTree(); 196 197 bsTree.createBSTree(A);// 创建二叉查找树 198 199 System.out.println(); 200 System.out.print("中序遍历构造的二叉查找树:"); 201 bsTree.inOrderBSTree(bsTree.rootNode);// 中序遍历二叉查找树 202 203 k = new BSTreeNode(23, null, null);// 初始化一节点k,左右孩子为null 204 l = new BSTreeNode(17, null, null);// 初始化一节点l,左右孩子为null 205 q = new BSTreeNode(12, null, null);// 初始化一节点q,左右孩子为null 206 // m=bsTree.searchBSTree2(bsTree.rootNode, 207 // k);//从二叉查找树里面查找一个节点,其m.data为k.data(这个m节点在后面用来测试程序) 208 searchNode1 = bsTree.searchBSTree1(bsTree.rootNode, k);// 查询二叉查找树----递归算法 209 searchNode2 = bsTree.searchBSTree2(bsTree.rootNode, k);// 查询二叉查找树----非递归算法 210 m = searchNode2; 211 212 System.out.println(""); 213 System.out.println("递归算法--查找节点域:" + searchNode1.data + "左孩子:" + searchNode1.left.data + "右孩子:" + "查找节点域:" 214 + searchNode1.right.data);// 递归 实现 215 System.out.println("非递归算法--查找节点域:" + searchNode2.data + "左孩子:" + searchNode2.left.data + "右孩子:" + "查找节点域:" 216 + searchNode2.right.data);// 循环实现 217 218 searchMinNode = bsTree.searchMinNode(bsTree.rootNode);// 找到最小节点 219 searchMaxNode = bsTree.searchMaxNode(bsTree.rootNode);// 找到最大节点 220 221 System.out.println("最小节点:" + searchMinNode.data); 222 System.out.println("最大节点:" + searchMaxNode.data); 223 224 bsTree.insert(l);// 把l节点插入到二叉查找树中 225 226 System.out.print("插入l节点(l的data为17)之后的二叉查找树的中序遍历结果:"); 227 bsTree.inOrderBSTree(bsTree.rootNode);// 中序遍历二叉查找树 228 229 p = bsTree.parent(q);// 取q节点的父亲节点 230 231 System.out.println(""); 232 System.out.println("q的父亲节点(q的data为12):" + p.data + "左孩子:" + p.left.data + "右孩子:" + "查找节点域:" + p.right.data);// 这里的左孩子或者右孩子节点可能打印为空,会出现null异常 233 234 bsTree.delete(l); 235 System.out.print("删除l节点(l的data为17)之后的二叉查找树的中序遍历结果:"); 236 bsTree.inOrderBSTree(bsTree.rootNode);// 中序遍历二叉查找树 237 238 n = bsTree.successor(m); 239 System.out.println(""); 240 System.out.println("K的后继节点(k的data为23):" + n.data); 241 242 n = bsTree.predecessor(m); 243 System.out.println("K的前驱节点(k的data为23):" + n.data); 244 } 245 246 } 247 248 // 二叉查找树的节点类 249 class BSTreeNode { 250 int data;// 数据域 251 BSTreeNode right;// 左孩子 252 BSTreeNode left;// 右孩子 253 // 构造二叉查找树的节点 254 255 public BSTreeNode(int data, BSTreeNode right, BSTreeNode left) { 256 this.data = data; // 给二叉树的数据域赋值 257 this.right = right; // 给该节点的右孩子赋值 258 this.left = left; // 给该节点的左孩子赋值 259 } 260 261 }
输出结果为:
1 打印出数组A中的元素23,12,43,2,87,54, 2 中序遍历构造的二叉查找树:2,12,23,43,54,87, 3 递归算法--查找节点域:23左孩子:12右孩子:查找节点域:43 4 非递归算法--查找节点域:23左孩子:12右孩子:查找节点域:43 5 最小节点:2 6 最大节点:87 7 插入l节点(l的data为17)之后的二叉查找树的中序遍历结果:2,12,17,23,43,54,87, 8 q的父亲节点(q的data为12):23左孩子:12右孩子:查找节点域:43 9 删除l节点(l的data为17)之后的二叉查找树的中序遍历结果:2,12,23,43,54,87, 10 K的后继节点(k的data为23):43 11 K的前驱节点(k的data为23):12