二分搜索树的基本实现
树是一种常见的数据结构,而二分搜索树又是一种最简单的树。接下来我将手写一棵二分搜索树,其中包含了
- 二分搜索树的增删改查
- 二分搜索树的前中后序遍历,其中前序遍历提供了非递归写法
- 二分搜索树的层序遍历
由于是底层实现,所以本文还是以递归代码为主
在这里主要想讲一下树的删除操作
我不会一上来就直接删除二分搜索树中的任意元素,首先我们可以先实现容易的删除。比如先删除树中的最大值或者最小值.
删除二分搜索树的最大值或最小值
为了方便,我们定义一个查找二分搜索树最大值或者最小值的方法
以删除二分搜索树的最大值为例。
如果该节点是叶子节点,那么很简单,我们直接将其删除即可,维护一下size变量即可。
如果该节点有左孩子,我们直接将他的左孩子进行挂接即可。
有了上述删除最大值或者最小值的思路,那么接下来我们进行删除二分搜索树中任意节点的实现。
- 如果删除的该节点只有左孩子,或者只有右孩子,那么与我们上面的思路是一样的,只需要将左孩子或者右孩子进行挂接就可以了。当然,如果是叶子节点,我们可以理解为只有左孩子或者只有右孩子,即叶子节点并不需要单独处理。
- 如果删除的该节点即有左孩子,又有右孩子。那么就是删除的重点了。在这里我们采用Hibbard deletion删除。
如果我们要删除的节点是58,此时我们找出58的后继节点,即第一个大于他的节点。很简单,我们只用去该节点的右子树中寻找最小元素即可,我们在上面封装的方法就派上了用场。
- 我们将删除该后继节点的右子树,挂接到该后继节点上
- 然后将58这个节点的左子树,挂接到后继节点上
- 然后将58这个节点删除即可
public class BinarySearchTree<E extends Comparable<E>> {
private class Node{
public E e;
public Node left;
public Node right;
public Node(E e){
this.e=e;
left=null;
right=null;
}
}
private Node root;
/**
* the number of node
*/
private int size;
public BinarySearchTree(){
size=0;
root=null;
}
/**
*
* @return size
*/
public int getSize(){
return size;
}
/**
*
* @return isEmpty
*/
public boolean isEmpty(){
return size==0;
}
/**
* insert element e to the tree
* @param e
*/
public void add(E e){
add(root,e);
}
/**
* recursion
* @param node
* @param e
* @return the root of the tree
*/
private Node add(Node node,E e){
if(node==null) {
size++;
return new Node(e);
}
// the number is small than the node.val
if(e.compareTo(node.e)<0)
node.left=add(node.left,e);
//the number is big than the node.val
if(e.compareTo(node.e)>0)
node.right=add(node.right,e);
return node;
}
/**
* is contains the element
* @param e
* @return
*/
public boolean contains(E e){
return contains(root,e);
}
/**
* recursion
* @param node
* @param e
* @return
*/
private boolean contains(Node node,E e){
if(node==null) return false;
if(e.compareTo(node.e)<0)
return contains(node.left,e);
else if(e.compareTo(node.e)>0)
return contains(node.right,e);
else{
return true;
}
}
/**
* preorder traversal
*/
public void preOrder(){
preOrder(root);
}
/**
* recursion
* @param node
*/
private void preOrder(Node node){
if(node==null) return;
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
/**
* preorder traversal and no recursion
*/
public void preOrderNR(){
if(root==null) return;
Stack<Node>stack=new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
Node cur=stack.pop();
System.out.println(cur.e);
if(cur.right!=null)
stack.push(cur.right);
if(cur.left!=null)
stack.push(cur.left);
}
}
/**
* inorder traversal
*/
public void inOrder(){
inOrder(root);
}
/**
* recursion
* @param node
*/
private void inOrder(Node node) {
if(node==null) return;
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
/**
* postOrder traversal
*/
public void postOrder(){
postOrder(root);
}
/**
* recursion
* @param node
*/
private void postOrder(Node node){
if(node==null) return;
postOrder(node.left);
postOrder(node.right);
System.out.println(node.e);
}
/**
* sequenceorder tavaersal
* use FIFO(first in first out)
*/
public void levelOrder(){
if(root==null) return;
Queue<Node>queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
Node cur=queue.remove();
System.out.println(cur.e);
if(cur.left!=null) queue.add(cur.left);
if(cur.right!=null) queue.add(cur.right);
}
}
/**
* search the minimum of the tree
* @return
*/
public E minimum(){
if(size==0)
throw new IllegalArgumentException("BinarySearch Tree is empty");
Node node=minimum(root);
return node.e;
}
/**
* recursion search
* @param node
* @return node
*/
private Node minimum(Node node){
if(node.left==null)
return node;
return minimum(node.left);
}
/**
* search the maximum of the tree
* @return
*/
public E maximum(){
if(size==0)
throw new IllegalArgumentException("BinarySearch Tree is empty");
Node node = maximum(root);
return node.e;
}
/**
* recursion search
* @param node
* @return node
*/
private Node maximum(Node node){
if(node.right==null)
return node;
return maximum(node.right);
}
/**
* delete the node of the minimum element
* @return the element
*/
public E removeMin(){
E minimum = minimum();
root=removeMin(root);
return minimum;
}
/**
* recursion process
* @param node
* @return node
*/
private Node removeMin(Node node){
//the border of recursion
if(node.left==null){
Node rightNode=node.right;
// make the node out of the tree
node.right=null;
size--;
return rightNode;
}
node.left=removeMin(node.left);
return node;
}
/**
* delete the node of the maximum element
* @return the element
*/
public E removeMax(){
E maximum=maximum();
root=removeMax(root);
return maximum;
}
/**
* recursion process
* @param node
* @return node
*/
private Node removeMax(Node node){
//the border of recursion
if(node.right==null){
Node leftNode=node.left;
// make the node out of the tree
node.left=null;
size--;
return leftNode;
}
node.right=removeMax(node.right);
return node;
}
/**
* delete the node of element
* @param e
*/
public void remove(E e){
root= remove(e,root);
}
/**
* recursion process
* @param e
* @param node
* @return node
*/
private Node remove(E e, Node node) {
if(node==null)
return null;
if(e.compareTo(node.e)<0){
node.left=remove(e,node.left);
return node;
}else if(e.compareTo(node.e)>0){
node.right=remove(e,node.right);
return node;
}else{
// e==node.e
if(node.left==null){
Node rightNode=node.right;
node.right=null;
size--;
return rightNode;
}
if(node.right==null){
Node leftNode=node.left;
node.left=null;
size--;
return leftNode;
}
Node successor=minimum(node.right);
successor.right=removeMin(node.right);
successor.left=node.left;
node.left=node.right=null;
return successor;
}
}
}