C实现伸展树(splay tree)

475 阅读3分钟

伸展树定义

伸展树实现

通用头文件 fatal.h

#include <stdio.h>
#include <stdlib.h>

#define Error( Str )        FatalError( Str )
#define FatalError( Str )   fprintf( stderr, "%s\n", Str ), exit( 1 )

声明头文件 splay.h

        #include <stdlib.h>
        #include "fatal.h"

        typedef int ElementType;
        #define Infinity 30000
        #define NegInfinity (-30000)

        #ifndef _Splay_H
        #define _Splay_H

        struct SplayNode;
        typedef struct SplayNode *SplayTree;

        SplayTree MakeEmpty( SplayTree T );
        SplayTree Find( ElementType X, SplayTree T );
        SplayTree FindMin( SplayTree T );
        SplayTree FindMax( SplayTree T );
        SplayTree Initialize( void );
        SplayTree Insert( ElementType X, SplayTree T );
        SplayTree Remove( ElementType X, SplayTree T );
        ElementType Retrieve( SplayTree T );  /* Gets root item */

        #endif  /* _Splay_H */

实现文件 splay.c

        #include "splay.h"
        #include <stdlib.h>
        #include "fatal.h"
        
        struct SplayNode
        {
            ElementType Element;
            SplayTree      Left;
            SplayTree      Right;
        };

        typedef struct SplayNode *Position;
        static Position NullNode = NULL;  /* Needs initialization */

        SplayTree
        Initialize( void )
        {
            if( NullNode == NULL )
            {
                NullNode = malloc( sizeof( struct SplayNode ) );
                if( NullNode == NULL )
                    FatalError( "Out of space!!!" );
                NullNode->Left = NullNode->Right = NullNode;
            }
            return NullNode;
        }

        static SplayTree Splay( ElementType Item, Position X );

        SplayTree
        MakeEmpty( SplayTree T )
        {
            if( T != NullNode )
            {
                MakeEmpty( T->Left );
                MakeEmpty( T->Right );
                free( T );
            }
            return NullNode;
        }

        void
        PrintTree( SplayTree T )
        {
            if( T != NullNode )
            {
                PrintTree( T->Left );
                printf( "%d ", T->Element );
                PrintTree( T->Right );
            }
        }

        SplayTree
        Find( ElementType X, SplayTree T )
        {
            return Splay( X, T );
        }

        SplayTree
        FindMin( SplayTree T )
        {
            return Splay( NegInfinity, T );
        }

        SplayTree
        FindMax( SplayTree T )
        {
            return Splay( Infinity, T );
        }

        /* This function can be called only if K2 has a left child */
        /* Perform a rotate between a node (K2) and its left child */
        /* Update heights, then return new root */


        // 一字型 从左往右旋转一次
        static Position
        SingleRotateWithLeft( Position K2 )
        {
            Position K1;

            K1 = K2->Left;
            K2->Left = K1->Right;
            K1->Right = K2;

            return K1;  /* New root */
        }

        /* This function can be called only if K1 has a right child */
        /* Perform a rotate between a node (K1) and its right child */
        /* Update heights, then return new root */

        // 一字型 从右往左旋转
        static Position
        SingleRotateWithRight( Position K1 )
        {
            Position K2;

            K2 = K1->Right;
            K1->Right = K2->Left;
            K2->Left = K1;

            return K2;  /* New root */
        }

