[05] Data Structure Red - Black tree base ---- binary search tree (Binary Search Tree)

Introduction
In [04] tree and binary tree algorithm has been introduced after the pre-order traversal of some basic concepts about the tree and binary tree, but this article will be launched on the basis of a binary tree up to explain the binary search tree, also That binary search tree built on top of the tree. As to why bloggers spend an entire article in terms of this binary search trees? The reason is simple, red - black tree is based on a binary search tree, if not understand binary search tree, then talk about how red - black tree? The importance of red-black tree I think you did not eat meat Paige certainly seen Yichun run .... Yes, jdk1.8's Map is a hash table + red-black tree implementation!

@

We must first be clear that binary search tree , also known as binary sort tree , binary search tree , Jane collectively BST

1. Introduction dichotomy

Before formally binary search tree, Yichun still want to talk about life, talk about life and thus cut into the binary search tree.

One day, programmers Laos to Yichun call: handsome, I order a pair of shoes today, a pretty old, expensive ah!
Yichun: Come on you, ah I do not know what the conditions, but also shoes, leather shoes mice it, if it is real leather shoes I ate it myself! ! !
Laos: You really do not say, it really is really shoes than real leather fake leather shoes, pretty old, and I guess you bought it and how much money, anyway, is less than 100, and then see if you can at least number guess, if you guess within five chances to cook the stew for you to eat, Ga bang crisp, hey ...
Yichun: you tinkling se it, or do you know me, know me well this one (from black) .... Ahem cough, 50
Laos: No, the high price
of Yichun: 25
Laos: No, the price is higher the
Yichun: 12.5
....

A word to see all of you here estimate all are human, it is a simple dichotomy guessing game by guessing the number "big" and "small" situation to judge, to guess the final digital. Of course, this change can be described in a few words, Yichun took so long strings of string describing the estimated Yichun TM is a talent .... In fact it, wanted to Yichun active active atmosphere, to focus on what you brain cells next, of course, wasted time, true sorry, Yichun online beating ....

I wonder if you ever wondered why the dichotomy is to have sufficient advantage of it? If less than 25 100 I guess not direct it? In this way it is more likely to fix prices? In fact, 25 or less directly guess this is an extreme guess, if indeed smaller than 25, it is within the range of 25, then you have not thought about: If it is greater than 25? Range directly turned into a 75! With the advance of this extreme thinking, you will find
each choose half will find more accurate and better able to cope with various situations of uncertainty! Optional half look complexity is O (log_2 n), that is most needed O (log_2 n) times can guess the final figure.

Here, to begin formally introduced binary search tree, in fact, a binary search tree is similar to the above-mentioned over binary search, has a similar flavor.

2, the definition of a binary search tree

Binary search tree (Binary Search Tree, BST) is a special kind of binary tree, a binary search tree (BST) is a binary tree, in which, for each node in the tree:
1, if its left subtree is present, then the left subtree is not greater than the value for each node of the node value;
2, if it is present right subtree, its right subtree is the value of each point is not less than the value of the node.

Here Insert Picture Description

CRUD 3, binary search tree

I want to mention is: We know that traverse the tree is in front after order traversal method, but traversal binary search tree is best to use in order traversal method, if you do not understand why using preorder, then you have three options:
First, the self [04] algorithm and the binary tree into the tree Bubu basic foundation
Second, the message to ask questions, went back to see Yichun
Third, both front refused Here Insert Picture Description, then you are a good ....

3.1 Find

If you're looking to find any node in the tree, it is assumed that binary X, we can be divided into the following steps:

1, if the binary search tree is empty, the operation returns empty, otherwise, at operation;
2, first take root, if X is equal to the root node, return;
3, if less than the root node, the left child recursive search trees;
4, if the node is greater than the root node, recursively find the right subtree.

Here Insert Picture Description

