数据结构 二叉搜索树

概述

二叉搜索树,也成为二叉查找树或者二叉排序树,这是一种特殊的二叉树。在二叉搜索树中的数据结构中,我们可以通过链表来表示。对该树种的节点,需要定义关键字data,父节点parent,左孩子节点leftChild,右孩子节点rightChild。

在二叉搜索树中,对任何节点node,其左子树中的所有节点的data的最大值不会超过node.data,其右子树的所有节点的data不会小于node.data。

这里写图片描述

二叉搜索树的简单操作

查找节点

在二叉搜索树中,查找一个节点k的过程需要从根节点开始。对于每次比较的节点x,如果k.note小于x.node,查找将从x的左子树继续,否则从右子树中遍历,直到k.note等于x.node,那么查找终止。

具体代码如下:

    /**
     * 查找指定节点
     * 
     * @param node
     */
    public TreeNode get(int data, TreeNode node) {
        if (node == null) {
            return null;
        }

        if (data == node.data) {
            return node;
        }

        if (data > node.data) {
            return get(data, node.rightChild);
        } else {
            return get(data, node.leftChild);
        }

    }

    /**
     * 查询指定结点
     * 
     * @param data
     * @return
     */
    public TreeNode get(int data) {
        return get(data, root);
    }

添加节点

想要在二叉搜索树中添加一个节点k,我们需要通过遍历的方式来完成。这个过程也是从根节点开始。

如果添加节点的值大于根节点的值,那么继续和根节点的右子树的根节点比较,否者和左子树的根节点比较,直到遍历到树的叶子节点。如果值大于叶子节点的值,那么该节点将作为叶子节点的右孩子节点插入到树中,否则作为叶子节点的左孩子节点插入,所以要插入的节点最终都是已叶子节点的形式插入到树中的。

具体代码如下:

    /**
     * 添加节点
     * 
     * @param data
     */
    public void put(int data) {
        // 防止插入一样的节点
        if (get(data) != null) {
            return;
        }

        // 创建即将插入的节点
        TreeNode newNode = new TreeNode(data, null, null, null);

        // 每次添加节点都从root节点开始
        TreeNode currNode = root;

        // 创建根节点
        if (currNode == null) {
            root = newNode;
            return;
        }

        while (currNode != null) {
            // 判断插入值和当前节点值的大小
            if (newNode.data >= currNode.data) {

                /*
                 * 当currNode的值比较大时,我们需要判断当前节点的右孩子和当前值得大小
                 */
                if (currNode.rightChild == null) {
                    newNode.parent = currNode;
                    currNode.rightChild = newNode;
                    return;
                } else {

                    currNode = currNode.rightChild;
                    continue;
                }
            } else {

                if (currNode.leftChild == null) {
                    newNode.parent = currNode;
                    currNode.leftChild = newNode;
                    return;
                } else {
                    currNode = currNode.leftChild;
                    continue;
                }

            }
        }

    }

删除节点

从一棵树中,删除一个节点deleteNode,情况稍微复杂一点,这里我们可以分为以下几种情况。

  1. 如果deleteNode没有孩子节点,那么直接删除即可
  2. 如果deleteNode有一个孩子节点,只需要将孩子节点替代deleteNode的位置即可
  3. 如果deleteNode有两个孩子节点,我们在删除的时候需要寻找deleteNode的后继节点,让deleteNode的值替换成后继节点的值,并删除后继节点。

以上这三种情况是在deleteNode不等于根节点root时的情况,如果deleteNode等于root,那么还有其他的处理方式,这个在下面的代码中会有体现。

具体代码如下:

