二叉查找树的定义:
二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树:
1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字;
2. 若右子树非空,则右子树上所有节点关键字值均大于根节点的关键字;
3. 左、右子树本身也分别是一颗二叉查找树。
二叉查找树的实现,功能有:
1. 用一个数组去构建二叉查找树
2. 二叉查找树的中序遍历和层次遍历
3. 插入节点
4. 查找节点
5. 查找二叉树中的最大值和最小值
6. 得到节点的直接父节点
7. 得到节点的直接前驱和直接后继节点
8. 删除节点
树节点TreeNode的定义见:
1 import java.lang.Integer; 2 import java.util.LinkedList; 3 import java.util.Queue; 4 import java.util.Scanner; 5 6 /* 7 * 二叉排序树(二叉查找树)的实现 8 */ 9 public class BinarySearchTree { 10 private TreeNoderoot; //根节点 11 12 public BinarySearchTree(){ 13 root = null; 14 } 15 16 //用一个数组去构建二叉查找树 17 public TreeNode buildBST(Integer[] array){ 18 if(array.length == 0){ 19 return null; 20 }else{ 21 root = null; //初始化树为空树 22 for(int i=0; i insertNode(TreeNode node, Integer data){ 31 if(node == null){ //原树为空,新插入的记录为根节点 32 node = new TreeNode (data,null,null); 33 }else{ 34 if(node.getData() == data){ //树中存在相同关键字的结点,什么也不做 35 36 }else{ 37 if(node.getData() > data){ //根节点>插入数据,插入到左子树中 38 node.setLchild(insertNode(node.getLchild(),data)); 39 }else{ //根节点 <插入数据,插入到右子树中 40 node.setrchild(insertnode(node.getrchild(),data)); 41 } 42 43 44 return node; 45 46 47 二叉查找树的中序遍历,可以得到一个递增的有序数列 48 public void inorder(treenode node){ 49 if(node != null){ 50 inOrder(node.getLchild()); 51 System.out.print(node.getData()+" "); 52 inOrder(node.getRchild()); 53 } 54 } 55 //二叉查找树的层次遍历 56 public void levelOrder(TreeNode root){ 57 Queue 插入数据,插入到右子树中>> nodeQueue = new LinkedList >(); 58 TreeNode node = null; 59 nodeQueue.add(root); //将根节点入队 60 while(!nodeQueue.isEmpty()){ //队列不空循环 61 node = nodeQueue.peek(); 62 System.out.print(node.getData()+" "); 63 nodeQueue.poll(); //队头元素出队 64 if(node.getLchild() != null){ //左子树不空,则左子树入队列 65 nodeQueue.add(node.getLchild()); 66 } 67 if(node.getRchild() != null){ //右子树不空,则右子树入队列 68 nodeQueue.add(node.getRchild()); 69 } 70 } 71 } 72 73 //查找数据域为data的结点,若不存在,返回null 74 public TreeNode searchNode(TreeNode node, Integer data){ 75 while(node != null && node.getData() != data){ 76 if(node.getData() > data){ 77 node = node.getLchild(); //根节点>数据,向左走 78 }else{ 79 node = node.getRchild(); //根节点 <数据,向右走 80 } 81 82 return node; 83 84 85 查找最大值:不断地寻找右子节点 86 public treenode getMaxData(TreeNode node){ 87 if(node.getRchild() == null){ 88 return node; 89 }else{ 90 return getMaxData(node.getRchild()); 91 } 92 } 93 94 //查找最小值:不断地寻找左子节点 95 public TreeNode 数据,向右走>getMinData(TreeNode node){ 96 if(node.getLchild() == null){ 97 return node; 98 }else{ 99 return getMinData(node.getLchild());100 }101 }102 103 //得到数据域为data的结点的直接父节点parentNode104 public TreeNode getParentNode(TreeNode root, Integer data){105 TreeNode parentNode = root;106 if(parentNode.getData() == data){ //根节点的父节点返回为null107 return null;108 }109 while(parentNode != null){110 //查找当前节点的父节点的左右子节点,若是相等,则返回该父节点111 if((parentNode.getLchild() != null && parentNode.getLchild().getData() == data) || 112 (parentNode.getRchild() != null && parentNode.getRchild().getData() == data)){113 return parentNode;114 }else{115 if(parentNode.getData() > data){ //向左查找父节点116 parentNode = parentNode.getLchild();117 }else{118 parentNode = parentNode.getRchild(); //向右查找父节点119 }120 } 121 }122 return null;123 }124 125 /**126 * 得到结点node的直接前趋127 * a.该节点左子树不为空:其前驱节点为其左子树的最大元素128 * b.该节点左子树为空: 其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点129 * (就是一直往其parent找,出现左拐后的那个祖先节点) 130 */131 public TreeNode getPrecessor(TreeNode root,TreeNode node){132 if(node == null){133 return null;134 }135 //a.该节点左子树不为空:其前驱节点为其左子树的最大元素136 if(node.getLchild() != null){137 return getMaxData(node.getLchild());138 }else{ //b.该节点左子树为空: 其前驱节点为其祖先节点(递归) 139 TreeNode parentNode = getParentNode(root, node.getData());140 while(parentNode != null && node == parentNode.getLchild()){141 node = parentNode;142 parentNode = getParentNode(root, parentNode.getData());143 }144 return parentNode;145 }146 }147 148 /**149 * 得到结点node的直接后继(后继节点就是比要删除的节点的关键值要大的节点集合中的最小值)150 * a.该节点右子树不为空,其后继节点为其右子树的最小元素151 * b.该节点右子树为空,则其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点,152 * 就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点: 153 */154 public TreeNode getSuccessor(TreeNode root,TreeNode node){155 if(node == null){156 return null;157 }158 //a.该节点右子树不为空,其后继节点为其右子树的最小元素159 if(node.getRchild() != null){160 return getMinData(node.getRchild());161 }else{ //b.该节点右子树为空,则其后继节点为其最高祖先节点(递归) 162 TreeNode parentNode = getParentNode(root, node.getData());163 while(parentNode != null && node == parentNode.getRchild()){164 node = parentNode;165 parentNode = getParentNode(root, parentNode.getData());166 }167 return parentNode;168 }169 }170 171 /**172 * 删除数据域为data的结点173 * 按三种情况处理:174 * a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质175 * b.如果节点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,代替z的位置176 * c.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,177 * 然后从二叉查找树中删去这个直接后继(或直接前驱),这样就转换为第一或第二种情况178 * @param node 二叉查找树的根节点179 * @param data 需要删除的结点的数据域180 * @return181 */182 public boolean deleteNode(TreeNode node, Integer data){183 if(node == null){ //树为空184 throw new RuntimeException("树为空!");185 }186 TreeNode delNode= searchNode(node, data); //搜索需要删除的结点187 TreeNode parent = null;188 if(delNode == null){ //如果树中不存在要删除的关键字189 throw new RuntimeException("树中不存在要删除的关键字!");190 }else{191 parent = getParentNode(node,data); //得到删除节点的直接父节点192 //a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质 193 if(delNode.getLchild()==null && delNode.getRchild()==null){ 194 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子195 parent.setLchild(null);196 }else{ //被删除节点为其父节点的右孩子197 parent.setRchild(null);198 }199 return true;200 }201 //b1.如果节点z只有一颗左子树,则让z的子树成为z父节点的子树,代替z的位置202 if(delNode.getLchild()!=null && delNode.getRchild()==null){203 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子204 parent.setLchild(delNode.getLchild()); 205 }else{ //被删除节点为其父节点的右孩子206 parent.setRchild(delNode.getLchild());207 }208 delNode.setLchild(null); //设置被删除结点的左孩子为null209 return true;210 }211 //b2.如果节点z只有一颗右子树,则让z的子树成为z父节点的子树,代替z的位置212 if(delNode.getLchild()==null && delNode.getRchild()!=null){213 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子214 parent.setLchild(delNode.getRchild());215 }else{ //被删除节点为其父节点的右孩子216 parent.setRchild(delNode.getRchild());217 }218 delNode.setRchild(null); //设置被删除结点的右孩子为null219 return true;220 }221 //c.若结点z有左、右两颗子树,则删除该结点的后继结点,并用该后继结点取代该结点222 if(delNode.getLchild()!=null && delNode.getRchild()!=null){223 TreeNode successorNode = getSuccessor(node,delNode); //得到被删除结点的后继节点224 deleteNode(node,successorNode.getData()); //删除该结点的后继结点225 delNode.setData(successorNode.getData()); //用该后继结点取代该结点226 return true;227 }228 }229 return false;230 }231 232 233 public static void main(String args[]){234 Scanner input = new Scanner(System.in);235 Integer[] array = {8,3,10,1,6,14,4,7,13};236 BinarySearchTree bst = new BinarySearchTree();237 TreeNode root = bst.buildBST(array);238 System.out.print("层次遍历:"); 239 bst.levelOrder(root);240 System.out.print("\n"+"中序遍历:"); 241 bst.inOrder(root); 242 System.out.println();243 System.out.print("得到最大值:"); 244 System.out.println(bst.getMaxData(root).getData()); 245 System.out.print("得到最小值:"); 246 System.out.println(bst.getMinData(root).getData()); 247 System.out.print("向二叉查找树中插入一个节点,请输入需插入节点的数据域:");248 int data = input.nextInt();249 System.out.print("插入节点"+ data +"后,中序遍历的结果:");250 root = bst.insertNode(root, data); 251 bst.inOrder(root); 252 System.out.println("\n"+"在二叉查找树中查找元素,"+"请输入需要查找的结点值:");253 data = input.nextInt();254 if(bst.searchNode(root, data) == null){255 System.out.println("false");256 }else{257 System.out.println("true");258 }259 System.out.println("查找节点的直接父节点,"+"请输入需要查找的结点值:");260 data = input.nextInt();261 System.out.print("节点"+ data +"的父节点是:");262 if(bst.getParentNode(root, data) == null){263 System.out.println("null");264 }else{265 System.out.println(bst.getParentNode(root, data).getData());266 }267 System.out.println("删除结点,"+"请输入需要删除的结点值:");268 data = input.nextInt();269 if(bst.deleteNode(root, data)){270 System.out.print("删除结点后的层次遍历:"); 271 bst.levelOrder(root);272 System.out.print("\n"+"删除结点后的中序遍历:"); 273 bst.inOrder(root); 274 } 275 } 276 }
程序运行结果:
层次遍历:8 3 10 1 6 14 4 7 13 中序遍历:1 3 4 6 7 8 10 13 14 得到最大值:14得到最小值:1向二叉查找树中插入一个节点,请输入需插入节点的数据域:15插入节点15后,中序遍历的结果:1 3 4 6 7 8 10 13 14 15 在二叉查找树中查找元素,请输入需要查找的结点值:4true查找节点的直接父节点,请输入需要查找的结点值:10节点10的父节点是:8删除结点,请输入需要删除的结点值:4删除结点后的层次遍历:8 3 10 1 6 14 7 13 15 删除结点后的中序遍历:1 3 6 7 8 10 13 14 15
1. 插入节点insertNode():
1 //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点 2 public TreeNodeinsertNode(TreeNode node, Integer data){ 3 TreeNode newNode = new TreeNode (data,null,null); 4 TreeNode tmpNode = node; //遍历节点 5 TreeNode pnode = null; //记录当前节点的父节点 6 7 if(node == null){ //原树为空,新插入的记录为根节点 8 node = newNode; 9 return node;10 }11 while(tmpNode != null){12 pnode = tmpNode;13 if(tmpNode.getData() == data){ //树中存在相同关键字的结点,什么也不做14 return node;15 }else{16 if(tmpNode.getData() > data){ //根节点>插入数据,插入到左子树中17 tmpNode = tmpNode.getLchild();18 }else{ //根节点 <插入数据,插入到右子树中19 tmpnode="tmpNode.getRchild();20" }21 }22 }23 if(pnode.getdata()> data){24 pnode.setLchild(newNode);25 }else{26 pnode.setRchild(newNode);27 } 28 return node;29 } 插入数据,插入到右子树中19>
2. 二叉查找树的中序遍历:
1 //二叉查找树的中序遍历LNR,可以得到一个递增的有序数列 2 public void inOrder(TreeNodenode){ 3 Stack > nodeStack = new Stack >(); 4 TreeNode tempNode = node; //遍历指针 5 while(tempNode != null || !nodeStack.isEmpty()){ 6 if(tempNode != null){ 7 nodeStack.push(tempNode); 8 tempNode = tempNode.getLchild(); 9 }else{10 tempNode = nodeStack.pop();11 System.out.print(tempNode.getData() + " ");12 tempNode = tempNode.getRchild();13 }14 }15 }
3. 得到二叉查找树的最大值和最小值:
1 //查找最大值:不断地寻找右子节点 2 public TreeNodegetMaxData(TreeNode node){ 3 TreeNode tempNode = node; 4 while(tempNode.getRchild()!=null){ 5 tempNode = tempNode.getRchild(); 6 } 7 return tempNode; 8 } 9 10 //查找最小值:不断地寻找左子节点11 public TreeNode getMinData(TreeNode node){12 TreeNode tempNode = node;13 while(tempNode.getLchild() != null){14 tempNode = tempNode.getLchild();15 }16 return tempNode;17 }