学习 严蔚敏讲数据结构笔记17

来源:互联网 发布:做淘宝仓库打包员累吗 编辑:程序博客网 时间:2024/05/19 13:14

5.5 广义表的表示方法

头、尾指针的链表结构

表结点:  

Tag=1

Hp

Tp

 

 

原子结点:

Tag=0

Data

构造存储结构的两种分析方法:

1)  空表 ls=NIL

非空表

若表头为原子,则为

0

Data

否则,依次类推。

 

2)  空表ls=NIL

非空表LS=(a1,a2,…,an)

若子表为原子,则为

0

Data

否则,依次类推

5.6 广义表操作的递归函数

递归函数:一个含直接或间接调用本函数语句的函数被称之为递归函数,它必须满足以下两个条件:

1)在每一次调用自己时,必须是(在某种意义上)更接近于解;

2)必须有一个终止处理或计算的准则。

例如:梵塔的递归函数

30_001

void hanoi(int n, char x, char y, char z)

{

         if(n  == 1)

                   move(x,  1, z);

         else

         {

                   hanoi(n-1,  x, z, y);

                   move(x,  n, z);

                   hanoi(n-1,  y, x, z);

         }

}

例如:二叉树的遍历

30_002

void PreOrderTraverse(BiTree T,  void(Visit)(BiTree P))

{

         if(T)

         {

                   Visit(T->data);

                   PreOrderTraverse(T->lchild,  Visit);

                   PreOrderTraverse(T->rchild,  Visit);

         }

}//PreOrderTraverse

这一类问题可以借助算法设计的一种方法分治法(分割求解)(Divide and Conquer)来求解。

分治法的设计思想为:

对于一个输入规模为n的函数或问题,用某种方法把输入分割成k(1<k<=n)个子集,从而产生l个子问题,分别求解这l个问题,得出l个问题的子解,在用某种方法把它们组合成原来的问题的解,若子问题还相当大,则可以反复使用分治法,直至最后所分得的子问题足够小,以至可以直接求解为止。

在利用分治法求解时,所得子问题的类型常常和原问题相同,因而很自然地导致递归求解。

例如:梵塔问题:Hanoi(n,x,y,z)可以分解成三个子问题

1)  Hanoi(n-1, x, y, z) –递归

2)  Move(n, ‘X’->’Z’)

3)  Hanoi(n-1,y,x,z) –递归

n=1时可以直接求解

二叉树的遍历Traverse(BT)分成三个子问题

1)      Visite(RootNod)

2)      Traverse(LBT) –递归

3)      Traverse(RBT)—递归

对空树不需要遍历

广义表从结构上可以分解成广义表=表头+表尾或者广义表=子表1+子表2+……+子表n

依次常用分治法求解。

广义表的头尾链表存储表示:

30_003

typedef enum{ATOM, LIST} ElemTag;  //ATOM==0:原子,LIST==1:子表

typedef struct GLNode

{

         ElemTag  tag;

         union

         {

                   AtomType  atom;

                   struct{struct  GLNode *hp, *tp}ptr;

         };

} *Glist;

例一求广义表的深度

广义表的深度=Max{z子表的深度}+1

空表的深度= 1 原子的深度=0

30_004

int GlistDepth(Glist L)

{

         if(!  L)

         {

                   return  1;

         }

         if(L->tag  == ATOM)

         {

                   return  0;

         }

         for(max  = 0, pp = L; pp; pp = pp->ptr.tp)

         {

                   dep  = GlistDepth(pp->ptr.hp);

                   if(dep  > max)

                   {

                            max  = dep;

                   }

         }

         return  max + 1;

}//GlistDepth

 

例二复制广义表

ls=NILnewls=NIL

否则由表头ls^.hp复制得newhp由表尾ls^.tp复制得newtp构造结点newls,并使newls^.hp=newhp,newls^.tp=newtp

31_001

Status CopyGList(Glist &T, Glist L)

{

         if(!  L)

         {

                   T  = NULL; //复制空表

         }

         else

         {

                   if(!  (T = (Glist) malloc(sizeof(GLNode))))

                   {

                            exit(OVERFLOW);  //建表结点

                   }

                   T->tag  = L->tag;

                   if(L->tag  == ATOM)

                   {

                            T->atom  = L->atom;

                   }

                   else

                   {

                            CopyGlist(T->ptr.hp,  L->ptr.hp);

                            CopyGlist(T->ptr.tp,  L->ptr.tp);

                   }//else

         }//else

         return  OK;

}

