s
零零散散花了近半天的时间,查看各种资料,总算是完成了头尾链表存储结构的广义表的建立以及一种输出。
第一次写一篇文章记录一下此时的思路,唉,免不了过两天就忘了!
一 ,首先,结构体的定义很常规。
typedef enum {ATOM,LIST} Elemtag;
typedef struct Glong {
Elemtag tag;
union {
char atom;
struct {
struct Glong *hp,*tp;
} htp;
} un;
}*Glist;
这里懒得加注释了。
二,主程序框架
int main()
{
Glist head; /* 头节点指针 */
fgets(str,100,stdin); /* 输入的字符串 */
head=Create(str); /* Create函数为这个创建函数 */
output(head); /* 输出函数 */
return 0;
}
三,广义表创建函数
Glist Create(char *str)
{
Glist p;
char s[3];
s[0]='(',s[1]=')',s[2]='\0';
if(Compare(str,s)==1) { /* 比较是否为空表 */
return NULL;
} else {
p=(Glist)malloc(sizeof(struct Glong)); /* 无论后面是要表结点还是原子节点都要建立节点,所以在最前面建立,后面进行标识化 */
if(str[0]>='a'&&str[0]<='z') { /* 如果是字母,说明是原子节点 */
p->tag=ATOM;
p->un.atom=str[0];
return p;
}
if(str[0]=='(') { /* 如果有括号,结合前面非空表可知,此处一定不是空表,而且一定有一个表节点 */
char *headstr,*tailstr;
headstr=NULL,tailstr=NULL; /* 建立表头所依据的字符串 和 建立表尾所依据的字符串 */
p->tag=LIST;
Remove(str); /* 去点最外层括号,这样后面就方便得到headstr,tailstr了 */
str++;
headstr=str;
Divide(&tailstr,str); /* 因为不是空表,所以表头字符串一定存在,所以我只需要看表尾是否为空,这里如果没有表尾,我将其理解为空 */
/* 因为要改变tailstr的值所以用二重指针 */
p->un.htp.hp=Create(headstr); /* 创建表头节点 */
if(tailstr!=NULL) {
addpar(tailstr); /* !!!!由于广义表定义上将除第一个元素外的其他元素看成一个表,称作表尾,但在形式上并没有最外层象征表的括号 */
/* 所以我表尾字符串加上最外层的括号 */
tailstr--;
p->un.htp.tp=Create(tailstr); /* 建立表尾节点 */
} else {
p->un.htp.tp=NULL;
}
}
}
return p;
}
其中也用到一些辅助函数:
Remove函数
void Remove(char *sdr) /* 去括号函数,即把括号换成字符# */
{
int i,k;
i=1,k=1;
sdr[0]='#';
while(sdr[i]!='\0') {
if(sdr[i]=='(') {
k++;
i++;
continue;
}
if(sdr[i]==')') {
k--;
if(k==0) {
break;
}
}
i++;
}
sdr[i]='#';
}
这里如果把最外层括号变为空字符的话,那么因为开头就是空字符,系统将此字符串视作空,不方便,而我只需要一个规定一个符号为逻辑上的空符即可。如果有uu们有好的解决办法,欢迎指教。
Divide函数,分离出表尾字符串的
void Divide(char **q,char *str)
{
int i,k;
i=0,k=0;
while(str[k]!='\0'&&str[k]!='#') {
if(str[k]=='(') {
i++;
}
if(str[k]==')') {
i--;
}
if(i==0&&str[k]==',') {
str[k]='#';
*q=str+k+1;
break;
}
k++;
}
}
至于为什么写Compare函数,是因为一时间解决不了strcmp函数参数要为字符串常量的问题
int Compare(char *str,char *st)
{
int i=0;
while(str[i]!='\0'&&st[i]!='\0')
{
if(str[i]>st[i])
{
return 0;
}
if(str[i]<st[i])
{
return 0;
}
i++;
}
if(str[i]=='\0'&&st[i]=='\0')
{
return 1;
}else{
return 0;
}
}
最后一个加括号函数
void addpar(char *p)
{
p--;
p[0]='(';
int i=1;
while(p[i]!='#'&&p[i]!='\0') {
i++;
}
p[i]=')';
}
输出的话一个递归就可以了
void output(Glist p)
{
if(p!=NULL) {
if(p->tag==LIST) {
output(p->un.htp.hp);
output(p->un.htp.tp);
} else {
printf("%c",p->un.atom);
return;
}
}
}
四,举例验证此方法
如果给定字符串为“(a,(b,c)) ”,那么进入程序
非空表--------->>>>建立表结点1,字符串处理,去括号后为“ #a,(b,c)# ”,str指针就指向a,
分开后变成“ #a#(b,c)# ”,headstr指向a,tailstr指向第二个#
然后递归调用自身建立原子节点a,结束此递归。
给表尾字符串加括号变成“ #a((b,c))\0 ”,此时tailstr指针指向第一个( 。
然后递归调用建立表尾节点2。
字符串去括号 “ #a#(b,c)# ” headstr指向第一个( 。
表尾为空,那个就再递归建表头。
有括号,建立表结点3。再去括号,有表头表尾,再递归建表头的原子节点b,而c要加上括号的。
所以递归建表尾的时候又会有一个表结点。然后也是只有表头c,没有表尾。
所以建表头的原子节点c。
到此结束。
不喜勿喷,有问题可以留言讨论。作者是个菜逼(先自报家门,事先申明)。