需要在了解广义表这种数据结构的基础上才能读懂本文。也就是说,如果能看懂我的上一篇博客,应该就能看懂这篇博客。http://blog.csdn.net/sinat_34927324/article/details/53932226(上篇博客的链接)
书上的算法实在是难懂,去翻看别人博客上的算法,好像比我的教科书上的要简单一些,但是好像没有处理子表是共享表的功能。于是我用了两个晚上才看懂书上的算法,在这个跨年夜拿出来跟大家分享一下。哈哈
假设一个广义表的元素类型是字符型,每一个元素是英文字母,并假定广义表从输入流对象istream输入。输入形式如:
"D( B(a,b), C( u, (x,y,z) ), A(#) );"
输入流建立起来的广义表如下图所示。
我们可以看到输入的字符串里面有大写英文字母(代表子表结点),小写英文字母(代表原子结点),左右括号,逗号,#号还有分号。
在算法的执行过程中,对于输入流对象输入的一个字符,检测它的内容。如果遇到用大写英文字母表示的表名,首先检查这个表名是否存在,如果是,说明该表是共享表,只要将附加头结点的引用计数加一即可;如果不是,保存该表名并建立相应广义表。表名后面一定是左括号‘(’,如果不是,说明输入错误,是则建立广义表结构。如果遇到小写字母表示的原子,则建立原子元素结点;如果遇到右括号‘)’,子表链收尾并退出递归。
我们的任务就是设计一个算法,根据输入字符串来建立如图所示的广义表。
"D( B(a,b), C( u, (x,y,z) ), A(#) );"
我们从左到右输入这个字符串,第一个输入的是D,常理来说此时我们应该建立广义表D的一个附加头结点,但是为了把大写字母统一操作。
(1)我们此时建立一个子表结点。(=0附加头结点;=1原子结点;=2子表结点)
(2)然后我们知道之后扫描到的左括号是没有作用的,因此我们应该在此处再输入一个字符使程序跳过左括号。
(3)建立完子表结点后我们需要先建立这个子表,然后再建立后继表(这两处都是递归)。因此我们首先要建立子表的附加头结点。
建立完附加头结点之后我们要建立ls的子表Createlist(ls->info.hlink->tlink)参数指针简称ls1,在这一层递归中我们输入的字符轮到了B,因此我们重复进行上面三步的操作:
同理我们又递归调用Createlist(ls1->info.hlink->tlink):参数指针简称ls2 建立ls1的子表,这一层递归中我们输入的字符是a,因此我们需要建立一个原子结点。
建立完原子结点后我们继续递归调用建立它的该子表的后继表Createlist(ls2),至于此处为什么是ls2不是ls2->tlink,是因为在这一层递归中我们将要输入的字符串是逗号“,”,因此我们需要跳过这个逗号,在判断输入字符为逗号后我们才调用Createlist(ls2->tlink)指针简称ls3来继续建立后继表。
跳过了逗号后,在这一层递归中我们输入的字符是b,因此我们再次建立原子结点
又递归调用Createlist(ls3),这一层递归中我们输入的字符是右括号,表示子表建立完毕。此时我们另ls,3->tlink=NULL,表示ls1的子表被建立完毕。递归返回到ls1的递归空间接着建立它的后继表Createlist(ls1),至于此处参数为什么不是ls1->tlink,如上所述因为下一个符号是逗号。
后面同理递推就可以根据输入流建立起这个广义表啦。
现在大家再看这段代码,思路是否更清晰一点了呢?
void Createlist(istream& istr,GenListNode *&ls){
char chr;
istr>>chr;
if(isalpha(chr)&&isupper(chr)||chr=='('){
ls=new GenListNode;
ls->utype=2;
istr>>chr;
if(chr!='(')
exit(1);
ls->info.hlink=new GenListNode;
ls->info.hlink->utype=0;
ls->info.hlink->info.ref=1;
Createlist(istr,ls->info.hlink->tlink,L1,L2);
Createlist(istr,ls,L1,L2);
}
else if(isalpha(chr)&&islower(chr)){
ls=new GenListNode;
ls->utype=1;
ls->info.value=chr;
Createlist(istr,ls,L1,L2);
}
else if(chr==',')
Createlist(istr,ls->tlink,L1,L2);
else if(chr==')')
ls->tlink=NULL;
else if(chr=='#')
ls=NULL;
}
细心的朋友不难发现这段代码建立起来的广义表还有一点问题,就是多了一个子表结点,只要再通过另一个函数把该子表结点删掉,然后把头指针指向正确的位置就可以了。
如果建立的表中含有共享表,我们只要把代码稍作改动就可以了
void Createlist(istream& istr,GenListNode *&ls,SeqList& L1,SeqList& L2){
char chr;
istr>>chr;
if(isalpha(chr)&&isupper(chr)||chr=='('){
ls=new GenListNode;
ls->utype=2;
if(isalpha(chr)&&isupper(chr)){
int m=L1.Search(chr);
int n=L1.Length();
if(m!=0){
GenListNode *p=NULL;
L2.getData(m,p);
p->info.hlink->info.ref++;
}
else{
L1.Inseart(n,chr);
L2.Inseart(n,ls);
}
istr>>chr;
if(chr!='(')
exit(1);
}
ls->info.hlink=new GenListNode;
ls->info.hlink->utype=0;
ls->info.hlink->info.ref=1;
Createlist(istr,ls->info.hlink->tlink,L1,L2);
Createlist(istr,ls,L1,L2);
}
else if(isalpha(chr)&&islower(chr)){
ls=new GenListNode;
ls->utype=1;
ls->info.value=chr;
Createlist(istr,ls,L1,L2);
}
else if(chr==',')
Createlist(istr,ls->tlink,L1,L2);
else if(chr==')')
ls->tlink=NULL;
else if(chr=='#')
ls=NULL;
}