自顶向下伸展树的实现与测试-建立自己的c数据结构与算法库系列(14)

不说费话了,直接进入主题。
伸展树的主要特点:每次访问某个节点时,都把此节点旋转到根部。保证从空树开始任意M次操作最多花费O(MlogN)的时间,也就是说它的摊还时间为O(F(N))。
伸展数的主要难点:展开函数的实现。
基本操作插入、删除、查找都和展开函数相关。
插入:在根处按要插入的元素展开树,如果树中已经存在此节点,则此节点在展开后就是根;如果不存在节点,根处的元素是比欲插入节点小的最大节点或比之大的最小节点。插入时则把元素插入根处!
删除:按欲删除的元素展开根,如果此元素存在,则根就是它。删除就删除它吧!^_^请看实现的代码,你会惊奇的发现,相比AVL树的删除,它竟然是如此的简单!
查找:也就是展开树了,欲查找的元素会被旋转到根部。
注意:由于每次访问都会发生旋转,树的根都可能发生变化,所以一定要注意在每一次访问(查找,删除,插入)时要保存新根!

自顶向下展开实现:
一些书上认为伸展树有三种旋转操作:即单旋转/一字旋转/之字旋转。
而我觉得实际上可以认为只有一字旋转/之字旋转,就看你是怎么旋转的思想的,其实至于是什么旋转我们不必归类得这么清晰。我们只要透彻理解展开的思想就行了,即:
设 Splay(e,t)表示从树t中找e,在根处展开,把e旋转到根处,设pcur指向当前的根,有临时节点header,其右孩子用于保存所有比pcur小的节点组成的子树,左孩子节点保存所有比pcur大的节点组成的子树,则从pcur往e的查找路径中,用leftTreeMax把比pcur小的子树串到header的右子树上,用rightTreeMin把比pcur大的子树串到header的左子树上.
(这样看这些文字是非常抽象的,请参见SP_Splay的实现)

在最终找到节点e时,它的左子树和右子树最后一次分离(通过leftTreeMax和rightTreeMin)到header的左右子树上,然后当前的e点左孩子取header的右孩子,e的右孩子取header的左孩子,展开即结束。


源代码:

/**SplayTree.h*/
/**********************************
*author:Felix
*last update:Thu Jan 24 07:56:48 EST 2008
*Address me and exchange our idea ^_^,QQ:349871604,e-mail:lzqziliao2004@163.com
*description:
*
*
*
*/
#ifndef ___SPLAYTREE___
#define ___SPLAYTREE___
#ifndef ___SP_ELEMENT___
#include<stdio.h>
#include<stdlib.h>
typedef int SP_Element;
#endif
struct SP_TreeNode;
typedef struct SP_TreeNode * SP_SplayTree;
typedef struct SP_TreeNode * SP_Position;

/*
if t is not NULL ,free it and return NULL.
*/
SP_SplayTree SP_MakeEmpty(SP_SplayTree t);
SP_SplayTree SP_FindMax(SP_SplayTree t);
SP_SplayTree SP_FindMin( SP_SplayTree t);

/*
 splay the tree.
 if the root is e return immediately
 or insert e as a root and rejust it's left and right child.
*/
SP_SplayTree SP_Insert(SP_Element e,SP_SplayTree t);

/*
 splay the tree .
 if e is found ,delete and rejust it's root
*/
SP_SplayTree SP_Delete(SP_Element e,SP_SplayTree t);

/*find an element e,if element e found ,
it's the root or the root is the one which
is the biggest one in those smaller than e or
the smallest one int those bigger than e
*/
SP_SplayTree SP_Find(SP_Element e,SP_SplayTree t);

/*get the root element**/
SP_Element SP_Retrieve(SP_Position t);



#endif


/***SplayTree.c****/
/**********************************
*author:Felix
*last update:Thu Jan 24 07:56:48 EST 2008
*Address me and exchange our idea ^_^,QQ:349871604,e-mail:lzqziliao2004@163.com
*description:
*
*
*
*/
#include "SplayTree.h"
/**definition and declaration*/
struct SP_TreeNode
{
 SP_Element e;
 SP_SplayTree left;
 SP_SplayTree right;
};
/*
  main function :splay the tree and return root;
*/
static SP_SplayTree SP_Splay(SP_Element e,SP_Position t);
static SP_Position SP_SingleRotateLeft(SP_Position p);
static SP_Position SP_SingleRotateRight(SP_Position p);

