第4章 树(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lc250123/article/details/82117215

目录

树的实现

二叉树

查找树 ADT -- 二叉查找树

AVL 树


对于大量的输入数据,链表的线性访问时间太长,不宜使用。

树的实现

由于树的每个结点的儿子数可能变化很大并且事先不知道,因此在数据结构中建立到各儿子结点的直接链接是不可行的,因为这会产生太多浪费的空间。最好的办法是使用:第一儿子/下一兄弟表示法

        struct TreeNode
        {
            Object        element;
            TreeNode     *firstChild;
            TreeNode     *nextSibling;
        }

结点的所有儿子结点都放在 firstChild 链表中,兄弟结点在 nextSibling 链表中。


二叉树

因为一个二叉树结点最多有两个儿子,所以可以直接链接到它们。实现如下

        struct BinaryNode
        {
            Object      element;      // The data in the node
            BinaryNode *left;         // Left child
            BinaryNode *right;        // Right child
        };

具有 N 个结点的二叉树,都将需要 N + 1 个 NULL 链。

二叉树分三种遍历:前根、中根、后根遍历。


查找树 ADT -- 二叉查找树

二叉树的一个重要应用是它们在查找中的应用。

二叉查找树的性质是:对于树中的每个结点 X,它的左子树中所有项的值小于 X 中的项,而它的右子树中所有项的值都大于 X 中的项。

下面的代码中 Comparable 指的是具有比较功能的,查找是基于 “<” 操作符的,所以必须在 Comparable 中进行定义该操作符。

#ifndef BINARYSEARCHTREE_H
#define BINARYSEARCHTREE_H

template <typename Comparable>
class BinarySearchTree
{
public:
    BinarySearchTree( ):root(nullptr){}
    BinarySearchTree( const BinarySearchTree & rhs );
    ~BinarySearchTree( )
        { makeEmpty(); }

    const Comparable & findMin( ) const
        { return findMin(root)->element; }
    const Comparable & findMax( ) const
        { return findMax(root)->element; }
    bool contains( const Comparable & x ) const
        { return contains(x, root); }
    bool isEmpty( ) const
    {
        if( root == nullptr )
            return true;
        else
            return false;
    }
    void printTree( ) const
        { printTree(root); }

    void makeEmpty( )
        { makeEmpty(root); }
    void insert( const Comparable & x )
        { insert(x, root); }
    void remove( const Comparable & x )
        { remove(x, root); }

    const BinarySearchTree & operator=( const BinarySearchTree & rhs )
    {
        if( this != &rhs )
        {
            makeEmpty();
            root = clone(rhs.root);
        }
        return *this;
    }

private:
    struct BinaryNode
    {
       Comparable element;
       BinaryNode *left;
       BinaryNode *right;

       BinaryNode( const Comparable & theElement, BinaryNode *lt, BinaryNode *rt )
         : element( theElement ), left( lt ), right( rt ) { }
    };

    BinaryNode *root;   // 根节点

    void insert( const Comparable & x, BinaryNode * & t )
    {
        if( t == nullptr ){
            // 如果树中不存在该结点,最终会将树的一个空节点指向这个新节点
            t = new BinaryNode(x, nullptr, nullptr);
        }else if ( x < t->element ) {
            insert( x, t->left );
        }else if ( t->element < x) {
            insert( x, t->right );
        }else {
            ;               // 树中已存在这样的结点,do-nothing
        }
    }
    void remove( const Comparable & x, BinaryNode * & t )
    {
        if( t == nullptr )
            return;
        if( x < t->element )
            remove(x, t->left);
        else if ( t->element < x ) {
            remove(x, t->right);
        }else if ( t->left != nullptr && t->right != nullptr) {
            t->element = findMin(t->right)->element;
            remove(t->element, t->right);
        }else {
            BinaryNode *oldNode = t;
            t = (t->left != nullptr) ? t->left : t->right;
            delete oldNode;
        }
    }
    BinaryNode * findMin( BinaryNode *t ) const
    {
        if( t == nullptr )
            return nullptr;
        if( t->left == nullptr )
            return t;

        return findMin(t->left);
    }
    BinaryNode * findMax( BinaryNode *t ) const
    {
        if( t == nullptr)
            return nullptr;
        if( t->right == nullptr )
            return t;

        return findMax(t->right);
    }
    bool contains( const Comparable & x, BinaryNode *t ) const
    {
        if( t == nullptr )
            return false;
        else if (x < t->element) {
            return contains(x, t->left);
        }else if (t->element < x) {
            return contains(x, t->right);
        }else {
            return true;  // 匹配
        }
    }
    void makeEmpty( BinaryNode * & t )
    {
        if( t != nullptr)
        {
            makeEmpty(t->left);
            makeEmpty(t->right);
            delete t;
        }
        t = nullptr;
    }
    void printTree( AvlNode *t, void *arg ) const   // 后根遍历打印
    {
        if( t == nullptr )
            return;
        if( t->left )
            printTree(t->left, arg);
        if( t->right )
            printTree(t->right, arg);

        printf((char *)arg, t->element);
    }
    BinaryNode * clone( BinaryNode *t ) const
    {
        if( t == nullptr )
            return nullptr;

        return new BinaryNode(t->element, clone(t->left), clone(t->right));
    }
};


#endif // BINARYSEARCHTREE_H

上面是 BinarySearchTree 的实现代码。

一棵树的所有结点的深度和称为 内部路径长(internal path length),记为 D(N)。根结点的深度为 0

可以推得:D(N) = O(NlogN)。所以任意结点预期的深度为 O(logN)。通常是以 2 为底的 log(N) 稍多一些。

上面的代码中,remove 操作始终使用 右侧子树 中的最小点代替被删除的操作,多次的 insert / remove 操作后二叉树明显的不平衡状态。


AVL 树

AVL 树是带有平衡条件的二叉查找树。一颗 AVL 树是每个结点的左子树和右子树的高度最多差 1 的二叉查找树(空树的高度定义为 -1)

对 AVL 树,插入和删除操作会引起它的不平衡。

引起不平衡的插入操作有以下四种情景:

  • (1)对 A 结点的左儿子的左子树进行一次插入
  • (2)对 A 结点的左儿子的右子树进行一次插入
  • (3)对 A 结点的右儿子的左子树进行一次插入
  • (4)对 A 结点的左儿子的右子树进行一次插入

其中(1)和(4)成镜像对称,调整平衡需要 “单旋转” 操作,(2)和(3)成镜像对称,调整平衡需要 “双旋转” 操作。

单旋转:

对应情景(1):对 k2 结点的左儿子的左子树进行一次插入

对应情景(4):对 k1 结点的左儿子的右子树进行一次插入

双旋转:

对于如下的情形,单旋转并不能解决:

因为 Y 过深,所以不管怎么单旋转都无法达到平衡。

对应情景(2):对 k3 结点的左儿子的右子树进行一次插入

对应情景(3):对 k1 结点的右儿子的左子树进行一次插入

下面的代码中 Comparable 指的是具有比较功能的,查找是基于 “<” 操作符的,所以必须在 Comparable 中进行定义该操作符。

#ifndef AVLTREE_H
#define AVLTREE_H

#define max(a, b) ((a)>(b)?(a):(b))

template <typename Comparable>
class AvlTree
{
public:
    AvlTree( ):root(nullptr){}
    AvlTree( const AvlTree & rhs );
    ~AvlTree( )
        { makeEmpty(); }

    const Comparable & findMin( ) const
        { return findMin(root)->element; }
    const Comparable & findMax( ) const
        { return findMax(root)->element; }
    bool contains( const Comparable & x ) const
        { return contains(x, root); }
    bool isEmpty( ) const
    {
        if( root == nullptr )
            return true;
        else
            return false;
    }
    void printTree( void *arg ) const
        { printTree(root, arg); }

    void makeEmpty( )
        { makeEmpty(root); }
    void insert( const Comparable & x )
        { insert(x, root); }
    void remove( const Comparable & x )
        { remove(x, root); }

    const AvlTree & operator=( const AvlTree & rhs )
    {
        if( this != &rhs )
        {
            makeEmpty();
            root = clone(rhs.root);
        }
        return *this;
    }

private:
    struct AvlNode
    {
       Comparable element;
       AvlNode *left;
       AvlNode *right;
       int height;

       AvlNode( const Comparable & theElement, AvlNode *lt, AvlNode *rt, int h = 0 )
         : element( theElement ), left( lt ), right( rt ), height( h ) { }
    };

    AvlNode *root;   // 根节点

    // 获得结点的高度
    int height( AvlNode *t ) const
    {
        return t == nullptr ? -1 : t->height;
    }

    /*
     *              k2              k1
     *             /  \            /  \
     *            k1   A    -->  k3    k2
     *           /  \            |    /  \
     *          k3   B           O   B    A
     *          |
     *          O
     */
    void rotateWithLeftChild(AvlNode * & k2)
    {
        AvlNode *k1 = k2->left;
        k2->left = k1->right;
        k1->right = k2;
        k2->height = max(height(k2->left), height(k2->right)) + 1;
        k1->height = max(height(k1->left), k2->height) + 1;

        //此时该部分的根节点变成 k1,由于是引用,直接赋值就可改变
        //原来 k2 的父节点指向孩子的指针为 k1
        k2 = k1;
    }

    /*
     *              k1              k2
     *             /  \            /  \
     *            A   k2    -->   k1   K3
     *               /  \        /  \   |
     *              B   K3      A    B  O
     *                   |
     *                   O
     */
    void rotateWithRightChild(AvlNode * & k1)
    {
        AvlNode *k2 = k1->right;
        k1->right = k2->left;
        k2->left = k1;
        k1->height = max(height(k1->left), height(k1->right)) + 1;
        k2->height = max(height(k2->right), k1->height) + 1;

        k1 = k2;
    }

    /*
    *        k3              k3         k3            k2          k2
    *       /  \            /  \       /  \          /  \        /  \
    *      k1   B    -->   k2   B 或  k2   B  -->   k1   k3  或 k1   k3
    *     /  \            /  \       /             /    / \    /  \   \
    *    A   k2          k1   O     k1            A    O   B  A    O   B
    *         |         /          /  \
    *         O        A          A    O
    */
    void doubleWithLeftChild(AvlNode * & k3)
    {
        rotateWithRightChild(k3->left);
        rotateWithLeftChild(k3);
    }

    /*
    *        k3              k3           k3             k2          k2
    *       /  \            /  \         /  \           /  \        /  \
    *      A    k1    -->  A   k2    或  A   k2  -->   k3   k1  或 k3   k1
    *          /  \           /  \            \       / \    \    /    /  \
    *         k2   B         O   k1           k1     A   O    B  A    O    B
    *         |                    \         /  \
    *         O                     B       O    B
    */
    void doubleWithRightChild(AvlNode * & k3)
    {
        rotateWithLeftChild(k3->right);
        rotateWithRightChild(k3);
    }

    void rebalance(AvlNode * & t)
    {
        if(t){
            int diff = height(t->left) - height(t->right);
            if(diff == 2)
            {
                if(height(t->left->left) - height(t->right) == 1)
                    rotateWithLeftChild(t);
                else
                    doubleWithLeftChild(t);
            }

            if(diff == -2)
            {
                if(height(t->right->right) - height(t->left) == 1)
                    rotateWithRightChild(t);
                else
                    doubleWithRightChild(t);
            }
        }
    }

    void insert( const Comparable & x, AvlNode * & t )
    {
        if( t == nullptr ){
            // 如果树中不存在该结点,最终会将树的一个空节点指向这个新节点
            t = new AvlNode(x, nullptr, nullptr);
        }else if ( x < t->element ) {
            insert( x, t->left );
            rebalance(t);
        }else if ( t->element < x) {
            insert( x, t->right );
            rebalance(t);
        }else {
            ;               // 树中已存在这样的结点,do-nothing
        }

        t->height = max(height(t->left), height(t->right)) + 1;
    }

    void remove( const Comparable & x, AvlNode * & t )
    {
        if( t == nullptr )
            return;
        if( x < t->element ){
            remove(x, t->left);
            rebalance(t);
        }else if ( t->element < x ) {
            remove(x, t->right);
            rebalance(t);
        }else if ( t->left != nullptr && t->right != nullptr) {
            t->element = findMin(t->right)->element;
            remove(t->element, t->right);
            rebalance(t);
        }else {
            AvlNode *oldNode = t;
            t = (t->left != nullptr) ? t->left : t->right;
            delete oldNode;
        }

        if(t != nullptr)
            t->height = max(height(t->left), height(t->right)) + 1;
    }

    AvlNode * findMin( AvlNode *t ) const
    {
        if( t == nullptr )
            return nullptr;
        if( t->left == nullptr )
            return t;

        return findMin(t->left);
    }
    AvlNode * findMax( AvlNode *t ) const
    {
        if( t == nullptr)
            return nullptr;
        if( t->right == nullptr )
            return t;

        return findMax(t->right);
    }
    bool contains( const Comparable & x, AvlNode *t ) const
    {
        if( t == nullptr )
            return false;
        else if (x < t->element) {
            return contains(x, t->left);
        }else if (t->element < x) {
            return contains(x, t->right);
        }else {
            return true;  // 匹配
        }
    }
    void makeEmpty( AvlNode * & t )
    {
        if( t != nullptr)
        {
            makeEmpty(t->left);
            makeEmpty(t->right);
            delete t;
        }
        t = nullptr;
    }
    void printTree( AvlNode *t, void *arg ) const   // 后根遍历打印
    {
        if( t == nullptr )
            return;
        if( t->left )
            printTree(t->left, arg);
        if( t->right )
            printTree(t->right, arg);

        printf((char *)arg, t->element);
    }
    AvlNode * clone( AvlNode *t ) const
    {
        if( t == nullptr )
            return nullptr;

        return new AvlNode(t->element, clone(t->left), clone(t->right), t->height);
    }
};

/*
 * 只要你碰到*&,就应该想到**。也就是说这个函数修改或可能修改调用者的指针,
 * 而调用者像普通变量一样传递这个指针,不使用地址操作符&。
 */
#endif // AVLTREE_H

测试程序:

int main(int argc, char *argv[])
{
    AvlTree<int> avlTree;
    avlTree.insert(20);
    avlTree.insert(30);
    avlTree.insert(10);
    avlTree.insert(22);
    avlTree.insert(40);
    avlTree.insert(50);
    avlTree.insert(44);
    avlTree.insert(55);
    avlTree.insert(12);
    avlTree.insert(3);

    char buf[5] = " %d ";
    avlTree.printTree((void*)buf);

    avlTree.remove(10);
    avlTree.remove(30);
    avlTree.printTree((void*)buf);

    return 0;
}

输出:3 12 10 22 20 40 55 50 44 30

           3 12 22 20 44 55 50 40

 

猜你喜欢

转载自blog.csdn.net/lc250123/article/details/82117215
今日推荐