数据结构之二叉搜索树(java语言版)

之前介绍了树,主要实现了二叉树的代码。在二叉树的基础上有许多衍生的树,如二叉搜索树、哈夫曼树等,今天学习一下二叉搜索树。

二叉搜索树

二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST

又被称为:二叉查找树、二叉排序树

特点

任意一个节点的值都大于其左子树所有节点的值

任意一个节点的值都小于其右子树所有节点的值

它的左右子树也是一棵二叉搜索树

◼ 二叉搜索树可以大大提高搜索数据的效率

◼ 二叉搜索树存储的元素必须具备可比较性

在这里插入图片描述

java语言实现

有了之前的二叉树的实现,此时实现二叉搜索树就比较简单。

节点类

1、节点类中的data要求必须具有可比较性,我在此使用int型,如果使用的数据是其他对象,需要经过处理使其具有可比较性。

2、二叉搜索树需要有三条引用链,左节点、右节点、父节点。父节点在删除数据时需要使用。

public class Node {
    int data;
    Node left;
    Node right;
    Node parent;

    public Node(int data, Node left, Node right, Node parent) {
        this.data = data;
        this.left = left;
        this.right = right;
        this.parent = parent;
    }

    @Override
    public String toString() {
        return "Node [data=" + data + "]";
    }

}


方法

二叉搜索树的方法:

作为接口BinaryTree

public interface BinaryTree {

    void add(int e);

    void remove(int e);

    void replace(int e1, int e2);

    Node search(int e);

    boolean isEmpty();

    int compare(int e1, int e2);

    void display(Node root);

}

二叉搜索树实现

构造方法

public class BST implements BinaryTree {

    int size;
    Node root;

    public BST() {
        System.out.println("BST init....");
    }

    
    @Override
    public boolean isEmpty() {

        return size == 0;
    }


}

compare()方法

通过compare方法比较大小,决定节点的位置。

 /**
     *
     *     将两个节点传入,进行比较,
     *     如果e1>e2,return 1
     *     e1<e2,return -1
     *     相等 return 0
     */
@Override
public int compare(int e1, int e2) {
        if (e1 == e2) {
            return 0;
        }
        return e1 > e2 ? 1 : -1;
    }

    

add方法

add方法用于加入数据.

调用了compare()方法用于比较大小。

@Override
    public void add(int e) {
        if (root == null) {
            root = new Node(e, null, null, null);
            System.out.println("add root succcessful!!!");
            return;
        }

        Node head = root;
        Node parent = root;
        int cap = 0;
        while (head != null) {
            parent = head;

            cap = compare(head.data, e);

            if (cap > 0) {
                head = head.left;
            } else if (cap < 0) {
                head = head.right;
            } else {
                System.out.println("=====已有相关节点");
                return;
            }
        }

        Node newNode = new Node(e, null, null, parent);
        if (cap > 0) {
            parent.left = newNode;
        } else {
            parent.right = newNode;
        }
        System.out.println("add successfu!!!");

        size++;
    }

先序遍历

@Override
public void display(Node root) {
        if (root == null) {
            return;
        }

        System.out.print("  " + root);
        display(root.left);
        display(root.right);
    }

测试

用一个数组作为数据测试一下:

public static void main(String[] args) {
		int[] arr = new int[] { 8, 6, 10, 4, 7, 9, 13 };

        BST bst = new BST();
        for (int i : arr) {
            bst.add(i);
        }

        bst.display(bst.root);

    }

按测试数据的输入,得到的树应该是右边的树。

在这里插入图片描述

得到如下结果:

BST init....
add root succcessful!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
  Node [data=8]  Node [data=4]  Node [data=6]  Node [data=7]  Node [data=9]  Node [data=10]  Node [data=13]

如果需要得到左边的树、那么需要以下顺序输入:

8,6,10,4,7,9,13

此时的树与节点输入顺序有关系,右边的树虽然是二叉搜索树,但已经不平衡了。不平衡的树有许多缺点,所以有了平衡二叉树。

search方法

通过search方法得到元素所在的节点。

 @Override
public Node search(int e) {
        if (isEmpty()) {
            System.out.println("null tree");
            return null;
        }

        Node head = root;
        Node parent = root;
        int cap = 0;
        while (head != null) {
            parent = head;

            cap = compare(head.data, e);

            if (cap > 0) {
                head = head.left;
            } else if (cap < 0) {
                head = head.right;
            } else {
                return head;
            }
        }

        return null;
    }

replace方法

replace这个方法在使用是注意约束条件,要控制替换的数据满足二叉搜索树的定义。

 @Override
public void replace(int e1, int e2) {
        Node node = search(e1);
        if (node == null) {
            System.out.println("not node could be replace");
            return;
        }

        // 代替的位置主要要满足约束条件
        int cap;
        if (node.parent != null) {
            cap = compare(node.parent.data, e2);
            if (cap >= 0) {
                System.out.println("not node could be replace");
                return;
            }
        }

        if (node.left != null) {
            cap = compare(node.left.data, e2);
            if (cap >= 0) {
                System.out.println("not node could be replace");
                return;
            }
        }

        if (node.right != null) {
            cap = compare(node.right.data, e2);
            if (cap <= 0) {
                System.out.println("not node could be replace");
                return;
            }
        }

        node.data = e2;

    }

remove方法

删除节点需要用到parent节点,节点被删除后,其子节点和父节点的相关引用都会改变。

并且根据删除的节点的位置,需要有一些细微的操作。

删除叶子节点
 @Override
    public void remove(int e) {
        Node node = search(e);
        if (node == null) {
            System.out.println("not node could be remove");
            return;
        }
        size--;

        Node parent = node.parent;// 得到父节点

        if (node.left == null && node.right == null) {// 叶子节点
            if (parent.right == node) {
                parent.right = null;
            } else {
                parent.left = null;
            }
            System.out.println("叶子节点删除。。。");
            return;
        }

        if (node.left != null && node.right != null) {// 度为2的节点
            if (parent == null) {// 为root节点时,默认选择前驱节点为new root
                Node newRoot = maxChildNode(node);
                System.out.println(" new root: " + newRoot);
                if (newRoot != null) {
                    node.data = newRoot.data;

                } else {
                    newRoot = minChildNode(node);
                    node.data = newRoot.data;
                }

                // 消灭newRoot
                if (newRoot.parent.right == newRoot) {
                    newRoot.parent.right = null;
                } else {
                    newRoot.parent.left = null;
                }
            }

            if (parent != null) {
						//.........
            }

            System.out.println("度为2的节点删除");
            return;
        } else {// 度为1

            if (parent == null) {// root节点
                if (node.right != null) {

                    node = node.right;
                    node.right.parent = null;

                } else {
                    node = node.left;
                    node.left.parent = null;
                }
            }
            
            //.................

            System.out.println("度为1的节点删除");
        }

    }
posted @ 2024-04-11 11:47  cgl_dong  阅读(18)  评论(0编辑  收藏  举报