/**definition and declaration end*/


/*rotate function*/
SP_Position SP_SingleRotateLeft(SP_Position p)
{
   SP_Position tmp;
   tmp=p->left;
   p->left=tmp->right;
   tmp->right=p;
   return tmp;
  
}
SP_Position SP_SingleRotateRight(SP_Position p)
{
    SP_Position tmp;
    tmp=p->right;
    p->right=tmp->left;
    tmp->left=p;
    return tmp;
}
static SP_SplayTree SP_Splay(SP_Element e,SP_Position p)
{
  /*
  header stores the maxs and mins than e by
  using leftTreeMax and rightTreeMin sticking
  Nodes to itself;
  */
   struct SP_TreeNode header;
   SP_SplayTree leftTreeMax ,rightTreeMin;
   header.left=header.right=NULL;
   leftTreeMax=rightTreeMin=&header;
   if (!p)return NULL;
   while(e!=SP_Retrieve(p)) 
   {
      if(e>SP_Retrieve(p))
      {
         if(!p->right)break;
         if(e>SP_Retrieve(p->right))p=SP_SingleRotateRight(p);
         if(!p->right)break;
         leftTreeMax->right=p;
         leftTreeMax=p;
         p=p->right;
      }
      else
      {
         if(!p->left)break;
         if(e<SP_Retrieve(p->left))p=SP_SingleRotateLeft(p);
         if(!p->left)break;
         rightTreeMin->left=p;
         rightTreeMin=p;
         p=p->left;
          
      }
   }
   leftTreeMax->right=p->left;   
   rightTreeMin->left=p->right;
   p->left=header.right;
   p->right=header.left;

}
/*
if t is not NULL ,free it and return NULL.
*/
SP_SplayTree SP_MakeEmpty(SP_SplayTree t)
{
  if(t)
  {
    if(t->left)SP_MakeEmpty(t->left);
    if(t->right)SP_MakeEmpty(t->right);
    free(t);
  }
 return NULL;
}
SP_SplayTree SP_FindMax(SP_SplayTree t)
{
SP_SplayTree tmp=t;
  if(!tmp)return NULL;
  while(tmp->right)tmp=tmp->right;
 return SP_Splay(SP_Retrieve(tmp),t);
}
SP_SplayTree SP_FindMin( SP_SplayTree t)
{
SP_SplayTree tmp=t;
  if(!tmp) return NULL;
  while(tmp->left)tmp=tmp->left;
 return SP_Splay(SP_Retrieve(tmp),t);
}

/*
 splay the tree.
 if the root is e return immediately
 or insert e as a root and rejust it's left and right child.
*/
SP_SplayTree SP_Insert(SP_Element e,SP_SplayTree t)
{
   struct SP_TreeNode *newnode;
   if(t)t=SP_Splay(e,t);
   if(!t||e!=SP_Retrieve(t))
    {
        newnode = (struct SP_TreeNode*)malloc(sizeof(struct SP_TreeNode));
        newnode->e=e;
        if(!t)
        newnode->left=newnode->right=NULL;
       else
        if(e>SP_Retrieve(t))
        {
           newnode->left=t;
           newnode->right=t->right;
           t->right=NULL;
        }
        else
        {
          newnode->right=t;
          newnode->left=t->left;
          t->left=NULL;
        }
    }
  return (SP_SplayTree)newnode;
}

/*
 splay the tree .
 if e is found ,delete and rejust it's root
*/
SP_SplayTree SP_Delete(SP_Element e,SP_SplayTree t)
{
  SP_SplayTree tmp;
  if(t==NULL)return NULL;
   tmp=t=SP_Splay(e,t);
   if(SP_Retrieve(t)==e)
   {
     if(t->left==NULL)
     tmp=t->right;
     else
     {
         t->left=SP_Splay(e,t->left);
         t->left->right=t->right;
         tmp=t->left;
         free(t);
     }
   }
  return tmp;
}