//查找的逻辑代码实现:
    /**
     * @param value 希望查找结点的值
     * @return 如果找到返回该结点,否则返回null
     */
    public Node search(int value) {
        if(value == this.value) { //找到就是该结点
            return this;
        } else if(value < this.value) {//如果查找的值小于当前结点,向左子树递归查找
            //如果左子结点为空
            if(this.left  == null) {
                return null;
            }
            return this.left.search(value);
        } else { //如果查找的值不小于当前结点,向右子树递归查找
            if(this.right == null) {
                return null;
            }
            return this.right.search(value);
        }

    }

3.2, insert

The size relationship insert a node in a binary tree, think about it, you will find an insert node are generally inserted into a leaf node, so just start from the root, in order to traverse the comparison data to be inserted and nodes.

== binary search tree has a very important feature is inserted difficult to achieve and find almost ==. In fact, the insertion node can have the following three conditions:

1、如果树是空的,则直接将新节点插入,否则,执行下面步骤。
2、要插入的数据比根节点数据大,则到右子树中插入新数据,如果右子树为空,则将新数据直接插入到右子节点的位置;不为空,则继续遍历右子树,查找插入位置。
3、要插入的数据比根节点数据小,则到左子树中插入数据,如果左子树为空,则直接将新数据插入到左子节点的位置;不为空,则继续遍历左子树,查找插入的位置。

Here Insert Picture Description

 //添加结点的逻辑代码
    //递归的形式添加结点,注意需要满足二叉排序树的要求
    public void add(Node node) {
        if(node == null) {
            return;
        }
       if(root == null) {
            root = node;//如果root为空则直接让root指向node
        } 
        //判断传入的结点的值,和当前子树的根结点的值关系
      if(node.value < this.value) {
            //如果当前结点左子结点为null
            if(this.left == null) {
                this.left = node;
            } else {
                //递归的向左子树添加
                this.left.add(node);
            }
        } else { //添加的结点的值大于 当前结点的值
            if(this.right == null) {
                this.right = node;
            } else {
                //递归的向右子树添加
                this.right.add(node);
            }

        }
    }

3.3、删除

可以这么说,删除相对查找和插入来说比较复杂一些,为啥会复杂一些呢?因为要删除某一个节点,首先要查找到这个节点然后将其删除,删除之后还需要将该二叉搜索树还原成一颗二叉搜索树。因此针对要删除节点的子节点位置的不同,同样一般分为三种情况来处理:

1、 第一种情况,如果要删除的节点没有子节点,直接将父节点指向要删除节点的指针指向 null。比如途中要删除的节点 0。
2、第二种情况,如果要删除的节点只有一个节点,即只有左子节点或右子节点,则将父节点指向要删除节点的指针指向要删除节点的子节点即可。比如途中要删除的节点1。
3、第三种情况,如果要删除的节点有两个子节点,则需要先找到这个节点右子树中的最小节点或者左子树中的最大节点,将其替换到要删除的节点上。然后删除这个右子树中的最小节点或左子树中的最大节点,比如图中要删除的节点 6。

