二叉排序树/二叉查找树及其基本操作

一、二叉排序树

二叉排序树,又称二叉查找树(BST(Binary Sort/Search Tree) )或者是一棵空树,或者是具有下列特性的二叉树:
(1)若左子树非空,则左子树上所有结点的值均小于根结点的值
(2)若右子树非空,则右子树删所有结点的值均大于根结点的值
(3)左右子树分别为一棵二叉排序树

根据二叉排序树的定义,左子树结点值<根结点值<右子树结点值,则对二叉排序树进行中序遍历,可以得到一个递增的有序序列。如果有相同的值,可以将该节点放在左子节点或右子节点。
E.g:下图所示的二叉排序树的中序遍历序列为1 2 3 4 5 6 8。
二叉排序树/二叉查找树及其基本操作_第1张图片
二、二叉排序树的基本操作

1、添加子结点

1.1、实现思路

(1)若添加结点为空,则无法添加;
(2)若添加结点的值小于当前结点的值,根据二叉排序树的定义,我们需要向当前结点的左子树添加结点。若当前结点的左子树为空,则直接添加成为当前结点的左子树;若当前结点的左子树不为空,则递归向当前结点的左子树遍历。
(3)若添加结点的值大于等于当前结点的值,根据二叉排序树的定义,我们需要向当前结点的右子树添加结点。若当前结点的右子树为空,则直接添加成为当前结点的右子树;若当前结点的右子树不为空,则递归向当前结点的右子树遍历。

1.2、实现代码

public void add(Node 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);//否则递归向当前结点的右子树遍历
			}
		}
	}

2、查找子结点

2.1 实现思路

(1)若查找值等于当前结点值则直接返回;
(2)若查找值小于当前结点的值,根据二叉排序树的定义,我们需要向当前结点的左子树查找。若当前结点的左子树为空,则无法找到返回为空;若当前结点的左子树不为空,则递归向当前结点的左子树查找。
(3)若查找值大于等于当前结点的值,根据二叉排序树的定义,我们需要向当前结点的右子树查找。若当前结点的右子树为空,则无法找到返回为空;若当前结点的右子树不为空,则递归向当前结点的右子树查找。

2.2 实现代码

public  Node search(int value) {
		if(value==this.value) {//查找值等于当前结点值直接返回
			return this;
		}else if(value<this.value) {//查找值小于当前结点值,根据二叉排序树定义则向左子树查找
			if(this.left==null) {//当前结点的左子树为空则找不到
				return null;
			}
			return this.left.search(value);
		}else {//查找值大于等于当前结点值,根据二叉排序树定义则向右子树查找
			if(this.right==null) {//当前结点的右子树为空则找不到
				return null;
			}
			return this.right.search(value);
		}
	}

3、删除子结点

3.1 实现思路
3.1.1 删除叶子结点

(1)是否为叶子结点的判断条件为:targetNode.left == null && targetNode.right==null,即左右结点均为空;
(2)若targetNode为左子结点则将parent的左子结点置空即删除,即parent.left=null;若targetNode为右子结点则将parent的右子结点置空即删除,即parent.right=null。

3.1.2 删除只有一棵子树的结点

(1)是否为只有一棵子树的结点:targetNode.left ==null || targetNode.right ==null,即targetNode.left 和 targetNode.right 中有且仅有一个为 null;
(2)若删除的结点有左子结点时,当targetNode是 parent 的左子结点,使parent.left=targetNode.left;当targetNode是 parent 的右子结点,使parent.right=targetNode.left;最后使root=targetNode.left。
(3)若删除的结点有右子结点时,当targetNode是 parent 的左子结点,使parent.left=targetNode.right;当targetNode是 parent的右子结点,使parent.right=targetNode.right;最后使root=targetNode.right。

若删除的结点有左子结点时,两种示意图:
(1)当targetNode是 parent 的左子结点
二叉排序树/二叉查找树及其基本操作_第2张图片
(2)当targetNode是 parent的右子结点
二叉排序树/二叉查找树及其基本操作_第3张图片

若删除的结点有右子结点时,两种示意图:
(1)当targetNode是 parent 的左子结点
二叉排序树/二叉查找树及其基本操作_第4张图片
(2)当targetNode是 parent的右子结点
二叉排序树/二叉查找树及其基本操作_第5张图片

3.1.3 删除有两棵子树的结点

(1)先找到删除结点 targetNode,再找到其父结点parent;
(2)利用递归在 targetNode的左子树中寻找值最小的结点,用临时变量temp保存;
(3)删除该最小结点:delete(temp.value),再将 targetNode.value 设置为 temp即可。

