0
点赞
收藏
分享

微信扫一扫

数据结构之广义表

向上的萝卜白菜 2022-04-20 阅读 57
数据结构

前面分享了串的模式匹配后,今天我们来看看是《数据机构与算法》这本书最容易忽略的一种数据结构——广义表。广义表在日常应用中也不算冷门,leetcode等刷题网站也可以看到它的身影。下面我们了解一下它吧。

1.概念:广义表:(又称列表Lists)是n≥0个元素a₀ ,a₁,…,an-1的有限序列,其中每一个ai或者是原子,或者是一个广义表

广义表通常记作: LS = (a1,a2,…,an)
其中,LS为表名,n为表的长度,每一个ai为表的元素。习惯上,一般用大写字母表示广义表,小写字母表示原子。
表头:若LS非空(n≥1),则其第一个元素a1就是表头。记作head(LS)= a1。注:表头可以是原子,也可以是子表。
表尾:除表头之外的其他元素组成的表。记作 tail(LS) = (a1,···,an)。
注:表尾不是最后一个元素,而是一个子表

2.性质:

1.广义表中的数据元素有相对次序;一个直接前驱和一个直接后驱;
2.广义表的长度定义为最外层所包含元素的个数;
3.广义表的深度定义为该广义表展开后所含括号的重数;
A = (b,c)的深度为1,
B = (A,d)的深度为2,B = ((b,c),d)
C = (f,B,h)的深度为3。C = (f,((b,c),d),h)原子的深度为0;空表的深度为1。
4.广义表可以为其他广义表共享;
5.广义表可以是一个递归的表。
递归表的深度是无穷值,长度是有限值。
6.广义表是多层次结构,广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表,···。

3.广义表的基本实现操作(涉及到一些串的基本操作):

typedef enum { ATOM, LIST } ElemTag;//ATOM=0,表示原子,LIST=1,表示子表
typedef struct Node//注意这里面是有很多嵌套的
{
    ElemTag tag;                //标志位tag用于区分元素是原子还是子表
    union
    {
        AtomType atom;      //AtomType是原子结点的值域,用户自己定义类型
        struct
        {
            struct Node* hp, * tp;        //hp指向表头,tp指向表尾
        }ptr;
    };
}*GList, GLNode;
//广义表函数实现部分
void Error(char* s)                       //错误处理函数
{
    cout << s << endl;
    exit(1);
}

