概念分析
二叉排序树:对于二叉排序树的任何一个非叶子节点,要求左子结点的值比当前结点的值要小,右子结点的值比当前结点的值要大。如果有相同的值,可以将该结点放在左子结点或者右子结点。
注:下文中所有BST均代表二叉排序树(BinarySortTree)
优点
首先查找的快,我们进行查找时,只需要比较比当前结点小还是大,之后去对应的左右子树进行查询就好。
添加也很快,最多需要比较树的深度次,之后插入进去即可。
图解
我们将数组{7, 3, 10, 12, 5, 1, 9, 2}依次添加进BST中。
建立一个BST
叶子节点
class BinarySortTreeNode{
int value;
BinarySortTreeNode left;
BinarySortTreeNode right;
public BinarySortTreeNode(int value){
this.value = value;
}
/**
* BST中序遍历
*/
public void inorderTraversal(){
if (this.left != null){
this.left.inorderTraversal();
}
System.out.print(this.value + "\t");
if (this.right != null){
this.right.inorderTraversal();
}
}
@Override
public String toString() {
return "BinarySortTreeNode{" +
"value=" + value +
'}';
}
}
BST
public class BinarySortTree {
BinarySortTreeNode root;
}
向BST中添加节点
思路分析
- 当BST为空,将BST的root结点赋值为待添加结点即可
- 若BST不为空,比较其与根结点的值,小的话向左添加,大的话向右,之后再比较其与根节点的左子结点或者右子结点的值,这一步取决于与根节点的大小比较,之后如果下面还有结点,那就一路递归向下比较,最后将其添加使其成为一个叶子结点
代码实现
// 通过叶子结点实现add方法
/**
* BST添加节点
* @param node 待添加的节点
*/
public void add(BinarySortTreeNode node){
if(node == null){
return;
}
// 添加的节点小于此节点的值 我们将其添加到左子结点处
if (node.value < this.value){
if (this.left == null){
this.left = node;
}else {
// 如果其左子结点不为空,那么我们将要插入的节点与其左子结点进行比较
this.left.add(node);
}
}else { // 相等的或者大于的都添加到右子结点
if (this.right == null){
this.right = node;
}else {
this.right.add(node);
}
}
}
// 通过BST调用结点类的add方法
/**
* BST添加节点
* @param node 待被添加的节点
*/
public void add(BinarySortTreeNode node){
if (root == null) {
root = node;
}else {
root.add(node);
}
}
BST遍历
思路分析
遍历的思路与二叉树一样,但是由于BST的性质,我们一般采用中序遍历,因为中序遍历出来的数据是有序的(因为其左边结点的值小于自身,右边结点的值又大于自身)
代码实现
// 结点类的遍历
/**
* BST中序遍历
*/
public void inorderTraversal(){
if (this.left != null){
this.left.inorderTraversal();
}
System.out.print(this.value + "\t");
if (this.right != null){
this.right.inorderTraversal();
}
}
// BST的遍历
/**
* BST实现中序遍历
*/
public void inorderTraversal(){
if (root == null){
System.out.println("BST为空");
}else {
root.inorderTraversal();
}
}
BST删除节点
思路分析
删除节点是比较复杂的,因为我们要保证删除过后依然是一个BST,所以需要在删除时进行很多判断
- 如果该BST只有一个根节点,那么将根节点置空,终止流程
- 找到要被删除的节点,没找到则终止流程
- 因为二叉树是单向的,所以我们还需要找到其父结点
- 判断待删除节点是否是叶子节点(判断其是否有左子结点和右子结点)
- 如果是叶子节点,那么我们将其父结点对应的位置置为空即可,比如,待删除结点是其父结点的左子结点,那么就是parent.left = null,若是右子结点,那么parent.right = null
- 如果不是叶子节点,我们还需要判断其只有一个子树还是有两个子树
- 如果只有一个子树,那么我们进行判断待删除节点是其父结点的左子结点还是右子结点
- 如果是其父结点的左子结点,那么parent.left = targetNode.child(待删除节点那个唯一的子树)
- 如果是右子结点,那么只需要parent.right= targetNode.child(待删除节点那个唯一的子树)
- 如果待删除节点的子树有两个,这时我们将其删除,还需要找到它的左子树中最大的值放到待删除结点的位置,或者右子树中最小的值放到待删除结点的位置。这时将你选择的左边最小或者右边最大的结点删除即可
解释: 第10步为什么这么做呢? 我们顺着分析一下,如果删除当前结点,其有左子树和右子树,我们将该结点删除后,需要补一个比其左子树都大的值并且比其右子树都小的值放到被删除的结点的位置上。而左子树的所有值都小于右子树的最小值,右子树的所有值都大于左子树的最大值,所以我们随便从左子树取一个最大值,之后将其删除,放到待删除结点的位置,或者从右子树中取最小值放过来(这一步相当于我先换位置,之后把左边最大或者右边最小删除),这样删除后仍然是一个BST。
查找待删除的节点
/**
* 查找待删除的节点
* @param value 待删除节点的值
* @return 返回被删除的节点 不存在该节点则返回null
*/
public BinarySortTreeNode search(int value){
if(this.value == value){
return this;
} else if (value < this.value){ // 二叉排序树的左子结点都小于其父结点的值
if (this.left == null){
return null;
}else {
return this.left.search(value);
}
}else { // 如果不小于其父结点的值 那么可以判断在右子结点
if (this.right == null){
return null;
}else {
return this.right.search(value);
}
}
}
查找待删除节点的父结点
/**
* 查找待删除节点的父结点
* @param value 待删除节点的值
* @return 待删除节点的父结点
*/
public BinarySortTreeNode searchParent(int value){
if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){
return this;
} else {
if (value < this.value && this.left != null){
return this.left.searchParent(value);
} else if (value >= this.value && this.right != null){
return this.right.searchParent(value);
}else {
return null;
}
}
}
找到左子树最大的值并删除
public int deleteLeftMax(BinarySortTreeNode node){
BinarySortTreeNode temp = node;
while (temp.right != null){
temp = temp.right;
}
// 找到左侧最大 将其删除
temp = delete(temp.value);
return temp.value;
}
找到右子树最小的值并删除
public int deleteRightMin(BinarySortTreeNode node){
BinarySortTreeNode temp = node;
while (temp.left != null){
temp = temp.left;
}
// 找到右侧最小 将其删除
temp = delete(temp.value);
return temp.value;
}
删除的方法
public BinarySortTreeNode delete(int value){
// 如果当前BST为空 直接结束流程 返回空
if (root == null){
return null;
} else {
// 1. 先去找到待删除的节点
BinarySortTreeNode targetNode = search(value);
// 没有找到待删除节点
if (targetNode == null){
return null;
}
// 如果可以找到待删除节点 并且BST只有一个root
if (root.left == null && root.right == null){
BinarySortTreeNode temp = root;
root = null;
return temp;
}
// 如果可以找到待删除节点 并且BST不只有一个root,那么待删除节点必然有父结点
BinarySortTreeNode parent = searchParent(value);
// 判断待删除节点是否是叶子节点
if(targetNode.left == null && targetNode.right == null){
if (parent.left != null &&parent.left.value == value){
BinarySortTreeNode temp = parent.left;
parent.left = null;
return temp;
} else if (parent.right != null && parent.right.value == value){
BinarySortTreeNode temp = parent.right;
parent.right = null;
return temp;
}else {
return null;
}
}else if (targetNode.left != null && targetNode.right != null){
// 这种情况是待删除节点有两个子结点
int target;
// target = deleteRightMin(targetNode.right);
target = deleteLeftMax(targetNode.left);
targetNode.value = target;
}else {
// 待删除节点只有一个子结点
// 待删除节点的子结点是左子结点
if(targetNode.left != null){
if (parent != null){
if (parent.left.value == value){
parent.left = targetNode.left;
}else {
parent.right = targetNode.left;
}
}else {
root = targetNode.left;
}
}else {
if (parent != null){
if (parent.left.value == value){
parent.left = targetNode.right;
}else {
parent.right = targetNode.right;
}
}else {
root = targetNode.right;
}
}
return targetNode;
}
return null;
}
}
全部代码
package com.nansl.binaryTree;
public class BinarySortTree {
BinarySortTreeNode root;
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree tree = new BinarySortTree();
for (int i = 0; i < arr.length; i++){
BinarySortTreeNode node = new BinarySortTreeNode(arr[i]);
tree.add(node);
}
tree.delete(2);
tree.delete(5);
tree.delete(9);
tree.delete(12);
tree.delete(7);
tree.delete(3);
tree.delete(10);
// tree.delete(1);
tree.inorderTraversal();
}
/**
* 找到左子树中最大的值
* @param node 左子树的根节点
* @return 返回左子树中最大的值
*/
public int deleteLeftMax(BinarySortTreeNode node){
BinarySortTreeNode temp = node;
while (temp.right != null){
temp = temp.right;
}
// 找到左侧最大 将其删除
temp = delete(temp.value);
return temp.value;
}
/**
* 找到右子树中最小的值
* @param node 右子树的根节点
* @return 返回右子树中最小的值
*/
public int deleteRightMin(BinarySortTreeNode node){
BinarySortTreeNode temp = node;
while (temp.left != null){
temp = temp.left;
}
// 找到右侧最小 将其删除
temp = delete(temp.value);
return temp.value;
}
public BinarySortTreeNode delete(int value){
// 如果当前BST为空 直接结束流程 返回空
if (root == null){
return null;
} else {
// 1. 先去找到待删除的节点
BinarySortTreeNode targetNode = search(value);
// 没有找到待删除节点
if (targetNode == null){
return null;
}
// 如果可以找到待删除节点 并且BST只有一个root
if (root.left == null && root.right == null){
BinarySortTreeNode temp = root;
root = null;
return temp;
}
// 如果可以找到待删除节点 并且BST不只有一个root,那么待删除节点必然有父结点
BinarySortTreeNode parent = searchParent(value);
// 判断待删除节点是否是叶子节点
if(targetNode.left == null && targetNode.right == null){
if (parent.left != null &&parent.left.value == value){
BinarySortTreeNode temp = parent.left;
parent.left = null;
return temp;
} else if (parent.right != null && parent.right.value == value){
BinarySortTreeNode temp = parent.right;
parent.right = null;
return temp;
}else {
return null;
}
}else if (targetNode.left != null && targetNode.right != null){
// 这种情况是待删除节点有两个子结点
int target;
// target = deleteRightMin(targetNode.right);
target = deleteLeftMax(targetNode.left);
targetNode.value = target;
}else {
// 待删除节点只有一个子结点
// 待删除节点的子结点是左子结点
if(targetNode.left != null){
if (parent != null){
if (parent.left.value == value){
parent.left = targetNode.left;
}else {
parent.right = targetNode.left;
}
}else {
root = targetNode.left;
}
}else {
if (parent != null){
if (parent.left.value == value){
parent.left = targetNode.right;
}else {
parent.right = targetNode.right;
}
}else {
root = targetNode.right;
}
}
return targetNode;
}
return null;
}
}
public BinarySortTreeNode search(int value){
if (root == null){
return null;
}else {
return root.search(value);
}
}
public BinarySortTreeNode searchParent(int value){
if (root == null){
return null;
}else {
return root.searchParent(value);
}
}
/**
* BST添加节点
* @param node 待被添加的节点
*/
public void add(BinarySortTreeNode node){
if (root == null) {
root = node;
}else {
root.add(node);
}
}
/**
* BST实现中序遍历
*/
public void inorderTraversal(){
if (root == null){
System.out.println("BST为空");
}else {
root.inorderTraversal();
}
}
}
class BinarySortTreeNode{
int value;
BinarySortTreeNode left;
BinarySortTreeNode right;
public BinarySortTreeNode(int value){
this.value = value;
}
/**
* 查找待删除的节点
* @param value 待删除节点的值
* @return 返回被删除的节点 不存在该节点则返回null
*/
public BinarySortTreeNode search(int value){
if(this.value == value){
return this;
} else if (value < this.value){ // 二叉排序树的左子结点都小于其父结点的值
if (this.left == null){
return null;
}else {
return this.left.search(value);
}
}else { // 如果不小于其父结点的值 那么可以判断在右子结点
if (this.right == null){
return null;
}else {
return this.right.search(value);
}
}
}
/**
* 查找待删除节点的父结点
* @param value 待删除节点的值
* @return 待删除节点的父结点
*/
public BinarySortTreeNode searchParent(int value){
if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){
return this;
} else {
if (value < this.value && this.left != null){
return this.left.searchParent(value);
} else if (value >= this.value && this.right != null){
return this.right.searchParent(value);
}else {
return null;
}
}
}
/**
* BST添加节点
* @param node 待添加的节点
*/
public void add(BinarySortTreeNode node){
if(node == null){
return;
}
// 添加的节点小于此节点的值 我们将其添加到左子结点处
if (node.value < this.value){
if (this.left == null){
this.left = node;
}else {
// 如果其左子结点不为空,那么我们将要插入的节点与其左子结点进行比较
this.left.add(node);
}
}else { // 相等的或者大于的都添加到右子结点
if (this.right == null){
this.right = node;
}else {
this.right.add(node);
}
}
}
/**
* BST中序遍历
*/
public void inorderTraversal(){
if (this.left != null){
this.left.inorderTraversal();
}
System.out.print(this.value + "\t");
if (this.right != null){
this.right.inorderTraversal();
}
}
@Override
public String toString() {
return "BinarySortTreeNode{" +
"value=" + value +
'}';
}
}
坚持只为更好的风景。