数据结构 实验13(1-4班)_广义表算法库(两种存储结构:孩子兄弟链,头尾链)

目录

问题描述: 建立广义表算法库,包括:  

先看头文件(glist.h)定义:

广义常用的存储结构有两种:

源文件(glist.c):

创建完头文件源文件,我们用main()函数测试一下:


问题描述: 建立广义表算法库,包括:  


            ① 头文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]可以重复定义,运行结果如下:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leisure_水中鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值