广义表是一种链式存储结构,广义表的表示方法有多种,在稍后的博文中我会依次给出各种广义表的算法代码实现。
首先简单的描述一下广义表的数据结构,广义表一般分为列表结点和原子结点。广义表中可以包含另一个广义表,这就使用广义表具有递归性,在下面的广义表创建中,就利用递归的思想进行创建。
对于广义表的表头表尾的理解:
比如广义表:A = (a,(b,c),((d,e,f),g)) ,其中B = (b,c) C = ((d,e,f),g) D = (d,e,f)
注意这里的表示方法: 小写字母表示原子结点,大写子表表示列表结点,其实也就是一个广义表
所以上面的表示方法也就可以写成: A = (a,B,C) 或者A = (a,B,(D,g))
广义表的表头可以是原子结点或者列表结点
广义表的表尾只能是列表结点
比如Head(A) = a Tail(A) = ( (b,c),((d,e,f)g) ) 或者(B,C)
Head(D) = d Tail(D) = (e,f)
Head(C) = (d,e,f) 或则D Tail(C) = (g)
如果不是很清楚,查看下数据结构的书籍。总之记住广义表的表尾必须是表就行了,这就是为什么要加一对括号的原因。
比如一些特殊的广义表:
空表 () 程序代号NIL 没有表头和表尾 长度为0
(()) 这是一个非空表 表头为( ) 表尾为 ( ) 长度为1
程序中的广义表字符表示: "(a,(b,c),(e,f,(g)))"
#include <iostream>
#include <string.h>
#include <stdlib.h>
#define ERROR -1
#define OVER_FLOW 0
#define OK 1
#define MAX_STR_LEN 100
char hstr[MAX_STR_LEN] = {0};
char istr[MAX_STR_LEN] = {0};
typedef int Status;
using namespace std;
typedef int AtomType;
typedef enum {ATOM, LIST} ElemTag;
typedef struct GLNode
{
ElemTag tag; //公共部分,用于区分原子和列表结点
union{ //共用体,共享同一片内存空间,大小由最大的元素决定
AtomType atom; //原子结点的的值域
struct{
struct GLNode * hp; //列表的表头指针
struct GLNode * tp; //列表的表尾指针
}prt;
};
}*GList;
/*建立广义表的思想:
广义表结点分为列表结点和原子结点,当L为单个字符的时候,为原子结点。各个表中结点用逗号分隔
建立广义表可以用递归思想实现,将一个表中的表头和表尾分别提取出来,分别建立表头和表尾的广义表,
建立过程中注意将递归得出的广义表进行连接*/
void SubString(char *sub, char *p,int start,int end){
char *temp = p+start;
for(; temp <= p+end; temp++){
*sub++ = *temp;
}
*sub = '\0';
}
void SplitHeadStr(char *&inputstr,char *&headstr){
//将非空串inputstr分割成两个部分,headstr为第一个','之前的子串,inputstr为之后的字串
int n = strlen(inputstr);
int i = 0;
int k = 0; //记录尚为配对的左括号的个数
do{
if(inputstr[i] == '(')
k++;
else if(inputstr[i] == ')')
k--;
i++;
}while(i < n &&(inputstr[i] != ',' || k != 0));
//该while循环的作用就是找出该表第一个表头逗号出的位置,如果没有逗号如: ((a,b))则会使i = n
//注意该表已经脱去最外层的括号了
if(i < n){
SubString(headstr,inputstr,0,i-1);
SubString(inputstr,inputstr,i+1,strlen(inputstr)-1);
}else{
strcpy(headstr,inputstr);
inputstr = '\0'; //here equal sign the inputstr NULL
}
}
Status CreateGList(GList &glist, char *str){
if(strcmp(str, "()") == 0)
glist = NULL; //建立空表
else{
glist = (GList)malloc(sizeof(struct GLNode)); //新建一个表结点
if(glist == NULL)
return OVER_FLOW;
if(strlen(str) == 1){ //如果为单个字符,则创建原子结点
glist->tag = ATOM;;
glist->atom = *str;
}
else{
char *headstr = hstr;
glist->tag = LIST;
SubString(str,str, 1, strlen(str) - 2); //脱去最外边的括号
GList pointer = glist;
do{ //创建子表
SplitHeadStr(str,headstr); //这里headstr为分割后的表头,str为分割后的表尾
char tstr[MAX_STR_LEN] = {0};
strcpy(tstr,headstr);
CreateGList(pointer->prt.hp,tstr); //递归创建表头
if(str != NULL && strlen(str) != 0){ //如果表尾不为空的话
GList tailnode = (GList)malloc(sizeof(struct GLNode));
if(pointer == NULL)
return OVER_FLOW;
tailnode->tag = LIST; //广义表的表尾肯定是一张表
pointer->prt.tp = tailnode;
pointer = tailnode; //set the tail of the current list
} //end if
}while(str != NULL && strlen(str) != 0); //直到表尾为空NULL则退出
pointer->prt.tp = NULL; //最后将表尾赋值为NULL
} //end else
}
return OK;
}
void OutputAtomNode(const GList mlist){
GList pointer = mlist; //利用递归思想输出广义表中的所有原子结点的值
do{
GList temp = pointer->prt.hp;
if(temp != NULL){ //递归输出表头中原子结点
if(temp->tag == ATOM)
cout<<temp->atom<<endl;
else
OutputAtomNode(temp);
}
pointer = pointer->prt.tp; // 指针指向表尾
}while(pointer != NULL);
}
int main(){
strcpy(istr, "(a,(b,c),(e,f,(g)))");
GList ml;
CreateGList(ml,istr);
OutputAtomNode(ml);
return 0;
}
教课书上创建广义链表的原始代码,在指针域上做了轻微的调整
Status CreateGList(GList &glist, char *str){
if(strcmp(str, "()") == 0)
glist = NULL; //建立空表
else{
glist = (GList)malloc(sizeof(struct GLNode)); //新建一个表结点
if(glist == NULL)
return OVER_FLOW;
if(strlen(str) == 1){ //如果为单个字符,则创建原子结点
glist->tag = ATOM;;
glist->atom = *str;
}
else{
char *headstr = hstr;
glist->tag = LIST;
SubString(str,str, 1, strlen(str) - 2); //脱去最外边的括号
GList pointer = glist;
GList tailnode = NULL;
do{ //创建子表
SplitHeadStr(str,headstr); //这里headstr为分割后的表头,str为分割后的表尾
char tstr[MAX_STR_LEN] = {0};
strcpy(tstr,headstr);
CreateGList(pointer->prt.hp,tstr); //递归创建表头
tailnode = pointer;
if(str != NULL && strlen(str) != 0){ //如果表尾不为空的话
pointer = (GList)malloc(sizeof(struct GLNode));
if(pointer == NULL)
return OVER_FLOW;
pointer->tag = LIST; //广义表的表尾肯定是一张表
tailnode->prt.tp = pointer; //set the tail of the current list
} //end if
}while(str != NULL && strlen(str) != 0); //直到表尾为空NULL则退出
pointer->prt.tp = NULL; //最后将表尾赋值为NULL
} //end else
}
return OK;
}