二分搜索树
什么是二分搜索树?
二分搜索树是二叉树,二分搜索树的,每个节点比左子树的所有节点的值都大,比右子树的所有节点值小,,每一棵子树也是二分搜索树
其中,二分搜索树存储的数据具有可比性,所以在定义泛型接口时要注意实现Comparable接口BST<E extends Comparable<E>>
接着来看二叉树的基本结构,它与链表一样,动态数据结构
private class Node{
public E e;
public Node left,right;
public Node(E e){
this.e=e;
left=null;
right=null;
}
它具有天然递归结构,所以添加查询删除都可用递归实现
添加
首先来实现二分搜索树的添加元素操作,先判断根节点是否为空,为空则直接添加,否则,再将要加入元素与根节点比较大小,这里用到compareTo方法,比根节点大归为右子树,递归调用该函数,直到找到节点为空的位置,其他同理,最后返回插入新节点后二叉树的根节点
//向二分搜索树中加入元素
public void add(E e){
root=add(root,e);
}
private Node add(Node node,E e){
if (node==null){//这是递归终止条件
size++;
return new Node(e);
}
if (e.compareTo(node.e)<0){
node.left=add(node.left,e);
}
else if (e.compareTo(node.e)>0){
node.right=add(node.right,e);
}
return node;
}
查询
也是用递归实现,要查询元素e是否包含在树中,私有一个contains方法,里面同样要判断节点是否为空,再进行比较,若相等返回true,与添加操作相似
public boolean contains(E e){
return contains(root,e);
}
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;
}
}
前序遍历
前序遍历,就是先访问这个节点,再访问左子树,最后再访问右子树
同样,递归终止条件是访问到的结点为空,
public void preOrder(){
preOrder(root);
}
private void preOrder(Node node){
if (node==null){
return;
}
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
非递归方式实现
//非递归方式的前序遍历,利用栈来实现,
//先把根节点入栈,再循环栈是否为空,不为空出栈,
//栈是先进后出,所以右孩子先入栈,左孩子再入栈
public void preOrderNR(){
Stack<Node> stack=new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
Node cur=stack.pop();
System.out.println(cur);
if (cur.right!=null){
stack.push(cur.right);
}
if (cur.left!=null){
stack.push(cur.left);
}
}
}
中序遍历
有了前序遍历的基础,中序遍历的过程也是相似,
public void inOrder(){
inOrder(root);
}
private void inOrder(Node node){
if (node==null){
return;
}
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
后序遍历
后序遍历也是如此,
public void outOrder(){
outOrder(root);
}
private void outOrder(Node node){
if (node==null){
return;
}
outOrder(node.left);
outOrder(node.right);
System.out.println(node.e);
}
层序遍历
层序遍历,利用队列来实现,从0层,1层,2层逐层遍历
首先把根节点入队,再循环遍历队列是否为空
/根节点出队,队列是先进先出,所以左孩子入队,右孩子入队
//层序遍历,利用队列来实现,从0层,1层,2层逐层遍历
//首先把根节点入队,再循环遍历队列是否为空
//根节点出队,队列是先进先出,所以左孩子入队,右孩子入队
public void depthOrder(){
Queue<Node> queue =new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
Node cur=queue.remove();
System.out.println(cur);
if (cur.left!=null){
queue.add(cur.left);
}
if (cur.right!=null){
queue.add(cur.right);
}
}
}
删除节点(删除最小和最大值)
首先我们先从删除最小和最大值开始,删除最小值,就要先找到最小值,最小值只要一直找到左子树的最小值,而最大值则是一直找到右子树的最大值,要先判断树是否为空
public E minimum(){
if (size==0){
throw new IllegalArgumentException("bst is empty");
}
return minimum(root).e;
}
private Node minimum(Node node){
if (node.left==null){
return node;
}
return minimum(node.left);
}
public E maxmum(){
if (size==0){
throw new IllegalArgumentException("bst is empty");
}
return maxmum(root).e;
}
private Node maxmum(Node node){
if (node.right==null){
return node;
}
return maxmum(node.right);
}
public E removeMin(){
E ret = minimum();
root = removeMin(root);
return ret;
}
// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
private Node removeMin(Node node){
if (node.left==null){
Node rightNode=node.right;
node.right=null;
size --;
return rightNode;
}
node.left=removeMin(node.left);
return node;
}
public E removeMax(){
E ret = maxmum();
root = removeMax(root);
return ret;
}
// 删除掉以node为根的二分搜索树中的最大节点
// 返回删除节点后新的二分搜索树的根
private Node removeMax(Node node) {
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
node.right = removeMin(node.right);
return node;
}
删除元素(删除任意结点的元素)
// 从二分搜索树中删除元素为e的节点
public void remove(E e){
root = remove(root, e);
}
// 删除掉以node为根的二分搜索树中值为e的节点, 递归算法
// 返回删除节点后新的二分搜索树的根
Node remove(Node node, E e){
if( node == null )
return null;
if( e.compareTo(node.e) < 0 ){
node.left = remove(node.left , e);
return node;
}
else if(e.compareTo(node.e) > 0 ){
node.right = remove(node.right, e);
return node;
}
else{ // e.compareTo(node.e) == 0
// 待删除节点左子树为空的情况
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 = new Node(minimum(node.right).e);
size ++;
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
size --;
return successor;
}
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
generateBSTString(root, 0, res);
return res.toString();
}