二叉搜索树

Posted 壮少Bryant

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉搜索树相关的知识,希望对你有一定的参考价值。

性质:左子节点<父节点<右子节点

定义:

public class TreeNode {
    public int keyValue;    //关键字值
    public TreeNode leftNode;//左节点
    public TreeNode rightNode;//右节点

    public TreeNode(){}
    public TreeNode(int Key){
        this.keyValue = Key;
    }
}

1、查找

 public TreeNode search(int Key) {
        TreeNode node = root;
        // 首先定义一个节点让其指向根,在下面的循环中
        // 只要节点值不等于要查找的节点值就进入循环如果没有找到则返回null
        while (node.keyValue != Key) {
            if (Key < node.keyValue) { // 如果要查找的值小于节点值则指向左节点
                node = node.leftNode;
            } else { // 否则指向右节点
                node = node.rightNode;
            }
            if (node == null) { // 如果节点为空了则返回null
                return null;
            }
        }
        return node;
    }

2、插入

这里写图片描述

public void insert(int Key) {
        TreeNode node = new TreeNode(Key);
        // 添加节点之前首先要找到要添加的位置,这样就要记住要添加节点的父节点
        // 让父节点的左右指向要添加的节点
        if (root == null) { // 如果根结点为空,则根节点指向新节点
            root = node;
        } else {
            TreeNode currentNode = root;// 定义当前节点并指向根节点
            TreeNode parentNode;
            while (true) { // 寻找节点添加的位置
                parentNode = currentNode;//当前节点的父节点
                if (Key < currentNode.keyValue) {
                    currentNode = currentNode.leftNode;
                    if (currentNode == null) { // 当找到空节点的时候,父节点的左节点指向新节点
                        parentNode.leftNode = node;
                        return;
                    }
                } else {
                    currentNode = currentNode.rightNode;
                    if (currentNode == null) { // 当找到空节点的时候,父节点的右节点指向新节点
                        parentNode.rightNode = node;
                        return;
                    }
                }
            }
        }
    }

3、删除

二叉树的节点删除要分成三类:删除没有子节点的节点、删除只有一个子节点的节点、删除有两个子节点的节点。第一种和第二种很简单,第三种比较复杂。

在开始之前先要找到要删除的子节点,这就用到了查找

TreeNode current = root;
        TreeNode parent = root;
        boolean isLeftNode = true;//标记是父节点的左子节点还是右子节点
        while (current.keyValue != Key) {
            parent = current;//当前节点的父节点
            if (Key < current.keyValue) {
                isLeftNode = true;
                current = current.leftNode;
            } else {
                isLeftNode = false;
                current = current.rightNode;
            }
        }
        if (current == null) {
            System.out.println("没有找到要删除的节点!");
            return false;
        }

删除没有子节点的节点: 删除没有子节点的节点只需要将被删除节点的父节点指向空即可。
这里写图片描述

if (current.leftNode == null && current.rightNode == null) {  //要删除的节点没有子节点
            if (current == root) { // 根节点就删除整棵树
                root = null;
            } else if (isLeftNode) { // 如果是左节点,做节点指向空
                parent.leftNode = null;
            } else { // 如果是右节点,右节点指向空
                parent.rightNode = null;
            }
        }

删除有一个子节点的节点:删除有一个子节点的节点,只需要将被删除节点的父节点指向删除节点的子节点即可
这里写图片描述

      if (current.leftNode == null) {         //要删除的节点只有右节点
            if (current == root) { 
                root = current.rightNode;
            } else if (isLeftNode) {
                parent.leftNode = current.rightNode;
            } else {
                parent.rightNode = current.rightNode;
            }
        } else if (current.rightNode == null) {         //要删除的节点只有左节点
            if (current == root) { 
                root = current.leftNode;
            } else if (isLeftNode) {
                parent.leftNode = current.leftNode;
            } else {
                parent.rightNode = current.leftNode;
            }
        } 

删除有两个子节点的节点:删除有两个子节点的节点,到底谁来替代被删除的节点的位置呢?是左节点,还是右节点,代替以后这个子节点的子节点应该怎么安排?一系列的问题都出来了。。。简便的方法就是要找一个节点代替这个被删除的节点,这就要从二叉搜索树的定义来看。因为二叉搜索树是有序的,我们要找的节点在这棵树上,而且这个节点要比被删除的左节点大,比右节点小。先看看这个以被删除节点的右节点为根的子树的所有节点的值都要比被删除节点大,这是二叉搜索树定义的,但是要在这个集合中找到最小的一个,来代替被删除的节点,那就要在这棵子树上一直往左找。这个节点比被删除的节点的右节点小,且比左节点大,那这个节点就叫做被删除节点的后继节点,用这个节点来代替被删除节点。

private TreeNode findSuccessor(TreeNode delNode){
        TreeNode parent = delNode;
        TreeNode successor = delNode;
        TreeNode current = delNode.rightNode;
        while(current != null){
            parent = successor;
            successor = current;
            current = current.leftNode;
        }
        //这段代码的作用是当后继节点不是被删除节点的右节点并且后继结点有右节点的情况下的时候做的操作
        if(successor != delNode.rightNode){
            parent.leftNode = successor.rightNode;
            successor.rightNode = delNode.rightNode;
        }
        return successor;
    }