例三创建广义表的存储结构

根据LS=’(a1,a2,…,an)’建广义表ls

LS=’()’ls=NIL

否则构造表结点ls^

分解出第一个子串a1,对应建广义表的表头ls^.hp

若剩余串非空,则构造表尾结点ls^.tp分解出第二个子串a2,对应广义表,依次类推,直至剩余串为空串为止。

31_002

void CreateGList(Glist &L, String S)

{

         if(空串) L = NULL; //创建空表

         else

         {

                   L  = (Glist) malloc(sizeof(GLNode));

                   L->tag  = List;

                   p  = L;

                   sub  = SubString(S, 2, StrLength(S) - 1); //脱外层括弧

                   do

                   {

                            //重复建n个子表

                            sever(sub,  hsub); //分离出子表hsub = a1

                            if(StrLength(hsub)  == 1)

                            {

                                     p->ptr.hp  = (GList) malloc(sizeof(GNolde));

                                     p->ptr.hp->tag  = ATOM;

                                     p->ptr.hp->atom  = hsub; //创建单原子

                            }

                            else

                            {

                                     CreateGList(p->ptr.hp,  hsub); //递归建子表

                            }

                            if(!  StrEmpty(sub)

                            {

                                     p->ptr.tp  = (Glist) malloc(sizeof(GLNode));

                                     p  = p->ptr.tp; //建表尾结点*p

                            }

                   }while(!  StrEmpty(sub));

                   p->ptr.tp  = NULL;

         }//else

}

删除单链表中所有值为x的数据元素

分析:

1)  单链表是一种顺序结构,必须从第一个结点起,逐个检查每个结点的数据元素;

2)  从另一角度看,链表又是一个递归结构,若L是线性表(a1,a2,…,an)的头指针,则L->next是线性链表(a2,…,an)的头指针。

31_003

void delete(LinkList &L, ElemType x)

{

         //L为无头结点的单链表的头指针

         if(L)

         {

                   if(L->data  = x)

                   {

                            p  = L;

                            L  = L->next;

                            free(p);

                            delete(L,  x);

                   }

                   else

                   {

                            delete(L->next,  x);

                   }

         }

}

删除广义表中所有元素为x的原子结点

分析:广义表和线性表比较:

相似处:都是顺序结构

不同处:广义表的数据元素可能还是广义表;删除时,不仅要删除原子结点,还需要删除相应的表结点

 

综合几点:

1.      对于含有递归特性的问题,最好设计递归形式的算法。但也不要单纯追求形式,应在算法设计的分析过程中“就事论事”。例如,在利用分割求解设计算法时,子问题和原问题的性质相同;或者,问题的当前一步解决之后,余下的问题性质相同,则自然导致递归求解。

2.      实现递归函数,目前必须利用“栈”。一个递归函数必定能改写为利用栈实现的非递归函数函数;反之,一个用栈实现的非递归函数可以改写为递归函数。需要注意的是递归层次的深度决定所需存储量的大小。

3.      分析递归算法的工具是递归树,从递归树上可以得到递归函数的各种相关信息。例如:递归树的深度即为递归函数的递归深度;递归树上的结点数目恰为函数中的主要操作重复进行的次数;若递归树蜕化为单枝树或者递归树中含有很多相同的结点,则表明该递归函数不适用。

4.      递归函数中的尾递归容易消除。

32_001

void PreOrderTraverse(BiTree T)

{

         While(T)

         {

                   Visit(T->data);

                   PreOrderTraverse(T->lchild);

                   T  = T->rchild;

         }

}//PreOrder

5.      可以用递归方程来表述递归函数的时间性能。

例如:假设解n个圆盘的梵塔的执行时间为T(n),则递归方程为:T(n)=2T(n-1)+C,初始条件为:T(0)=0

 

5章广义表

补:

4.掌握广义表的结构特点及其存储表示方法,读者可以根据自己的学习习惯熟练掌握任意一种结构的链表,学会对非空广义表进行分解的两种分析方法:即可将一个非空广义表分解为表头和表尾两部分或者分解为n个子表。

5.学习利用分治法的算法设计思想编制递归算法的方法。

 

原创粉丝点击