void  GetHead(GList L)
//求广义表的表头结点操作
{
    if (L == NULL)
    {
        cout << "空表不能取表头!" << endl;
    }
    GLNode* Head = L->ptr.hp;
    if (Head->tag == ATOM)
        cout << "表头:" << Head->atom << endl;
    else
    {
        cout << "表头:";
        PrintGList(Head);
        cout << endl;
    }
}
void  GetTail(GList L)
//求广义表的表尾操作
{
    if (L == NULL)
    {
        cout << "空表不能取表尾!" << endl;
    }
    GLNode* tail = L->ptr.tp;
    cout << "表尾:";
    PrintGList(tail);
    cout << endl;
}
int GListLength(GList L)
//求广义表的长度操作
{
    int length = 0;
    GLNode* p = L;
    if (p == NULL)  return 0;
    else
    {
        length = GListLength(p->ptr.tp);
    }
    return length + 1;
}
int GListDepth(GList L)
//求广义表的深度操作
{
    int max, depth;
    GLNode* p;
    if (!L)                          //如果广义表非空,则返回1
        return 1;
    if (L->tag == ATOM)                 //如果广义表是原子,则返回0
        return 0;
    for (max = 0, p = L; p; p = p->ptr.tp)         //逐层处理广义表
    {
        depth = GListDepth(p->ptr.hp);
        if (max < depth)
            max = depth;
    }
    return (max + 1);
}
int CountAtom(GList L)//求广义表中原子结点的数目,并返回
{
    int n1, n2;
    if (L == NULL) return 0;
    if (L->tag == ATOM) return 1;
    n1 = CountAtom(L->ptr.hp);           //求表头中的原子数目
    n2 = CountAtom(L->ptr.tp);           //求表尾中的原子数目
    return (n1 + n2);
}
void CopyList(GList* T, GList L)
//广义表的复制操作。由广义表L复制得到广义表T
{
    if (!L)                          //如果广义表为空,则T为空表
        *T = NULL;
    else
    {
        *T = (GList)malloc(sizeof(GLNode));   //表L不空,为T建立一个表结点
        if (*T == NULL)
            Error(L"内存申请失败!");
        (*T)->tag = L->tag;
        if (L->tag == ATOM)            //复制原子
            (*T)->atom = L->atom;
        else                        //递归复制子表
        {
            CopyList(&((*T)->ptr.hp), L->ptr.hp);
            CopyList(&((*T)->ptr.tp), L->ptr.tp);
        }
    }
}
/ 截取子串操作
int SubString(SString * Sub, SString S, int pos, int len)
{
    int i;
    if (pos < 0 || len<0 || pos + len - 1>S.length)
    {
        cout<<"参数pos和len不合法"<<endl;
        return 0;
    }
    else
    {
        for (i = 0; i < len; i++)
            Sub->str[i] = S.str[i + pos - 1];
        Sub->length = len;
        return 1;
    }
}
void StrAssign(SString* S, char str[])
{
    int i;
    for (i = 0; str[i] != '\0'; i++)
        S->str[i] = str[i];   //将常量str中的字符赋值给串S  
    S->length = i;
}
void StrClear(SString* S) {
    S->length = 0;
}
void DistributeString(SString* Str, SString* HeadStr)
//将串Str分离成两个部分,HeadStr为第一个逗号之前的子串,Str为逗号后的子串
{
    int len, i, k;
    SString Ch, Ch1, Ch2, Ch3;
    len = StrLength(*Str);                //len为Str的长度
    StrAssign(&Ch1, ",");                //将字符','、'('和')'分别赋给Ch1,Ch2和Ch3
    StrAssign(&Ch2, "(");
    StrAssign(&Ch3, ")");
    SubString(&Ch, *Str, 1, 1);            //Ch保存Str的第一个字符
    for (i = 1, k = 0; i <= len && StrCompare(Ch, Ch1) || k != 0; i++) //搜索Str最外层的第一个括号
    {
        SubString(&Ch, *Str, i, 1);        //取出Str的第一个字符
        if (!StrCompare(Ch, Ch2))         //如果第一个字符是'(',则令k加1
            k++;
        else if (!StrCompare(Ch, Ch3))    //如果当前字符是')',则令k减去1
            k--;
    }
    if (i <= len)                           //串Str中存在',',它是第i-1个字符
    {
        SubString(HeadStr, *Str, 1, i - 2);  //HeadStr保存串Str','前的字符
        SubString(Str, *Str, i, len - i + 1);  //Str保存串Str','后的字符
    }
    else                                //串Str中不存在','
    {
        StrCopy(HeadStr, *Str);          //将串Str的内容复制到串HeadStr
        StrClear(Str);                  //清空串Str
    }
}
void CreateList(GList* L, SString S)
//采用头尾链表创建广义表
{
    SString Sub, HeadSub, Empty;
    GList p, q;
    StrAssign(&Empty, "()");
    if (!StrCompare(S, Empty))            //如果输入的串是空串则创建一个空的广义表
        *L = NULL;
    else
    {
        if (!(*L = (GList)malloc(sizeof(GLNode))))     //为广义表生成一个结点
            cout<<"内存分配失败!"<<endl;
        if (StrLength(S) == 1)                         //广义表是原子,则将原子的值赋值给广义表结点
        {
            (*L)->tag = ATOM;
            (*L)->atom = S.str[0];
        }
        else                                        //如果是子表
        {
            (*L)->tag = LIST;
            p = *L;
            SubString(&Sub, S, 2, StrLength(S) - 2);     //将S去除最外层的括号,然后赋值给Sub
            do
            {
                DistributeString(&Sub, &HeadSub);    //将Sub分离出表头和表尾分别赋值给HeadSub和Sub
                CreateList(&(p->ptr.hp), HeadSub);    //递归调用生成广义表
                q = p;
                if (!StrEmpty(Sub))                  //如果表尾不空,则生成结点p,并将尾指针域指向p
                {
                    if (!(p = (GLNode*)malloc(sizeof(GLNode))))
                        Error("内存分配失败!");
                    p->tag = LIST;
                    q->ptr.tp = p;
                }
            } while (!StrEmpty(Sub));
            q->ptr.tp = NULL;
        }
    }
}
void PrintGList(GList L)
//输出广义表的元素
{
    if (!L)  cout << "()";
    else
    {
        if (L->tag == ATOM)
            cout << L->atom;
        else
        {
            cout << '(';
            GLNode* p = L;
            while (p)
            {
                PrintGList(p->ptr.hp);
                p = p->ptr.tp;
                if (p)  cout << ',';
            }
            cout << ')';
        }
    }

好啦,关于广义表的基本知识就分享到这啦。

本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.

举报

相关推荐

0 条评论