概述
二叉搜索树,也成为二叉查找树或者二叉排序树,这是一种特殊的二叉树。在二叉搜索树中的数据结构中,我们可以通过链表来表示。对该树种的节点,需要定义关键字data,父节点parent,左孩子节点leftChild,右孩子节点rightChild。
在二叉搜索树中,对任何节点node,其左子树中的所有节点的data的最大值不会超过node.data,其右子树的所有节点的data不会小于node.data。
二叉搜索树的简单操作
查找节点
在二叉搜索树中,查找一个节点k的过程需要从根节点开始。对于每次比较的节点x,如果k.note小于x.node,查找将从x的左子树继续,否则从右子树中遍历,直到k.note等于x.node,那么查找终止。
具体代码如下:
/**
* 查找指定节点
*
* @param node
*/
public TreeNode get(int data, TreeNode node) {
if (node == null) {
return null;
}
if (data == node.data) {
return node;
}
if (data > node.data) {
return get(data, node.rightChild);
} else {
return get(data, node.leftChild);
}
}
/**
* 查询指定结点
*
* @param data
* @return
*/
public TreeNode get(int data) {
return get(data, root);
}
添加节点
想要在二叉搜索树中添加一个节点k,我们需要通过遍历的方式来完成。这个过程也是从根节点开始。
如果添加节点的值大于根节点的值,那么继续和根节点的右子树的根节点比较,否者和左子树的根节点比较,直到遍历到树的叶子节点。如果值大于叶子节点的值,那么该节点将作为叶子节点的右孩子节点插入到树中,否则作为叶子节点的左孩子节点插入,所以要插入的节点最终都是已叶子节点的形式插入到树中的。
具体代码如下:
/**
* 添加节点
*
* @param data
*/
public void put(int data) {
// 防止插入一样的节点
if (get(data) != null) {
return;
}
// 创建即将插入的节点
TreeNode newNode = new TreeNode(data, null, null, null);
// 每次添加节点都从root节点开始
TreeNode currNode = root;
// 创建根节点
if (currNode == null) {
root = newNode;
return;
}
while (currNode != null) {
// 判断插入值和当前节点值的大小
if (newNode.data >= currNode.data) {
/*
* 当currNode的值比较大时,我们需要判断当前节点的右孩子和当前值得大小
*/
if (currNode.rightChild == null) {
newNode.parent = currNode;
currNode.rightChild = newNode;
return;
} else {
currNode = currNode.rightChild;
continue;
}
} else {
if (currNode.leftChild == null) {
newNode.parent = currNode;
currNode.leftChild = newNode;
return;
} else {
currNode = currNode.leftChild;
continue;
}
}
}
}
删除节点
从一棵树中,删除一个节点deleteNode,情况稍微复杂一点,这里我们可以分为以下几种情况。
- 如果deleteNode没有孩子节点,那么直接删除即可
- 如果deleteNode有一个孩子节点,只需要将孩子节点替代deleteNode的位置即可
- 如果deleteNode有两个孩子节点,我们在删除的时候需要寻找deleteNode的后继节点,让deleteNode的值替换成后继节点的值,并删除后继节点。
以上这三种情况是在deleteNode不等于根节点root时的情况,如果deleteNode等于root,那么还有其他的处理方式,这个在下面的代码中会有体现。
具体代码如下:
/**
* 删除节点
*
*/
public boolean delete(int data) {
/*
* 删除结点有以下几种情况:1、如果该结点没有孩子结点,那么直接删除(设置该结点的parent为null)
* 2、如果该结点只有一个孩子,那么直接使用孩子结点替换该节点即可 3、如果该结点有两个孩子,寻找后继结点替换该节点
*/
TreeNode deleteNode = get(data);
if (deleteNode == null) {
System.out.println("该结点不存在");
// throw new IllegalArgumentException("该节点");
return false;
} else if (root == deleteNode) {
return deleteRoot();
}
TreeNode leftChild = deleteNode.leftChild;
TreeNode rightChild = deleteNode.rightChild;
TreeNode parent = deleteNode.parent;
// 1、没有孩子节点
if (leftChild == null && rightChild == null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = null;
} else {
parent.rightChild = null;
}
deleteNode.parent = null;
return true;
}
// 2、只有一个左孩子节点
if (leftChild != null && rightChild == null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = deleteNode.leftChild;
} else {
parent.rightChild = deleteNode.leftChild;
}
deleteNode.parent = null;
return true;
}
// 2、只有一个右孩子节点
if (leftChild == null && rightChild != null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = rightChild;
} else {
parent.rightChild = rightChild;
}
deleteNode.parent = null;
return true;
}
// 3、存在两个孩子节点
if (leftChild != null && rightChild != null) {
// 获取要删除的节点的后继节点
TreeNode nextNode = getNextNode(deleteNode);
delete(nextNode.data);
deleteNode.data = nextNode.data;
return true;
}
return false;
}
二叉搜索树的代码实现
package structdemo;
/**
* 二叉排序树、二叉搜索树
*
* @author zhangke
*
*/
public class BinarySearchTree {
public static void main(String[] args) {
BinarySearchTree createTree = BinarySearchTree.createTree();
createTree.delete(4);
System.out.println("前序遍历:");
createTree.preOrder(createTree.getRoot());
System.out.println("\n\n中序遍历:");
createTree.midOrder(createTree.getRoot());
}
/**
* 创建二叉搜索树
*
* @return
*/
public static BinarySearchTree createTree() {
BinarySearchTree tree = new BinarySearchTree();
int[] data = { 4, 2, 1, 0, 3, 6, 5, 10, 8, 7, 9 };
for (int i = 0; i < 11; i++) {
tree.put(data[i]);
}
return tree;
}
/**
* 树的根节点
*/
private TreeNode root;
public TreeNode getRoot() {
return root;
}
/**
* 添加节点
*
* @param data
*/
public void put(int data) {
// 防止插入一样的节点
if (get(data) != null) {
return;
}
// 创建即将插入的节点
TreeNode newNode = new TreeNode(data, null, null, null);
// 每次添加节点都从root节点开始
TreeNode currNode = root;
// 创建根节点
if (currNode == null) {
root = newNode;
return;
}
while (currNode != null) {
// 判断插入值和当前节点值的大小
if (newNode.data >= currNode.data) {
/*
* 当currNode的值比较大时,我们需要判断当前节点的右孩子和当前值得大小
*/
if (currNode.rightChild == null) {
newNode.parent = currNode;
currNode.rightChild = newNode;
return;
} else {
currNode = currNode.rightChild;
continue;
}
} else {
if (currNode.leftChild == null) {
newNode.parent = currNode;
currNode.leftChild = newNode;
return;
} else {
currNode = currNode.leftChild;
continue;
}
}
}
}
/**
* 查找指定节点
*
* @param node
*/
public TreeNode get(int data, TreeNode node) {
if (node == null) {
return null;
}
if (data == node.data) {
return node;
}
if (data > node.data) {
return get(data, node.rightChild);
} else {
return get(data, node.leftChild);
}
}
/**
* 查询指定结点
*
* @param data
* @return
*/
public TreeNode get(int data) {
return get(data, root);
}
/**
* 删除节点
*
*/
public boolean delete(int data) {
/*
* 删除结点有以下几种情况:1、如果该结点没有孩子结点,那么直接删除(设置该结点的parent为null)
* 2、如果该结点只有一个孩子,那么直接使用孩子结点替换该节点即可 3、如果该结点有两个孩子,寻找后继结点替换该节点
*/
TreeNode deleteNode = get(data);
if (deleteNode == null) {
System.out.println("该结点不存在");
// throw new IllegalArgumentException("该节点");
return false;
} else if (root == deleteNode) {
return deleteRoot();
}
TreeNode leftChild = deleteNode.leftChild;
TreeNode rightChild = deleteNode.rightChild;
TreeNode parent = deleteNode.parent;
// 1、没有孩子节点
if (leftChild == null && rightChild == null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = null;
} else {
parent.rightChild = null;
}
deleteNode.parent = null;
return true;
}
// 2、只有一个左孩子节点
if (leftChild != null && rightChild == null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = deleteNode.leftChild;
} else {
parent.rightChild = deleteNode.leftChild;
}
deleteNode.parent = null;
return true;
}
// 2、只有一个右孩子节点
if (leftChild == null && rightChild != null) {
if (deleteNode == parent.leftChild) {
parent.leftChild = rightChild;
} else {
parent.rightChild = rightChild;
}
deleteNode.parent = null;
return true;
}
// 3、存在两个孩子节点
if (leftChild != null && rightChild != null) {
// 获取要删除的节点的后继节点
TreeNode nextNode = getNextNode(deleteNode);
delete(nextNode.data);
deleteNode.data = nextNode.data;
return true;
}
return false;
}
/**
* 删除根节点
*
* @param data
* @return
*/
private boolean deleteRoot() {
TreeNode nextNode = getNextNode(root);
// 1、后继节点为空,左孩子节点为跟节点
if (nextNode == null) {
root = root.leftChild;
if (root.leftChild != null) {
root.leftChild.parent = null;
}
return true;
}
int data = nextNode.data;
delete(data);
root.data = data;
return true;
}
/**
* 获取后继节点
*
* @param deleteNode
* @return
*/
private TreeNode getNextNode(TreeNode node) {
if (node == null) {
throw null;
}
// 获取节点的后继节点,只要获取右子树的最小节点即可
return getTreeMinNode(node.rightChild);
}
/**
* 获取最小节点
*
* @param node
* @return
*/
public TreeNode getTreeMinNode(TreeNode node) {
if (node == null) {
return null;
}
TreeNode minNode = node;
while (minNode.leftChild != null) {
minNode = minNode.leftChild;
}
return minNode;
}
/**
* 获取最大节点
*
* @param node
* @return
*/
public TreeNode getTreMaxNode(TreeNode node) {
if (node == null) {
return null;
}
TreeNode maxNode = node;
while (maxNode.rightChild != null) {
maxNode = maxNode.rightChild;
}
return maxNode;
}
/**
* 获取一个树的最小结点
*
* @param node
*/
public TreeNode getTreeMinNode() {
return getTreeMinNode(root);
}
/**
* 获取一个树的最大结点
*
* @param node
*/
public TreeNode getTreMaxNode() {
return getTreMaxNode(root);
}
/**
* 获取结点个数
*
* @return
*/
public int getSize() {
return getSize(root);
}
/**
* 获取树的节点个数
*
* @return
*/
private int getSize(TreeNode node) {
if (node == null) {
return 0;
}
return 1 + getSize(node.leftChild) + getSize(node.rightChild);
}
/**
* 中序遍历
*
* left->parent->right
*/
public void midOrder(TreeNode node) {
if (node == null) {
return;
}
midOrder(node.leftChild);
System.out.print(node.data + "\t");
midOrder(node.rightChild);
}
/**
* 前序遍历
*
* parent->left->right
*/
public void preOrder(TreeNode node) {
if (node == null) {
return;
}
System.out.print(node.data + "\t");
preOrder(node.leftChild);
preOrder(node.rightChild);
}
/**
* 树的节点
*/
public class TreeNode {
public int data;
public TreeNode parent;
public TreeNode leftChild;
public TreeNode rightChild;
public TreeNode(int data) {
this.data = data;
}
public TreeNode(int data, TreeNode parent, TreeNode leftChild, TreeNode rightChild) {
this.data = data;
this.parent = parent;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
}