/**
     * 删除节点
     * 
     */
    public boolean delete(int data) {
        /*
         * 删除结点有以下几种情况:1、如果该结点没有孩子结点,那么直接删除(设置该结点的parent为null)
         * 2、如果该结点只有一个孩子,那么直接使用孩子结点替换该节点即可 3、如果该结点有两个孩子,寻找后继结点替换该节点
         */

        TreeNode deleteNode = get(data);
        if (deleteNode == null) {
            System.out.println("该结点不存在");
            // throw new IllegalArgumentException("该节点");
            return false;
        } else if (root == deleteNode) {
            return deleteRoot();
        }

        TreeNode leftChild = deleteNode.leftChild;
        TreeNode rightChild = deleteNode.rightChild;
        TreeNode parent = deleteNode.parent;

        // 1、没有孩子节点
        if (leftChild == null && rightChild == null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = null;
            } else {
                parent.rightChild = null;
            }

            deleteNode.parent = null;

            return true;
        }

        // 2、只有一个左孩子节点
        if (leftChild != null && rightChild == null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = deleteNode.leftChild;
            } else {
                parent.rightChild = deleteNode.leftChild;
            }
            deleteNode.parent = null;

            return true;
        }

        // 2、只有一个右孩子节点
        if (leftChild == null && rightChild != null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = rightChild;
            } else {
                parent.rightChild = rightChild;
            }
            deleteNode.parent = null;

            return true;
        }

        // 3、存在两个孩子节点
        if (leftChild != null && rightChild != null) {
            // 获取要删除的节点的后继节点
            TreeNode nextNode = getNextNode(deleteNode);
            delete(nextNode.data);
            deleteNode.data = nextNode.data;

            return true;
        }

        return false;

    }

二叉搜索树的代码实现

package structdemo;

/**
 * 二叉排序树、二叉搜索树
 * 
 * @author zhangke
 *
 */
public class BinarySearchTree {

    public static void main(String[] args) {
        BinarySearchTree createTree = BinarySearchTree.createTree();

        createTree.delete(4);

        System.out.println("前序遍历:");
        createTree.preOrder(createTree.getRoot());
        System.out.println("\n\n中序遍历:");
        createTree.midOrder(createTree.getRoot());

    }

    /**
     * 创建二叉搜索树
     * 
     * @return
     */
    public static BinarySearchTree createTree() {
        BinarySearchTree tree = new BinarySearchTree();

        int[] data = { 4, 2, 1, 0, 3, 6, 5, 10, 8, 7, 9 };
        for (int i = 0; i < 11; i++) {
            tree.put(data[i]);
        }

        return tree;
    }

    /**
     * 树的根节点
     */
    private TreeNode root;

    public TreeNode getRoot() {
        return root;
    }

    /**
     * 添加节点
     * 
     * @param data
     */
    public void put(int data) {
        // 防止插入一样的节点
        if (get(data) != null) {
            return;
        }

        // 创建即将插入的节点
        TreeNode newNode = new TreeNode(data, null, null, null);

        // 每次添加节点都从root节点开始
        TreeNode currNode = root;

        // 创建根节点
        if (currNode == null) {
            root = newNode;
            return;
        }

        while (currNode != null) {
            // 判断插入值和当前节点值的大小
            if (newNode.data >= currNode.data) {

                /*
                 * 当currNode的值比较大时,我们需要判断当前节点的右孩子和当前值得大小
                 */
                if (currNode.rightChild == null) {
                    newNode.parent = currNode;
                    currNode.rightChild = newNode;
                    return;
                } else {

                    currNode = currNode.rightChild;
                    continue;
                }
            } else {

                if (currNode.leftChild == null) {
                    newNode.parent = currNode;
                    currNode.leftChild = newNode;
                    return;
                } else {
                    currNode = currNode.leftChild;
                    continue;
                }

            }
        }

    }

    /**
     * 查找指定节点
     * 
     * @param node
     */
    public TreeNode get(int data, TreeNode node) {
        if (node == null) {
            return null;
        }

        if (data == node.data) {
            return node;
        }

        if (data > node.data) {
            return get(data, node.rightChild);
        } else {
            return get(data, node.leftChild);
        }

    }

    /**
     * 查询指定结点
     * 
     * @param data
     * @return
     */
    public TreeNode get(int data) {
        return get(data, root);
    }

    /**
     * 删除节点
     * 
     */
    public boolean delete(int data) {
        /*
         * 删除结点有以下几种情况:1、如果该结点没有孩子结点,那么直接删除(设置该结点的parent为null)
         * 2、如果该结点只有一个孩子,那么直接使用孩子结点替换该节点即可 3、如果该结点有两个孩子,寻找后继结点替换该节点
         */

        TreeNode deleteNode = get(data);
        if (deleteNode == null) {
            System.out.println("该结点不存在");
            // throw new IllegalArgumentException("该节点");
            return false;
        } else if (root == deleteNode) {
            return deleteRoot();
        }

        TreeNode leftChild = deleteNode.leftChild;
        TreeNode rightChild = deleteNode.rightChild;
        TreeNode parent = deleteNode.parent;

        // 1、没有孩子节点
        if (leftChild == null && rightChild == null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = null;
            } else {
                parent.rightChild = null;
            }

            deleteNode.parent = null;

            return true;
        }

