1、基本介绍
二叉搜索树(Binary Search Tree),又称二叉查找树或二叉排序树,它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 它的左、右子树也分别为二叉搜索树
例如,有数组24,34,29,45,35,5,4,8,6,23,按顺序插入二叉排序树最终结果如下图所示:
2、主要操作
二叉排序树的操作主要有:查找、插入和删除,查找和插入比较简单,这里对删除操作进行较为详细的介绍,步骤如下:
- 叶子节点:直接删除,不影响原树
- 仅仅有左或右子树的节点:节点删除后,将它的左子树或右子树整个移动到删除节点的位置就可以,子承父业
- 既有左又有右子树的节点:找到须要删除的节点p的直接前驱或者直接后继s,用s来替换节点p,然后再删除节点s
接下来基于Java语言实现以上提及的操作,
首先定义一个节点类TreeNode,
static class TreeNode {
int val;
TreeNode left, right;
public TreeNode(int val){
this.val = val;
}
}
查找操作,
/**
* 查找
* @param key 关键字
* @return
*/
public boolean find(int key) {
for (TreeNode node = root;
node != null;
node = node.val > key ? node.left : node.right) {
if (node.val == key) {
return true;
}
}
return false;
}
插入操作,
/**
* 插入
* @param val 要插入的节点值
*/
public void insert(int val) {
for (TreeNode node = root;;) {
if (node.val > val) {
if (node.left == null) {
node.left = new TreeNode(val);
return;
}
node = node.left;
}else {
if (node.right == null) {
node.right = new TreeNode(val);
return;
}
node = node.right;
}
}
}
删除操作,
/**
* 删除
* @param key 关键字
*/
public void delete(int key) {
for (TreeNode node = root, pa = null;
node != null;
pa = node, node = node.val > key ? node.left : node.right) {
if (node.val == key) { // 找到要删除的节点
if (node.left == null) {
if (node.right == null) { // 无孩子节点
if (pa == null) { //BST只有一个节点根节点
root = null;
}else if (pa.left == node) {
pa.left = null;
}else {
pa.right = null;
}
}else { // 有右子节点
if (pa == null) { // 根节点
root = root.right;
}else if (pa.left == node) {
pa.left = node.right;
}else {
pa.right = node.right;
}
}
}else {
if (node.right == null) { // 有左子节点
if (pa == null) {
root = root.left;
}else if (pa.left == node) {
pa.left = node.left;
}else {
pa.right = node.left;
}
}else { // 左右子节点都有
// 找前驱
TreeNode pre, p1;
for (pre = node.left, p1 = null;
pre.right != null;
p1 = pre,pre = pre.right);
node.val = pre.val;
if (p1 != null) {
p1.right = pre.left;
}else {
node.left = pre.left;
}
// 找后继
/*TreeNode next, p2;
for (next = node.right, p2 = null;
next.left != null;
p2 = next,next = next.left);
node.val = next.val;
if (p2 != null) {
p2.left = next.right;
}else {
node.right = next.right;
}*/
}
}
return;
}
}
}
测试方法
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random() * 100);
}
BinarySearchTree bst = new BinarySearchTree(arr[0]);
for (int i = 1; i < arr.length; i++) {
bst.insert(arr[i]);
}
System.out.println("原始数组:\n"+Arrays.toString(arr));
System.out.println("BST的中序遍历:");
BinarySearchTree.inner(bst.root);
bst.delete(arr[0]);
System.out.println();
System.out.println("删除节点"+arr[0]+"后BST的中序遍历:");
BinarySearchTree.inner(bst.root);
}
运行结果:
原始数组:
[16, 15, 25, 33, 57, 88, 90, 95, 6, 65]
BST的中序遍历:
6 15 16 25 33 57 65 88 90 95
删除节点16后BST的中序遍历:
6 15 25 33 57 65 88 90 95
控制逻辑:首先定义一个整形数组,使用随机方法生成在区间[0,100)的数,为了便于观察,这里设置数组的大小为10,接着初始化BinarySearchTree类,该类内部维护了一个根节点root(TreeNode类型),使用插入方法按照顺序依次插入数组内的数字构造二叉搜索树。
由于二叉搜索树的性质,可以得出对一颗二叉搜索树进行中序遍历所得的序列是按照关键字递增升序的序列,为此,可以通过中序遍历访问二叉搜索树来验证我们构造的二叉搜索树是否正确。
对二叉树遍历操作不熟悉的推荐阅读这篇文章详解二叉树的遍历(递归与非递归)
完整BST类代码,
import java.util.Arrays;
public class BinarySearchTree {
static class TreeNode {
int val;
TreeNode left, right;
public TreeNode(int val){
this.val = val;
}
}
public TreeNode root;
public BinarySearchTree(int val){
root = new TreeNode(val);
}
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random() * 100);
}
BinarySearchTree bst = new BinarySearchTree(arr[0]);
for (int i = 1; i < arr.length; i++) {
bst.insert(arr[i]);
}
System.out.println("原始数组:\n"+Arrays.toString(arr));
System.out.println("BST的中序遍历:");
BinarySearchTree.inner(bst.root);
bst.delete(arr[0]);
System.out.println();
System.out.println("删除节点"+arr[0]+"后BST的中序遍历:");
BinarySearchTree.inner(bst.root);
}
/**
* 二叉树的中序遍历
* @param root 根节点
*/
public static void inner(TreeNode root) {
if (root != null) {
inner(root.left);
System.out.print(root.val+" ");
inner(root.right);
}
}
/**
* 查找
* @param key 关键字
* @return
*/
public boolean find(int key) {
for (TreeNode node = root;
node != null;
node = node.val > key ? node.left : node.right) {
if (node.val == key) {
return true;
}
}
return false;
}
/**
* 插入
* @param val 要插入的节点值
*/
public void insert(int val) {
for (TreeNode node = root;;) {
if (node.val > val) {
if (node.left == null) {
node.left = new TreeNode(val);
return;
}
node = node.left;
}else {
if (node.right == null) {
node.right = new TreeNode(val);
return;
}
node = node.right;
}
}
}
/**
* 删除
* @param key 关键字
*/
public void delete(int key) {
for (TreeNode node = root, pa = null;
node != null;
pa = node, node = node.val > key ? node.left : node.right) {
if (node.val == key) { // 找到要删除的节点
if (node.left == null) {
if (node.right == null) { // 无孩子节点
if (pa == null) { //BST只有一个节点根节点
root = null;
}else if (pa.left == node) {
pa.left = null;
}else {
pa.right = null;
}
}else { // 有右子节点
if (pa == null) { // 根节点
root = root.right;
}else if (pa.left == node) {
pa.left = node.right;
}else {
pa.right = node.right;
}
}
}else {
if (node.right == null) { // 有左子节点
if (pa == null) {
root = root.left;
}else if (pa.left == node) {
pa.left = node.left;
}else {
pa.right = node.left;
}
}else { // 左右子节点都有
// 找前驱
TreeNode pre, p1;
for (pre = node.left, p1 = null;
pre.right != null;
p1 = pre,pre = pre.right);
node.val = pre.val;
if (p1 != null) {
p1.right = pre.left;
}else {
node.left = pre.left;
}
// 找后继
/*TreeNode next, p2;
for (next = node.right, p2 = null;
next.left != null;
p2 = next,next = next.left);
node.val = next.val;
if (p2 != null) {
p2.left = next.right;
}else {
node.right = next.right;
}*/
}
}
return;
}
}
}
}
至此,二叉搜索树的概念及相关操作介绍到这里。