3.2 实现代码

//结点类Node
//查找删除结点的父结点
	public Node 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;
			}
		}
	}




//二叉排序树定义类SortTree
//删除子结点
public void delete(int value) {
		if(root==null) {//根结点为空无法操作直接返回
			return;
		}else {
			Node targetNode=search(value);//找到要删除的结点targetNode
			if(targetNode==null) {//若没有找到要删除的结点直接返回
				return;
			}
			if(root.left==null&&root.right==null) {//若二叉排序树只有一个结点即根结点,将根结点置空即删除
				root=null;
				return;
			}
			
			Node parent=searchParent(value);//寻找targetNode的父结点
			
			if(targetNode.left==null&&targetNode.right==null) {//删除结点为叶子结点
				//判断targetNode是父结点的左子结点还是右子结点
				if(parent.left!=null&&parent.left.value==value) {//targetNode为左子结点则将parent的左子结点置空即删除
					parent.left=null;
				}else if(parent.right!=null&&parent.right.value==value) {//targetNode为右子结点则将parent的右子结点置空即删除
					parent.right=null;
				}
			}else if(targetNode.left!=null&&targetNode.right!=null){//删除有两颗子树的节点
				int min=delRightMinTree(targetNode.right);
				targetNode.value=min;
			}else {//删除只有一颗子树的结点
				if(targetNode.left!=null) {//若删除的结点有左子结点
					if(parent!=null) {
						if(parent.left.value==value) {//若targetNode是 parent 的左子结点
							parent.left=targetNode.left;
						}else { //若targetNode是 parent 的右子结点
							parent.right=targetNode.left;
						}
					}else {
						root=targetNode.left;
					}
				}else {//若删除的结点有右子结点
					if(parent!=null) {
						if(parent.left.value==value) {//若targetNode是 parent 的左子结点
							parent.left=targetNode.right;
						}else {//若targetNode是 parent的右子结点
							parent.right=targetNode.right;
						}
					}else {
						root=targetNode.right;
					}
				}
			}
		}
	}
	
	/**
	 * @param node   二叉排序树的根结点
	 * @return       返回的 以node为根结点的二叉排序树的最小结点的值
	 */
	public int delRightMinTree(Node node) {
		Node temp=node;
		while(temp.left!=null) {//循环的查找左子节点,会找到最小值(因为左子树结点值<根结点值<右子树结点值)
			temp=temp.left;
		}
		delete(temp.value);//退出循环时temp指向最小结点,则删除最小值结点,此时该结点必为左叶子结点
		return temp.value;
	}

三、完整实现代码

package Tree;

public class BinarySortTree {
	public static void main(String[] args) {
		int[] arr= { 7, 3, 10, 12, 5, 1, 9};
		 SortTree binarysorttree = new SortTree();
		for(int i=0;i<arr.length;i++) {
			binarysorttree.add(new Node(arr[i]));
		}
		
		System.out.println("初始二叉排序树的中序遍历");
		binarysorttree.inOrder();// 1,3, 5, 7, 9, 10, 12

	    //测试删除叶子结点
		binarysorttree.delete(1);
		System.out.println("删除叶子结点后二叉排序树的中序遍历");
	    binarysorttree.inOrder();//3, 5, 7, 9, 10, 12
	    //测试删除只有一颗子树的结点
	    binarysorttree.delete(3);
	  	System.out.println("删除只有一颗子树的结点后二叉排序树的中序遍历");
	  	binarysorttree.inOrder();//5, 7, 9, 10, 12
	    //测试删除有两颗子树的节点
	  	binarysorttree.delete(10);
	  	System.out.println("删除有两颗子树的节点后二叉排序树的中序遍历");
	  	binarysorttree.inOrder();//5, 7, 9, 12
	}
}

class SortTree{
	private Node root;//二叉排序树根结点
	
	public Node getRoot() {//获取二次排序树根结点
		return root;
	}
	
	public void add(Node node) {//添加子结点
		if(root==null) {//若根结点为空直接让添加结点成为子结点
			root=node;
		}else {
			root.add(node);
		}
	}
	
	public void inOrder() {//中序遍历
		if(root!=null) {//若根结点不为空则调用结点的inOrder
			root.inOrder();
		}else {
			System.out.println("二叉排序树为空,无法遍历!");
		}
	}
	
	public Node search(int value) {//查找子结点
		if(root==null) {
			return null;
		}else{
			return root.search(value);
		}
	}
	
	public Node searchParent(int value) {//查找父结点
		if(root==null) {//根结点为空无法操作返回为空
			return null;
		}else {
			return root.searchParent(value);
		}
	}
	
