zoukankan      html  css  js  c++  java
  • 【数据结构】二叉查找树 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;
        }
    
    
  • 相关阅读:
    《领域驱动设计的原则与实践》读书笔记(一)
    asp.net identity UserSecurityStamp 的作用
    Python Django Apache配置
    SQL语句优化(转载)
    【转】性能测试,影响 TPS 的一些因素
    【转】接口测试总结
    【转】两款 Web 前端性能测试工具
    【转】工作中使用Trepn Power Profiler的应用总结
    【转】nGrinder 简易使用教程
    Http协议状态码总结
  • 原文地址:https://www.cnblogs.com/dreamtaker/p/13495072.html
Copyright © 2011-2022 走看看