找到了后继节点剩下的事情就简单了只需要将被删除节点替换为后继节点就可以了,这里的过程要注意各种指针的指向。

TreeNode successor = findSuccessor(current);
            if(current == root){
                root = successor;
            }else if(isLeftNode){
                parent.leftNode = successor;
            }else{
                parent.rightNode = successor;
            }
            successor.leftNode = current.leftNode;

二叉搜索树的所有代码

public class Tree {
    public TreeNode root;// 根节点

    // 查找节点
    public TreeNode search(int Key) {
        TreeNode node = root;
        // 首先定义一个节点让其指向根,在下面的循环中
        // 只要节点值不等于要查找的节点值就进入循环如果没有找到则返回null
        while (node.keyValue != Key) {
            if (Key < node.keyValue) { // 如果要查找的值小于节点值则指向左节点
                node = node.leftNode;
            } else { // 否则指向右节点
                node = node.rightNode;
            }
            if (node == null) { // 如果节点为空了则返回null
                return null;
            }
        }
        return node;
    }

    // 添加节点
    public void insert(int Key) {
        TreeNode node = new TreeNode(Key);
        // 添加节点之前首先要找到要添加的位置,这样就要记住要添加节点的父节点
        // 让父节点的左右指向要添加的节点
        if (root == null) { // 如果根结点为空,则根节点指向新节点
            root = node;
        } else {
            TreeNode currentNode = root;// 定义当前节点并指向根节点
            TreeNode parentNode;
            while (true) { // 寻找节点添加的位置
                parentNode = currentNode;
                if (Key < currentNode.keyValue) {
                    currentNode = currentNode.leftNode;
                    if (currentNode == null) { // 当找到空节点的时候,父节点的左节点指向新节点
                        parentNode.leftNode = node;
                        return;
                    }
                } else {
                    currentNode = currentNode.rightNode;
                    if (currentNode == null) { // 当找到空节点的时候,父节点的右节点指向新节点
                        parentNode.rightNode = node;
                        return;
                    }
                }
            }
        }
    }

    // 遍历树
    public void display(TreeNode node) {
        if (node != null) {
            display(node.leftNode);
            System.out.println(node.keyValue + ",");
            display(node.rightNode);
        }
    }

    // 最大值
    public int max() {
        TreeNode node = root;
        TreeNode parent = null;
        while (node != null) {
            parent = node;
            node = node.rightNode;
        }
        return parent.keyValue;
    }

    // 最小值
    public int min() {
        TreeNode node = root;
        TreeNode parent = null;
        while (node != null) {
            parent = node;
            node = node.leftNode;
        }
        return parent.keyValue;
    }

    // 删除节点分三种方式删除节点
    // 1、删除没有子节点的节点,直接让该节点的父节点的左节点或右节点指向空
    // 2、删除有一个子节点的节点,直接让该节点的父节点指向被删除节点的剩余节点
    // 3、删除有三个节点的子节点,找到要删除节点的后继节点, 用该节点替代删除的节点
    public boolean delete(int Key) {
        // 首先查找节点,并记录该节点的父节点引用
        TreeNode current = root;
        TreeNode parent = root;
        boolean isLeftNode = true;
        while (current.keyValue != Key) {
            parent = current;
            if (Key < current.keyValue) {
                isLeftNode = true;
                current = current.leftNode;
            } else {
                isLeftNode = false;
                current = current.rightNode;
            }
        }
        if (current == null) {
            System.out.println("没有找到要删除的节点!");
            return false;
        }
        // 下面分三种情况删除节点
        if (current.leftNode == null && current.rightNode == null) {  //要删除的节点没有子节点
            if (current == root) { // 根节点就删除整棵树
                root = null;
            } else if (isLeftNode) { // 如果是左节点,做节点指向空
                parent.leftNode = null;
            } else { // 如果是右节点,右节点指向空
                parent.rightNode = null;
            }
        } else if (current.leftNode == null) {                         //要删除的节点只有右节点
            if (current == root) { 
                root = current.rightNode;
            } else if (isLeftNode) {
                parent.leftNode = current.rightNode;
            } else {
                parent.rightNode = current.rightNode;
            }
        } else if (current.rightNode == null) {                         //要删除的节点只有左节点
            if (current == root) { 
                root = current.leftNode;
            } else if (isLeftNode) {
                parent.leftNode = current.leftNode;
            } else {
                parent.rightNode = current.leftNode;
            }
        } else {                                                         //要删除的节点有两个节点
            TreeNode successor = findSuccessor(current);
            if(current == root){
                root = successor;
            }else if(isLeftNode){
                parent.leftNode = successor;
            }else{
                parent.rightNode = successor;
            }
            successor.leftNode = current.leftNode;
        }
        return true;
    }

    //查找后继节点
    private TreeNode findSuccessor(TreeNode delNode){
        TreeNode parent = delNode;
        TreeNode successor = delNode;
        TreeNode current = delNode.rightNode;
        while(current != null){
            parent = successor;
            successor = current;
            current = current.leftNode;
        }

        if(successor != delNode.rightNode){
            parent.leftNode = successor.rightNode;
            successor.rightNode = delNode.rightNode;
        }
        return successor;
    }
}

以上是关于二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章

二叉搜索树_BST

二叉搜索树_BST

查找树(搜索树)

二叉搜索树的定义中是不是允许重复键?

是否二叉搜索树

二叉树与二叉搜索树