/* START: fig12_6.txt */
        /* Top-down splay procedure, */
        /* not requiring Item to be in tree */


        // Header用来作为一个 临时根节点 它的右/左子节点只被修改一次 分别指向LeftTreeMax/RightTreeMin最初一次赋值的节点 请注意这里的左右对应关系 理解这个很重要
        // LeftTreeMax 左侧子树 通过不断拓展它的右子节点来保证左侧字树的二叉排序性
        // RightTreeMin 右侧字数 通过不断拓展它的左子节点来保证右侧子树的二叉排序性
        SplayTree
        Splay( ElementType Item, Position X )
        {
            static struct SplayNode Header;
            Position LeftTreeMax, RightTreeMin;

            Header.Left = Header.Right = NullNode;
            LeftTreeMax = RightTreeMin = &Header;
            NullNode->Element = Item;

            while( Item != X->Element )
            {
                if( Item < X->Element )
                {
                    if( Item < X->Left->Element )
                        X = SingleRotateWithLeft( X );
                    if( X->Left == NullNode )
                        // 目标item不在tree中 把最后次比较的X当做根节点顶上去
                        break;
                    /* Link right */
                    // 第一个赋值的时候将Header的left指向第一个识别出来的右侧子树的根节点
                    // 然后接下来不断拓展右子树的左节点 并且移动它
                    RightTreeMin->Left = X;
                    RightTreeMin = X;
                    X = X->Left;
                }
                else
                {
                    if( Item > X->Right->Element )
                        X = SingleRotateWithRight( X );
                    if( X->Right == NullNode )
                        // 目标item不在tree中 把最后次比较的X当做根节点顶上去
                        break;
                    /* Link left */
                    // 第一个赋值的时候将Header的right指向第一个识别出来的左侧子树的根节点
                    // 然后接下来不断拓展右子树的右节点 并且移动它
                    LeftTreeMax->Right = X;
                    LeftTreeMax = X;
                    X = X->Right;
                }
            }  /* while Item != X->Element */

            /* Reassemble */
            // 迭代结束 各个变量值为:
            // X:我们的目标item或者最后一次比较的X
            // LeftTreeMax 一颗二叉搜索树 所有节点的值都比X的值小
            // RightTreeMin 一颗二叉搜索树 所有节点的值都比X的值大
            // Header.Right 在第一次被赋值的时候指向了LeftTreeMax的根节点
            // Header.Left 在第一次被赋值的时候指向了RightTreeMin的根节点
            // 我们把X作为新节点返回即可 以下是交换各个子树的操作
            LeftTreeMax->Right = X->Left;
            RightTreeMin->Left = X->Right;
            X->Left = Header.Right;
            X->Right = Header.Left;

            return X;
        }
/* END */




/* START: fig12_7.txt */
        SplayTree
        Insert( ElementType Item, SplayTree T )
        {
            static Position NewNode = NULL;

            if( NewNode == NULL )
            {
                NewNode = malloc( sizeof( struct SplayNode ) );
                if( NewNode == NULL )
                    FatalError( "Out of space!!!" );
            }
            NewNode->Element = Item;

            if( T == NullNode )
            {
                NewNode->Left = NewNode->Right = NullNode;
                T = NewNode;
            }
            else
            {
                // 调用一次伸展函数 让根节点距离item很接近 方便进行插入
                T = Splay( Item, T );
                if( Item < T->Element )
                {
                    NewNode->Left = T->Left;
                    NewNode->Right = T;
                    T->Left = NullNode;
                    T = NewNode;
                }
                else
                if( T->Element < Item )
                {
                    NewNode->Right = T->Right;
                    NewNode->Left = T;
                    T->Right = NullNode;
                    T = NewNode;
                }
                else
                    return T;  /* Already in the tree */
            }

            NewNode = NULL;   /* So next insert will call malloc */
            return T;
        }
/* END */


/* START: fig12_8.txt */
        SplayTree
        Remove( ElementType Item, SplayTree T )
        {
            Position NewTree;

            if( T != NullNode )
            {
                // 调用一次伸展函数 直接在根节点进行删除
                T = Splay( Item, T );
                if( Item == T->Element )
                {
                    /* Found it! */
                    if( T->Left == NullNode )
                        NewTree = T->Right;
                    else
                    {
                        NewTree = T->Left;
                        NewTree = Splay( Item, NewTree );
                        NewTree->Right = T->Right;
                    }
                    free( T );
                    T = NewTree;
                }
            }

            return T;
        }

/* END */

        ElementType
        Retrieve( SplayTree T )
        {
            return T->Element;
        }

参考资料:

《数据结构与算法分析: C语言描述》 www.cnblogs.com/kernel_hcy/…