Here Insert Picture Description

 //删除结点逻辑代码
    public void delNode(int value) {
        if(root == null) {
            return;
        }else {
            //1.需求先去找到要删除的结点  targetNode
            Node targetNode = search(value);
            //如果没有找到要删除的结点
            if(targetNode == null) {
                return;
            }
            //如果我们发现当前这颗二叉排序树只有一个结点
            if(root.left == null && root.right == null) {
                root = null;
                return;
            }

            //去找到targetNode的父结点
            Node parent = searchParent(value);
            //如果要删除的结点是叶子结点
            if(targetNode.left == null && targetNode.right == null) {
                //判断targetNode 是父结点的左子结点,还是右子结点
                if(parent.left != null && parent.left.value == value) { //是左子结点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) {//是由子结点
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) { //删除有两颗子树的节点
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;


            } else { // 删除只有一颗子树的结点
                //如果要删除的结点有左子结点
                if(targetNode.left != null) {
                    if(parent != null) {
                        //如果 targetNode 是 parent 的左子结点
                        if(parent.left.value == value) {
                            parent.left = targetNode.left;
                        } else { //  targetNode 是 parent 的右子结点
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else { //如果要删除的结点有右子结点
                    if(parent != null) {
                        //如果 targetNode 是 parent 的左子结点
                        if(parent.left.value == value) {
                            parent.left = targetNode.right;
                        } else { //如果 targetNode 是 parent 的右子结点
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }

            }

        }
    }

3.4、整体代码

为了连贯一下思维,可以自行编辑main方法进行测试!

package dataStructure;

//创建二叉排序树
class BinarySortTree {
    private Node root;

    public Node getRoot() {
        return root;
    }

    //查找要删除的结点
    public Node search(int value) {
        if(root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //查找父结点
    public Node searchParent(int value) {
        if(root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //编写方法:
    //1. 返回的 以node 为根结点的二叉排序树的最小结点的值
    //2. 删除node 为根结点的二叉排序树的最小结点
    /**
     *
     * @param node 传入的结点(当做二叉排序树的根结点)
     * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
     */
    public int delRightTreeMin(Node node) {
        Node target = node;
        //循环的查找左子节点,就会找到最小值
        while(target.left != null) {
            target = target.left;
        }
        //这时 target就指向了最小结点
        //删除最小结点
        delNode(target.value);
        return target.value;
    }


    //删除结点
    public void delNode(int value) {
        if(root == null) {
            return;
        }else {
            //1.需求先去找到要删除的结点  targetNode
            Node targetNode = search(value);
            //如果没有找到要删除的结点
            if(targetNode == null) {
                return;
            }
            //如果我们发现当前这颗二叉排序树只有一个结点
            if(root.left == null && root.right == null) {
                root = null;
                return;
            }

            //去找到targetNode的父结点
            Node parent = searchParent(value);
            //如果要删除的结点是叶子结点
            if(targetNode.left == null && targetNode.right == null) {
                //判断targetNode 是父结点的左子结点,还是右子结点
                if(parent.left != null && parent.left.value == value) { //是左子结点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) {//是由子结点
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) { //删除有两颗子树的节点
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;


            } else { // 删除只有一颗子树的结点
                //如果要删除的结点有左子结点
                if(targetNode.left != null) {
                    if(parent != null) {
                        //如果 targetNode 是 parent 的左子结点
                        if(parent.left.value == value) {
                            parent.left = targetNode.left;
                        } else { //  targetNode 是 parent 的右子结点
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else { //如果要删除的结点有右子结点
                    if(parent != null) {
                        //如果 targetNode 是 parent 的左子结点
                        if(parent.left.value == value) {
                            parent.left = targetNode.right;
                        } else { //如果 targetNode 是 parent 的右子结点
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }

            }

        }
    }

    //添加结点的方法
    public void add(Node node) {
        if(root == null) {
            root = node;//如果root为空则直接让root指向node
        } else {
            root.add(node);
        }
    }
    //中序遍历
    public void infixOrder() {
        if(root != null) {
            root.infixOrder();
        } else {
            System.out.println("二叉排序树为空,不能遍历");
        }
    }
}

//创建Node结点
class Node {
    int value;
    Node left;
    Node right;
    public Node(int value) {

        this.value = value;
    }


    //查找要删除的结点
    /**
     *
     * @param value 希望删除的结点的值
     * @return 如果找到返回该结点,否则返回null
     */
    public Node search(int value) {
        if(value == this.value) { //找到就是该结点
            return this;
        } else if(value < this.value) {//如果查找的值小于当前结点,向左子树递归查找
            //如果左子结点为空
            if(this.left  == null) {
                return null;
            }
            return this.left.search(value);
        } else { //如果查找的值不小于当前结点,向右子树递归查找
            if(this.right == null) {
                return null;
            }
            return this.right.search(value);
        }

    }
    //查找要删除结点的父结点
    /**
     *
     * @param value 要找到的结点的值
     * @return 返回的是要删除的结点的父结点,如果没有就返回null
     */
    public Node searchParent(int value) {
        //如果当前结点就是要删除的结点的父结点,就返回
        if((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            //如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
            if(value < this.value && this.left != null) {
                return this.left.searchParent(value); //向左子树递归查找
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value); //向右子树递归查找
            } else {
                return null; // 没有找到父结点
            }
        }

    }

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


    //添加结点的方法
    //递归的形式添加结点,注意需要满足二叉排序树的要求
    public void add(Node node) {
        if(node == null) {
            return;
        }

        //判断传入的结点的值,和当前子树的根结点的值关系
        if(node.value < this.value) {
            //如果当前结点左子结点为null
            if(this.left == null) {
                this.left = node;
            } else {
                //递归的向左子树添加
                this.left.add(node);
            }
        } else { //添加的结点的值大于 当前结点的值
            if(this.right == null) {
                this.right = node;
            } else {
                //递归的向右子树添加
                this.right.add(node);
            }

        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if(this.right != null) {
            this.right.infixOrder();
        }
    }

}


public class BinarySortTreeDemo { //==========至于main方法的测试代码可自行调整测试!!!!!!!!!
    public static void main(String[] args) {
        int[] arr = {4,7, 2, 13, 11, 5, 1, 9, 3};
        BinarySortTree binarySortTree = new BinarySortTree();
        for(int i = 0; i< arr.length; i++) {
            binarySortTree.add(new Node(arr[i]));
        }
        binarySortTree.add(new Node(4));

        System.out.println("中序遍历二叉排序树~");
        binarySortTree.infixOrder(); 
    }
}

4、二叉搜索树的两种极端情况

1、变成一颗 完全二叉树,所有节点尽量填满树的每一层,上一层填满后还有剩余节点的话,则由左向右尽量填满下一层。如下图所示,即为一颗完全二叉树;
Here Insert Picture Description
2、每一层只有一个节点的二叉树。如下图所示:
Here Insert Picture Description
我敲,这不是蛇皮怪单链表吗,是的,给我们的感觉就是树形怪退化为蛇皮怪单链表了!在这种情况下,树中每层只有一个节点,该状态的树结构更倾向于一种线性结构,节点的查询类似于数组的遍历,复杂度为 O(n)。

也正是因此,后面就出现了平衡二叉树,就涉及到了左旋右旋花里胡哨的蛇皮操作,当然这只是提一下,并不在本文的范畴之内,不过后续应该会写这方面的文章,尽量吧.....

5、二叉搜索树总结

二叉搜索树又称二叉排序树二叉查找树,简统称BST

根据二叉搜索树的特性,==可知比较次数等于给定值节点在二叉排序树中的层数==。遍历的话使用中序遍历。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2n+1,其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。而平衡二叉树可能又要涉及到了左旋右旋花里胡哨的蛇皮操作,当然这只是提一下,并不在本文的范畴之内,不过后续应该会写这方面的文章,尽量吧.....

如果本文对你有一点点帮助,那么请点个赞呗,谢谢~

Finally, if there is insufficient or is not correct, please correct me criticism, grateful! If you have questions please leave a message, the absolute first time to reply!

I welcome you to focus on the public number, there are some java learning materials and a large wave of java e-books, such as Zhou Zhiming teacher depth java virtual machine, java programming ideas, the core technology volume, Westward design patterns, java concurrent programming combat ... .. is a java Bible, do not say fast car on Tomcat, ye who go! The main thing is to explore technology, yearning technology, the pursuit of technology, said good pots Friends is coming Oh ...

Here Insert Picture Description

Guess you like

Origin www.cnblogs.com/yichunguo/p/12036581.html