• 【数据结构】二叉查找树 Binary Search Tree


    二叉查找树(Binary Search Tree)

    二叉查找树(英语:Binary Search Tree),也称为二叉搜索树有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树

    1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
    2. 若任意节点的右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
    3. 任意节点的左、右子树也分别为二叉查找树;

    这是一个非常有意思的数据结构。 它的查找和插入时间复杂度都能在O(logn),最坏的情况下O(n)(数列有序,树退化成线性表)。

    Node

    public class Node {
        public int iData;              // data item (key)
        public double dData;           // data item
        public Node leftChild;         // this node's left child
        public Node rightChild;        // this node's right child
    
        
    }
    
    

    查找算法

    查找x的过程:

    1. 若b是空树,则搜索失败,否则:
    2. 若x等于b的根节点的数据域之值,则查找成功;否则:
    3. 若x小于b的根节点的数据域之值,则搜索左子树;否则:
    4. 查找右子树。
     public Node find(int key)      // find node with given key
        {                           // (assumes non-empty tree)
            Node current = root;               // start at root
            while(current.iData != key)        // while no match,
            {
                if(key < current.iData)         // go left?
                    current = current.leftChild;
                else                            // or go right?
                    current = current.rightChild;
                if(current == null)             // if no child,
                    return null;                 // didn't find it
            }
            return current;                    // found it
        }  // end find()
    
    

    插入算法

    插入一个节点s的算法,过程为:

    1. 若b是空树,则将s所指节点作为根节点插入,否则:
    2. 若s->data等于b的根节点的数据域之值,则返回,否则:
    3. 若s->data小于b的根节点的数据域之值,则把s所指节点插入到左子树中,否则:
    4. 把s所指节点插入到右子树中。(新插入节点总是叶子节点)
    public void insert(int id, double dd)
        {
            Node newNode = new Node();    // make new node
            newNode.iData = id;           // insert data
            newNode.dData = dd;
            if(root==null)                // no node in root
                root = newNode;
            else                          // root occupied
            {
                Node current = root;       // start at root
                Node parent;
                while(true)                // (exits internally)
                {
                    parent = current;
                    if(id < current.iData)  // go left?
                    {
                        current = current.leftChild;
                        if(current == null)  // if end of the line,
                        {                 // insert on left
                            parent.leftChild = newNode;
                            return;
                        }
                    }  // end if go left
                    else                    // or go right?
                    {
                        current = current.rightChild;
                        if(current == null)  // if end of the line
                        {                 // insert on right
                            parent.rightChild = newNode;
                            return;
                        }
                    }  // end else go right
                }  // end while
            }  // end else not root
        }  // end insert()
    

    删除算法

    从树里面删除一个节点很简单,但是要从BST中删除一个节点,就会有点复杂,因为在删除节依然要保证整棵树的BST结构,即右子树上的节点>父节点>左子树的节点。
    所以在删除的过程中,要分几种情况:

    1. 如果p节点是叶子节点,即PL[左子树],PR[右子树]均为空。这个情况下,直接删掉,不会影响整树的结构

    2. 如果p节点只有左子树和右子树,此时只要将PL,PR直接成为其双亲节点f的左子树或右子树,这个情况,也不会影响树的结构。

    3. 如果p的PL,PR都不为空,此时删除p,为了保证整树的结构,就需要从L,R中寻找一个节点来代替p的位置。有2种方式来处理:
      a. 从p的左子树中寻找该子树中最右下的节点s,令p的左子树为f的左/右(根据p是f的左子树还是右子树而定)子树,而p的右子树为s的右子树。
      b. 令p的直接前驱(in-order predecessor)或直接后继(in-order successor)替代p,然后再从二叉查找树中删去它的直接前驱(或直接后继)。

    这个处理方式实在是抽象,其实就是删除有2个子节点的节点,用它的中序后继来代替该节点。但是麻烦一点的就是这个后继也有子节点。这样的场景是存在的,也只会存在右子节点,这个时候移动后继节点的时候,这个右子节点(树),跟着移动就可以。

    public boolean delete(int key){
            Node current = root;
            Node parent = root;
            boolean isLeftChild = true;
    
            while(current.iData != key)        // search for node
            {
                parent = current;
                if(key < current.iData)         // go left?
                {
                    isLeftChild = true;
                    current = current.leftChild;
                }
                else                            // or go right?
                {
                    isLeftChild = false;
                    current = current.rightChild;
                }
                if(current == null)             // end of the line,
                {
                    return false;                // didn't find it
                }
            }  // end while
            // found node to delete
    
            // if no children, simply delete it
            if(current.leftChild==null &&
                    current.rightChild==null)
            {
                if(current == root)             // if root,
                    root = null;                 // tree is empty
                else if(isLeftChild)
                    parent.leftChild = null;     // disconnect
                else                            // from parent
                    parent.rightChild = null;
            }
    
            // if no right child, replace with left subtree
            else if(current.rightChild==null)
                if(current == root)
                    root = current.leftChild;
                else if(isLeftChild)
                    parent.leftChild = current.leftChild;
                else
                    parent.rightChild = current.leftChild;
    
                // if no left child, replace with right subtree
            else if(current.leftChild==null)
                if(current == root)
                    root = current.rightChild;
                else if(isLeftChild)
                    parent.leftChild = current.rightChild;
                else
                    parent.rightChild = current.rightChild;
    
            else  // two children, so replace with inorder successor
            {
                // get successor of node to delete (current)
                Node successor = getSuccessor(current);
    
                // connect parent of current to successor instead
                if(current == root)
                    root = successor;
                else if(isLeftChild)
                    parent.leftChild = successor;
                else
                    parent.rightChild = successor;
    
                // connect successor to current's left child
                successor.leftChild = current.leftChild;
            }  // end else two children
            // (successor cannot have a left child)
            return true;
        }
    
    

    getSuccessor

    // returns node with next-highest value after delNode
        // goes to right child, then right child's left descendents
        private Node getSuccessor(Node delNode)
        {
            Node successorParent = delNode;
            Node successor = delNode;
            Node current = delNode.rightChild;   // go to right child
            while(current != null)               // until no more
            {                                 // left children,
                successorParent = successor;
                successor = current;
                current = current.leftChild;      // go to left child
            }
            // if successor not
            if(successor != delNode.rightChild)  // right child,
            {                                 // make connections
                successorParent.leftChild = successor.rightChild;
                successor.rightChild = delNode.rightChild;
            }
            return successor;
        }
    
    
  • 相关阅读:
    UVA 10462 Is There A Second Way Left?(次小生成树&Prim&Kruskal)题解
    POJ 1679 The Unique MST (次小生成树)题解
    POJ 2373 Dividing the Path (单调队列优化DP)题解
    BZOJ 2709 迷宫花园
    BZOJ 1270 雷涛的小猫
    BZOJ 2834 回家的路
    BZOJ 2506 calc
    BZOJ 3124 直径
    BZOJ 4416 阶乘字符串
    BZOJ 3930 选数
  • 原文地址:https://www.cnblogs.com/dreamtaker/p/13495072.html
Copyright © 2020-2023  润新知