NOJ-求广义表的深度(C语言描述)

NOJ-求广义表的深度

第一次写博客,希望能够记录下自己的心得体会,如有错误, 欢迎指正。
![在这里插入图片描述]

广义表的基本概念

  • 广义表是线性表的推广,是典型的递归定义。
  • 广义表由两部分组成——表头(Head),表尾(tail)。第一个元素称为表头,其余元素称为表尾。
  • 广义表的特点:
    1. 列表的元素可以是子表,而子表的元素还可以是子表。
    2. 列表可为其他列表共享。
    3. 列表可以是一个递归的表,即列表也可以是本身的一个子表。

广义表的存储结构

typedef enum{ATOM,LIST} ElemTag;//ATOM代表原子,LIST代表子表
typedef struct GLNode{
    ElemTag tag;//公共部分,用于区分原子结点和表结点
    union{
        char atom;//原子结点的值
        struct {
            struct GLNode *hp, *tp;
        } ptr;//ptr是表结点的指针域,*hp指向表头,*tp指向表尾
    };
} * GList,GLNode;

结点的结构:

  1. 标志域 tag用于区分原子结点和表结点。
  2. 指针域 union部分。 如果是表结点则是指向表头和表尾的指针,如果是原子结点则是值域,代表该原子结点的值。

广义表的建立

void SubString(char sub[], char S[], int start, int len);//脱外层括号
void sever(char str[], char hstr[]);//将str,之前的部分给hstr,在str中删掉hstr和,
void CreateGList(GList *L, char *S);//创建广义表

主要函数如上

脱外层括号

void SubString(char sub[], char S[], int start, int len)
{
    int i = 0;
    for (i=0;i<len;i++)
        sub[i] = S[i + start - 1];
    sub[i] = '\0';
}

这一部分用于将一个字符数组的一部分复制到另一个字符数组中,这样做是为了分离出表头和表尾。在下一个函数中有解释。

分离表头和表尾

void sever(char *str,char *hstr)
{
    int i,k;//k记录左括号数
    for (i=0,k=0;i<strlen(str);i++)
    {
        if(str[i]=='(')
            k++;
        if(str[i]==')')
            k--;
        if(k==0&&str[i]==',')
            break;
    }//此时的i为表头和表尾的分界
    if(i<strlen(str)){
        SubString(hstr, str, 1, i);//表头字符串
        SubString(str, str, i + 2, strlen(str) - i - 1);//表尾字符串
    }//表尾不空
    else{
        SubString(hstr, str, 1, strlen(str));
        str[0] = 0;
    }//表尾空
}

在这一部分中,传入一个去掉左右括号的字符串。
表头和表尾的分界点是:
从左到右遍历这个字符串如果遇到的左括号等于遇到的右括号且i所指的字符是‘,’那么就说明到了表头表尾的分界点

  • 如果遍历完了,说明表尾是空表,把这个字符数组直接赋给表头,所以表尾的字符数组直接给第一个元素赋予0。
  • 但是如果没有遍历完,说明表尾不空,那么把逗号之前赋给表头,逗号之后赋给表尾。

创建广义表

我觉得这部分是最难的,比求广义表的深度更难,因为它也是一个递归建立。

void CreateGList(GList *L, char *S)
{
    GList p=(GList)malloc(sizeof(GLNode));
    GList q=(GList)malloc(sizeof(GLNode));
    char hsub[100]={0};
    if(S==NULL)
            (*L) = NULL; //如果传入空字符,就创建空表
    else{
        (*L) = (GList)malloc(sizeof(GLNode));
        if(strlen(S)==1){
            (*L)->tag = ATOM;
            (*L)->atom = *S;
        }
        else{
            SubString(S, S, 2, strlen(S) - 2);//脱去外层括号
            p = (*L);
            p->tag = LIST;

            do{//重复建n个子表
                sever(S, hsub);//从sub中分离出表头串hsub
                CreateGList(&(p->ptr.hp), hsub);
                if((*S)!=0){
                    q = p;
                    p = (GList)malloc(sizeof(GLNode));
                    p->tag = LIST;
                    q->ptr.tp = p;
                }
            } while ((*S) != 0);
            p->ptr.tp = NULL;
        }
    }
}

