本文目录
前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(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}