	//删除子结点
	public void delete(int value) {
		if(root==null) {//根结点为空无法操作直接返回
			return;
		}else {
			Node targetNode=search(value);//找到要删除的结点targetNode
			if(targetNode==null) {//若没有找到要删除的结点直接返回
				return;
			}
			if(root.left==null&&root.right==null) {//若二叉排序树只有一个结点即根结点,将根结点置空即删除
				root=null;
				return;
			}
			
			Node parent=searchParent(value);//寻找targetNode的父结点
			
			if(targetNode.left==null&&targetNode.right==null) {//删除结点为叶子结点
				//判断targetNode是父结点的左子结点还是右子结点
				if(parent.left!=null&&parent.left.value==value) {//targetNode为左子结点则将parent的左子结点置空即删除
					parent.left=null;
				}else if(parent.right!=null&&parent.right.value==value) {//targetNode为右子结点则将parent的右子结点置空即删除
					parent.right=null;
				}
			}else if(targetNode.left!=null&&targetNode.right!=null){//删除有两颗子树的节点
				int min=delRightMinTree(targetNode.right);
				targetNode.value=min;
			}else {//删除只有一颗子树的结点
				if(targetNode.left!=null) {//若删除的结点有左子结点
					if(parent!=null) {
						if(parent.left.value==value) {//若targetNode是 parent 的左子结点
							parent.left=targetNode.left;
						}else { //若targetNode是 parent 的右子结点
							parent.right=targetNode.left;
						}
					}else {
						root=targetNode.left;
					}
				}else {//若删除的结点有右子结点
					if(parent!=null) {
						if(parent.left.value==value) {//若targetNode是 parent 的左子结点
							parent.left=targetNode.right;
						}else {//若targetNode是 parent的右子结点
							parent.right=targetNode.right;
						}
					}else {
						root=targetNode.right;
					}
				}
			}
		}
	}
	
	/**
	 * @param node   二叉排序树的根结点
	 * @return       返回的 以node为根结点的二叉排序树的最小结点的值
	 */
	public int delRightMinTree(Node node) {
		Node temp=node;
		while(temp.left!=null) {//循环的查找左子节点,会找到最小值(因为左子树结点值<根结点值<右子树结点值)
			temp=temp.left;
		}
		delete(temp.value);//退出循环时temp指向最小结点,则删除最小值结点,此时该结点必为左叶子结点
		return temp.value;
	}
}


class Node{
	int value;
	Node left;
	Node right;
	 
	public Node(int value) {//Node的构造函数(无返回值)
		this.value=value;
	}

	@Override
	public String toString() {//重写toString方法
		return "Node [value=" + value + "]";
	}
	
	public void inOrder() {//中序遍历二叉排序树得到有序序列
		if(this.left!=null) {
			this.left.inOrder();
		}
		System.out.println(this);
		if(this.right!=null) {
			this.right.inOrder();
		}
	}
	
	//添加子结点
	public void add(Node 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);//否则递归向当前结点的右子树遍历
			}
		}
	}
	
	//查找子结点
	public  Node search(int value) {
		if(value==this.value) {//查找值等于当前结点值直接返回
			return this;
		}else if(value<this.value) {//查找值小于当前结点值,根据二叉排序树定义则向左子树查找
			if(this.left==null) {//当前结点的左子树为空则找不到
				return null;
			}
			return this.left.search(value);
		}else {//查找值大于等于当前结点值,根据二叉排序树定义则向右子树查找
			if(this.right==null) {//当前结点的右子树为空则找不到
				return null;
			}
			return this.right.search(value);
		}
	}
	
	//查找删除结点的父结点
	public Node 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;
			}
		}
	}
}

运行结果:

初始二叉排序树的中序遍历
Node [value=1]
Node [value=3]
Node [value=5]
Node [value=7]
Node [value=9]
Node [value=10]
Node [value=12]
删除叶子结点后二叉排序树的中序遍历
Node [value=3]
Node [value=5]
Node [value=7]
Node [value=9]
Node [value=10]
Node [value=12]
删除只有一颗子树的结点后二叉排序树的中序遍历
Node [value=5]
Node [value=7]
Node [value=9]
Node [value=10]
Node [value=12]
删除有两颗子树的节点后二叉排序树的中序遍历
Node [value=5]
Node [value=7]
Node [value=9]
Node [value=12]

该初始二叉排序树如下图所示:
二叉排序树/二叉查找树及其基本操作_第6张图片

你可能感兴趣的:(数据结构,Java,数据结构,二叉树,二叉排序树,java)