分三种情况

  1. 传入一个空指针,则建立空表。
  2. 若传入一个字符,则建立一个只有原子的结点的广义表。
  3. 否则,则进行如下操作:
    脱去外层括号;
    从S中分离出表头hsub然后递归调用CreateGList函数;
    若表尾结点非空,则把表尾结点插入在表尾。

广义表深度的递归法

int GListDepth(GList L)
{
    if(!L)
        return 1;
    if(L->tag==ATOM)
        return 0;
    int max = 0,dep;
    GList p;
    for (p = L; p; p = p->ptr.tp)
    {
        dep = GListDepth(p->ptr.hp);
        if(dep>max)
            max = dep;
    }
    return (max + 1);
}

递归定义为:

  1. 当L为空表时,深度为1;
  2. 当L为原子时,深度为0;
  3. 归纳:Depth=1+MAX{Depth(α)}
    所以这部分递归是比较简单的,只用不断在指针非空的情况下遍历广义表,遇到更大的就更新MAX的值。
    完整代码为:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef enum{ATOM,LIST} ElemTag;//ATOM代表原子,LIST代表子表
typedef struct GLNode{
    ElemTag tag;//公共部分,用于区分原子结点和表结点
    union{
        char atom;//原子结点的值
        struct {
            struct GLNode *hp, *tp;
        } ptr;//ptr是表结点的指针域,*hp指向表头,*tp指向表尾
    };
} * GList,GLNode;
void SubString(char sub[], char S[], int start, int len);//脱外层括号
void sever(char str[], char hstr[]);//将str,之前的部分给hstr,在str中删掉hstr和,
void CreateGList(GList *L, char *S);//创建广义表
int GListDepth(GList L);//广义表深度递归求法

int main()
{
    char S[100] = {0};
    scanf("%s", S);
    GList L = (GList)malloc(sizeof(GLNode));
    CreateGList(&L, S);
    int n = GListDepth(L);
    printf("%d\n%d", n,n);
}
void SubString(char sub[], char S[], int start, int len)
{
    int i = 0;
    for (i=0;i<len;i++)
        sub[i] = S[i + start - 1];
    sub[i] = '\0';
}
void sever(char *str,char *hstr)
{
    int i,k;//k记录左括号数
    for (i=0,k=0;i<strlen(str);i++)
    {
        if(str[i]=='(')
            k++;
        if(str[i]==')')
            k--;
        if(k==0&&str[i]==',')
            break;
    }//此时的i为表头和表尾的分界
    if(i<strlen(str)){
        SubString(hstr, str, 1, i);//表头字符串
        SubString(str, str, i + 2, strlen(str) - i - 1);//表尾字符串
    }//表尾不空
    else{
        SubString(hstr, str, 1, strlen(str));
        str[0] = 0;
    }//表尾空
}
void CreateGList(GList *L, char *S)
{
    GList p=(GList)malloc(sizeof(GLNode));
    GList q=(GList)malloc(sizeof(GLNode));
    char hsub[100]={0};
    if(S==NULL)
            (*L) = NULL; //如果传入空字符,就创建空表
    else{
        (*L) = (GList)malloc(sizeof(GLNode));
        if(strlen(S)==1){
            (*L)->tag = ATOM;
            (*L)->atom = *S;
        }
        else{
            SubString(S, S, 2, strlen(S) - 2);//脱去外层括号
            p = (*L);
            p->tag = LIST;

            do{//重复建n个子表
                sever(S, hsub);//从sub中分离出表头串hsub
                CreateGList(&(p->ptr.hp), hsub);
                if((*S)!=0){
                    q = p;
                    p = (GList)malloc(sizeof(GLNode));
                    p->tag = LIST;
                    q->ptr.tp = p;
                }
            } while ((*S) != 0);
            p->ptr.tp = NULL;
        }
    }
}

int GListDepth(GList L)
{
    if(!L)
        return 1;
    if(L->tag==ATOM)
        return 0;
    int max = 0,dep;
    GList p;
    for (p = L; p; p = p->ptr.tp)
    {
        dep = GListDepth(p->ptr.hp);
        if(dep>max)
            max = dep;
    }
    return (max + 1);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Alfred young

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

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

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

打赏作者

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

抵扣说明:

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

余额充值