/*find an element e,if element e found ,
it's the root or the root is the one which
is the biggest one in those smaller than e or
the smallest one int those bigger than e
*/
SP_SplayTree SP_Find(SP_Element e,SP_SplayTree t)
{
    return SP_Splay(e,t);
}

/*get the root element**/
SP_Element SP_Retrieve(SP_Position t)
{
  return t->e;
}


/****测试文件,这里用到前面文章提到的绘制二叉树的函数,如果你无法编译通过,请自己编写测试文件,或者找我**/

/**testSplayTeee.c**/
/*vgalib库可能不太稳定。
*author:Felix
*create date:Tue Jan 15 20:14:10 EST 2008
*last update:Tue Jan 15 20:14:10 EST 2008
*description:
*
*/
#include "menu_c.h"
#include<stdio.h>
#include<stdlib.h>
#include"SplayTree.h"
#include"drawBitTree.h"
struct SP_SplayTreeNode
{
  SP_Element e;
  SP_SplayTree left;
  SP_SplayTree right;
};
/******define some function to show the tree*******/
 int getkey(void * t)
{
   return ((struct SP_SplayTreeNode*)t)->e;
}
void * getchild(void * t,int mode)
{
  if(mode==DB_LEFTCHILD)return ((struct SP_SplayTreeNode*)t)->left;
  else return ((struct SP_SplayTreeNode*)t)->right;
}
/*****************/
int main()
{
int n;
SP_SplayTree t;
SP_Position p;
t=SP_MakeEmpty(NULL);

while(SELECT())
{
 switch (SELECTION)
 {
 case '1':
  printf("integer:>");
  scanf("%d",&n);
  t=SP_Insert(n,t);

 break;
 case '2':
  printf("integer to delete:>");
  scanf("%d",&n);
  t=SP_Find(n,t);
  if(SP_Retrieve(t)!=n)printf("sorry,no integer match");
  else {
   t=SP_Delete(n,t);
   printf("deletion done");
  }
 break;
 case '3':
  if(t=SP_FindMin(t))
  {
     printf("%d " ,SP_Retrieve(p),t);
   t= SP_Delete(SP_Retrieve(t),t);
  }
 break;
 case '4':
  if(t=SP_FindMax(t))
  {
     printf("%d " ,SP_Retrieve(t));
    t=SP_Delete(SP_Retrieve(t),t);
  }
 break;
 case '5':
  if(!(t=SP_FindMax(t)))printf("sorry,the tree is NULL");
else
  printf("%d",SP_Retrieve(t));
 break;
 case '6':
  if(!(t=SP_FindMin(t)))printf("sorry ,the tree is NULL");
  else
  printf("%d",SP_Retrieve(t));
 break;
 case '7':
  
   DB_DrawBitTree(t,getchild,getkey);
 break;
 case '9':
 system("less ../SP_tree.h");
 break;
 default:break;
 }
}
return 0;
}
/**menu_c.h**/
/*///
*author:Felix
*create date:Fri Jan 11 03:45:18 EST 2008
*last update:Fri Jan 11 03:45:18 EST 2008
*description:
*
*
*/
#include <stdio.h>
#ifndef __MENU____
#define __MENU____
#define SELECT() ((___sel=___select())!='0')
#define SELECTION ___sel
char ___sel;
char ___select();
/*
 define the menu:
*/
#endif
/**menu_c.c*/
/*///
*author:Felix
*create date:Fri Jan 11 03:45:18 EST 2008
*last update:Fri Jan 11 03:45:18 EST 2008
*description:
*
*
*/
#include "menu_c.h"
char *___menu[]={
 
"1.Insert a integer.",
"2.Delete an integer from the tree.",
"3.Sort and show from small to big(will delete the tree)",
"4.Sort and show from big to small(will delete the tree)",
"5.show the max",
"6.show the min", "9.Print the file ST_tree.h",
"7.show the tree",
"0.EXIT",
NULL
};
void ___showMenu()
{
 printf("please select:/n");
 char **p=___menu;
 while(*p)printf("%s/n",*p++);
 printf(":>");
}
char ___select()
{
char c;
 ___showMenu();
 while((c=getchar())=='/n');
 return c;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值