        // 2、只有一个左孩子节点
        if (leftChild != null && rightChild == null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = deleteNode.leftChild;
            } else {
                parent.rightChild = deleteNode.leftChild;
            }
            deleteNode.parent = null;

            return true;
        }

        // 2、只有一个右孩子节点
        if (leftChild == null && rightChild != null) {

            if (deleteNode == parent.leftChild) {
                parent.leftChild = rightChild;
            } else {
                parent.rightChild = rightChild;
            }
            deleteNode.parent = null;

            return true;
        }

        // 3、存在两个孩子节点
        if (leftChild != null && rightChild != null) {
            // 获取要删除的节点的后继节点
            TreeNode nextNode = getNextNode(deleteNode);
            delete(nextNode.data);
            deleteNode.data = nextNode.data;

            return true;
        }

        return false;

    }

    /**
     * 删除根节点
     * 
     * @param data
     * @return
     */
    private boolean deleteRoot() {
        TreeNode nextNode = getNextNode(root);
        // 1、后继节点为空,左孩子节点为跟节点
        if (nextNode == null) {
            root = root.leftChild;
            if (root.leftChild != null) {
                root.leftChild.parent = null;
            }
            return true;
        }

        int data = nextNode.data;
        delete(data);
        root.data = data;
        return true;
    }

    /**
     * 获取后继节点
     * 
     * @param deleteNode
     * @return
     */
    private TreeNode getNextNode(TreeNode node) {
        if (node == null) {
            throw null;
        }

        // 获取节点的后继节点,只要获取右子树的最小节点即可
        return getTreeMinNode(node.rightChild);
    }

    /**
     * 获取最小节点
     * 
     * @param node
     * @return
     */
    public TreeNode getTreeMinNode(TreeNode node) {
        if (node == null) {
            return null;
        }
        TreeNode minNode = node;
        while (minNode.leftChild != null) {
            minNode = minNode.leftChild;
        }

        return minNode;
    }

    /**
     * 获取最大节点
     * 
     * @param node
     * @return
     */
    public TreeNode getTreMaxNode(TreeNode node) {
        if (node == null) {
            return null;
        }
        TreeNode maxNode = node;
        while (maxNode.rightChild != null) {
            maxNode = maxNode.rightChild;
        }

        return maxNode;
    }

    /**
     * 获取一个树的最小结点
     * 
     * @param node
     */
    public TreeNode getTreeMinNode() {
        return getTreeMinNode(root);
    }

    /**
     * 获取一个树的最大结点
     * 
     * @param node
     */
    public TreeNode getTreMaxNode() {
        return getTreMaxNode(root);
    }

    /**
     * 获取结点个数
     * 
     * @return
     */
    public int getSize() {
        return getSize(root);
    }

    /**
     * 获取树的节点个数
     * 
     * @return
     */
    private int getSize(TreeNode node) {
        if (node == null) {
            return 0;
        }
        return 1 + getSize(node.leftChild) + getSize(node.rightChild);
    }

    /**
     * 中序遍历
     * 
     * left->parent->right
     */
    public void midOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        midOrder(node.leftChild);
        System.out.print(node.data + "\t");
        midOrder(node.rightChild);
    }

    /**
     * 前序遍历
     * 
     * parent->left->right
     */
    public void preOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        System.out.print(node.data + "\t");
        preOrder(node.leftChild);
        preOrder(node.rightChild);
    }

    /**
     * 树的节点
     */
    public class TreeNode {

        public int data;
        public TreeNode parent;
        public TreeNode leftChild;
        public TreeNode rightChild;

        public TreeNode(int data) {
            this.data = data;
        }

        public TreeNode(int data, TreeNode parent, TreeNode leftChild, TreeNode rightChild) {
            this.data = data;
            this.parent = parent;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值