目录
问题描述: 建立广义表算法库,包括:
① 头文glist.h,定义数据类型,声明函数;
② 源文件glist.c
③ 设计main函数,测试上面实现的算法
先看头文件(glist.h)定义:
/*
**作者:李宗霖 日期:2023/5/5
**广义表的头尾链存储算法库
*/
//头文件:glist.h
#include <stdio.h>
#include <stdlib.h>
typedef int Elemtype;
//广义表采用表头表尾链的方式存储
typedef struct Node{
int tag; //0表示原子,1表示广义表
union {
Elemtype data; //共用体,tag值0,存放原子数据
struct { //tag值1
struct Node * head; //指向表头的指针
struct Node * tail; //指向表尾的指针
} Link;
};
} GLNode, *Glist;
int GLLength(Glist g); //求广义表g的长度
int GLDepth(Glist g); //求广义表g的深度
Glist CreateGL(char *s, int *loca); //返回由括号表示法表示s的广义表链式存储结构
void DispGL(Glist g); //输出广义表g
void dispGL(Glist g); //辅助DispGL函数
广义常用的存储结构有两种:
1.(表)头(表)尾链存储:原子结点有两项元素tag,data
表结点三项元素tag,指向表头指针head,指向表尾指针tail
2.孩子兄弟链存储:原子结点三项元素tag,data,指向兄弟结点的指针link
表结点三项元素:tag,指向孩子结点的指针sublist,指向兄弟结点的指针link
//广义表采用孩子兄弟链的方式存储
typedef struct Node{
int tag; //0表示原子,1表示广义表
union {
Elemtype data; //共用体,tag值0,存放原子数据
struct Node * sublist;
};
struct Node * link;
} GLNode, *Glist;
孩子兄弟链的存储方式,数据结构书上的代码例题都有,求深度,长度,建表,输出的函数都有代码,也有很多博主写过孩子兄弟链的存储和算法库,所以我是写的头尾链的存储和算法库
源文件(glist.c):
/*
**作者:李宗霖 日期:2023/5/5
**广义表的头尾链存储算法库
*/
//源文件:glist.c ; 如果是c++项目:glist.cpp
#include "glist.h"
源文件有头文件申明的五个函数,我们先看广义表的创建函数:
//根据字符串创建广义表
Glist CreateGL(char *s, int *loca) {
Glist g;
char c = s[(*loca)++]; //c语言不支持引用符&,c++可以传*&s,效果相同
//c获取字符串字符,loca为指向字符串元素的指针
if(c != '\0') {
g = (Glist)malloc(sizeof(GLNode));
//识别到前括号,说明表头为广义表
if(c == '(') {
g->tag = 1;
//如果为空表,表头指针为空,否则递归求表头表
if(s[*loca] == ')') g->Link.head = NULL;
else g->Link.head = CreateGL(s, loca);
}
//获取到反括号和逗号交给后面处理
else if(c ==')' || c == ',') (*loca)--;
//获取到字符,说明是原子
else {
g->tag = 0;
g->data = c;
return g;
}
}
//字符串读取结束返回NULL
else return NULL;
c = s[*loca];
//反括号代表广义表的结束
if(c == ')') {
g->Link.tail = NULL;
(*loca)++;
}
//逗号说明表头访问结束,该访问表尾了
else if(c == ',') {
s[*loca] = '(';
g->Link.tail = CreateGL(s, loca);
}
return g;
}
过程都有详细注释,这里扫描字符串采用了引入*loca,如果是cpp,可以用&loca,或者*&s
算法的主要思路是负责单一结点的创建,通过if语句:先处理表的tag=1和表头指针link.head(交给递归),或者是原子tag=0;再通过扫描到')'进行空表处理,','对表尾指针link.tail进行处理;总之就是函数对单一结点负责,其他结点交给递归处理
再来看求深度和长度的函数:
//获取广义表长度
int GLLength(Glist g) {
int length = 0;
//头尾链存储结构第一层结点个数即为表长
while(g != NULL) {
length++;
g = g->Link.tail;
}
return length;
}
//获取广义表深度
int GLDepth(Glist g) {
//如果是原子结构深度为0
if(!g->tag) return 0;
int depth = 1;
int max = 0;
//空表深度返回1
if(g->Link.head == NULL) return 1;
//遍历表的表头表尾结点
while(g != NULL) {
//子表的表头如果是广义表递归求深度
if(g->Link.head->tag) {
depth = GLDepth(g->Link.head);
if(depth > max) max = depth;
}
//指针后移
g = g->Link.tail;
}
return max + 1;
}
获取长度非常简单,因为link.tail指向表尾,所以只需呀遍历表的link.tail数量,就是表长
获取深度,通过while循环遍历表尾指针,通过递归遍历表头tail指针并返回最大深度
再来看以括号表示法打印广义表:
//以括号格式输出广义表
void DispGL(Glist g) {
printf("(");
dispGL(g);
}
//表头表尾存储结构没有头节点,格式不同一递归的话缺少第一个前括号;
void dispGL(Glist g) {
if(g != NULL) {
//原子结点直接输出原子元素
if(!g->tag) {
printf("%c", g->data);
return;
}
else if(g->Link.head != NULL){
//表头为广义表
if(g->Link.head->tag) printf("(");
dispGL(g->Link.head);
//有表尾,则逗号分隔
if(g->Link.tail != NULL) {
printf(",");
dispGL(g->Link.tail);
}
//表尾空,此广义表结束
else printf(")");
}
//表头为空表
else printf(")");
}
}
这里需要注意:我们求广义表表尾的时候,是除了表头外所有其他元素构成的广义表,而表头是表的第一个元素,所以表头和表尾不具备结构上的同一性
代码中所说没有头节点的意思是:在访问头尾链的第一个表头时,不像访问后续表头,都有因为是表尾所构建的“头节点”,第一个表头因为不是别的表的表尾,所以在用递归遍历的时候,因为第一个表头确少一个“表尾头结点”(和后面的表头结点结构上不同一),而少一个前括号,所以我们像“打补丁”一样提前输出一个前括号就好了
创建完头文件源文件,我们用main()函数测试一下:
先看代码和为了方便测试写的两个方法:
/*
**作者:李宗霖 日期:2023/5/5
**广义表的头尾链存储算法库
*/
#include "glist.h"
//检测字符串s是不是广义表
int judge(char *s) {
int i = 0;
int num = 0;
char c;
while((c = s[i++]) != '\0') {
if(c == '(') num++;
else if(num == 0) return 0;
else if(c == ')') {
if(s[i] == ')' || s[i] == ',' || s[i] == '\0') num--;
else return 0;
}
else if(s[i] == ',') {
if(s[i+1] != ')' ) i++;
else return 0;
}
}
if(num == 0) return 1;
return 0;
}
//测试广义表的功能
void test(char *s) {
int loca = 0;
if(judge(s)) printf("测试的广义表:\t\t%s\n", s);
else {
printf("出错啦 %s 不是广义表哦~~\n\n", s);
return;
}
Glist g = CreateGL(s, &loca);
printf("创建广义表成功!!!\n");
printf("括号表示法输出广义表:\t");
DispGL(g);
printf("\n**\n");
printf("广义表的长度:%d\n", GLLength(g));
printf("广义表的深度:%d\n\n", GLDepth(g));
}
int main() {
if(1) {
char s[100] = "(a,(((c,())(()))))";
test(s);
}
if(1) {
char s[100] = "((),(((),((())))))";
test(s);
}
if(1) {
char s[100] = "(a,g,(b,(d,e,((f))),c))";
test(s);
}
if(1) {
char s[100] = "((((a))))";
test(s);
}
return 0;
}
简单写了一个函数检验字符串s是否为括号表示法广义表
再在测试方法中依次调用广义表的创建,输出,求长度,求深度,main()函数当中通过if让s[100]可以重复定义,运行结果如下: