算法描述
二叉搜索树的定义
二叉搜索树,是指一棵空树或者具有下列性质的二叉树:
1、若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2、若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3、任意节点的左,右子树也分别为二叉搜索树;
4、没有键值相等的节点。
二叉搜索树的插入
插入节点key主要步骤为:
1、判断根节点是否为空,为空、则将待插入节点作为树根插入到空树中。
2、根节点不为空,则与根节点关键字进行比较比较,小于就往左子树找,大于就往右子树找,相等则无需插入。
3、子树的插入过程和在树中的插入过程相同,如此下去,直到把key作为一个新的节点插入到二叉树中,或者发现树中有相同的关键字为止
二叉搜索树的查找
二叉树的根节点指针为current,给定查找的关键字为key,查找过程如下:
1、若root不为空,置初值:current = root,否则查找结束,返回NULL;
2、若key > current.data,且树的右子树非空,则:current = current.right,转至2,否则查找失败,算法结束
3、若key < current.data,且树的左子树非空,则:current = current.left,转至2,否则查找失败,算法结束
二叉搜索树的遍历
遍历指的是按照某种特定的次序来访问二叉搜索树中的每个节点,主要有三种遍历的方法:
前序遍历,“中左右”
中序遍历,“左中右”
后续遍历,“左右中”
上面的口诀“中左右”表示的含义是,先访问根节点,再访问左子,最后访问右子。按照中序遍历的规则将一个二叉搜索树输入,结果为按照正序排列。
二叉搜索树的删除
首先,查找要删除的元素current是否在二叉搜索树中,如果不存在,则返回,否则要删除的节点分以下几种情况:
1、待删除的节点无孩子节点,即删除的是树中的叶子结点:直接删除即可;
2、待删除的节点只有左孩子节点,parent.left/parent.right = current.left:删除该节点且使被删除节点的父节点指向被删除节点的左孩子节点;
3、待删除的节点只有右孩子节点,parent.left/parent.right = current.right:删除该节点且使被删除节点的父节点指向被删除节点的右孩子节点;
4、待删除的节点既有左孩子节点也有右孩子节点,这种情况相对复杂,引入“后继节点”的概念帮助理解
【后继节点】:将一棵二叉搜索树按照中序周游的方式输出,则任一节点的下一个节点就是该节点的后继节点。由此可知,对于待删除节点有两个子节点,我们需要先找出该待删除节点current的后继节点successor,然后将current替换为successor,再处理该节点的删除问题即可。分以下两种情况:
a. 后继节点为待删除节点的右子:只需要将current用successor替换即可,successor.left = current.left
b. 后继节点为带删除节点的右子树的左孩子
代码展示
public class N7二叉搜索树的常规操作实现 {
//二叉搜索树类
private class Node{
//节点类
int data; //数据域
Node right; //右子树
Node left; //左子树
}
//创建一个树类的对象,该对象包含一个树根节点
private Node root; //树根节点
//创建二叉搜索树
//创建树的时候,主要用到了parent,current来记录要插入节点的位置。
public void insert(int key){
Node p = new Node(); //待插入的节点
p.data = key;
//判断根节点是否为空,为空,则该节点为根节点
if(root == null)
root = p;
else{
Node parent = new Node();
Node current = root;
while(true){
parent=current;
if(key > current.data){
current = current.right; //右子树
if(current == null){
parent.right = p;
return;
}
}
else{
current=current.left; //小于根节点,就成为左子树
if(current == null){
parent.left = p;
return;
}
}
}
}
}
//遍历二叉树
public void preOrder(Node root){
//前序遍历,“中左右”
if(root != null){
System.out.println(root.data + " ");
preOrder(root.left);
preOrder(root.right);
}
}
public void inOrder(Node root){
//中序遍历,“左中右”
if(root != null){
inOrder(root.left);
System.out.println(root.data +" ");
inOrder(root.right);
}
}
public void postOrder(Node node){
//后序遍历,“左右中”
if(root != null){
postOrder(root.left);
postOrder(root.right);
System.out.println(root.data + " ");
}
}
//选择以何种方式遍历
public void traverse(int traverseType){
//选择以何种方式遍历
switch (traverseType){
case 1:
System.out.println("preOreder traversal");
preOrder(root);
System.out.println();
break;
case 2:
System.out.println("inOrder traversal");
inOrder(root);
System.out.println();
break;
case 3:
System.out.println("postOrder traversal");
postOrder(root);
System.out.println();
break;
}
}
//查找节点,从树中按关键值查找元素
public Node find(int key){
Node current = root;
//如果找到节点则返回该节点,否则返回null
while(current.data != key){
if(key > current.data)
current = current.right;
else
current = current.left;
if(current == null)
return null;
}
return current;
}
//输出节点的数据域
public void show(Node node){
if(node != null)
System.out.println(node.data);
else
System.out.println("null");
}
//删除操作
//私有函数,用来寻找要删除节点的中序后继节点
private Node getSuccessor(Node delNode){
Node successorParent = delNode; //待删除节点的后继节点的父节点
Node sucessor = delNode; //待删除节点的后继节点
Node current = delNode.right; //待删除的节点
//用来寻找后继节点
while(current != null){
successorParent = sucessor;
sucessor = current;
current = current.left;
}
return sucessor;
}
//节点删除操作
public boolean delete(int key){
Node current = root;
Node parent = new Node();
boolean isRightChild = true;
while (current.data != key){
parent = current;
if(key > current.data){
current = current.right;
isRightChild = true;
}
else {
current = current.left;
isRightChild = false;
}
//没有找到要删除的节点
if(current == null)
return false;
}
//此时current就是要删除的节点,parent为其父节点
//01:要删除节点为叶子结点,没有子节点
if(current.right == null && current.left == null){
if(current == root){
root = null; //整棵树为空
}
else{
if(isRightChild)
parent.right = null;
else
parent.left = null;
}
return true;
}
//02:要删除节点有一个子节点
else if(current.left == null){
if(current == root)
root = current.left;
else if(isRightChild)
parent.right = current.right;
else
parent.left = current.right;
return true;
}
else if(current.right == null){
if(current == root)
root = current.left;
else if(isRightChild)
parent.right = current.left;
else
parent.left = current.left;
return true;
}
//03:删除的节点有两个子节点
else{
//找到要删除节点的后继节点
Node successor = getSuccessor(current);
if(current == root)
root=successor;
else if(isRightChild)
parent.right = successor;
else
parent.left = successor;
successor.left = current.left;
return true;
}
}
public static void main(String[] args) {
N7二叉搜索树的常规操作实现 tree = new N7二叉搜索树的常规操作实现();
int[] a = {39,24,64,23,30,60,1,2,3};
//二叉搜索树的构建:节点插入
for(int i=0 ; i<a.length;i++){
tree.insert(a[i]);
}
//二叉搜索树的遍历:前序1,中序2,后序3
tree.traverse(1);
tree.traverse(2);
//二叉搜索树的查找操作
tree.show(tree.find(23));
tree.show(tree.find(39));
tree.show(tree.find(2));
//二叉搜索树的删除操作
tree.delete(23);
tree.delete(60);
tree.show(tree.find(23));
tree.show(tree.find(64));
}
}