广义表与串的基本操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39956356/article/details/80777474

一:广义表

由于广义表涉及串的基本操作,所以这里把两个一起分析。当然自己还是把主角(广义表)放在前面,串放在后面。

1:广义表定义

广义表(Lists,又称列表)是一种非线性的数据结构,是线性表的一种推广

2:简洁描述

GList(α1,α2,α3···,αn),是一种递归定义的线性结构,
这里写图片描述

3:基本性质

1:各元素存在相对位序
2:广义表可以共享
3:广义表的长度为n
4:广义表的深度:所包括括号的重数。“原子”深度为0,“空表”深度为1
5:递归表深度∞,长度为2
6:广义表分解最终结果必有两部分:“原子”与“空表”
7:广义表表头为第一个元素α1,表尾即α2,α3···,αn
这里写图片描述

4:应用

广泛的应用于人工智能等领域的表处理语言LISP语言中。在LISP语言中,广义表是一种最基本的数据结构,就连LISP 语言的程序也表示为一系列的广义表。

5:存储结构

这里采用广义表的头尾链表表示
这里写图片描述

相关代码:

/***************************************************
**                  存储结构
** 1:广义表的头尾链表表示-----以此为存储解构
** 2:广义表的扩展线性链表表示
***************************************************/
typedef char AtomType;

typedef enum{                                                       //标志位,原子(0)与表(1)
    ATOM, LIST
}ElemTag;


//广义表的头尾链存储
typedef struct GNode {
    ElemTag tag;
    union {
        AtomType data;                                              //数据(char)
        struct { struct GNode *hp, *tp; }ptr;                       //ptr表结点指针域,hp:表头,tp:表尾
    };
}*GList;
5:创建广义表

首先这个创建广义表是比较复杂的!!!有两个函数组成,这里自己仔细分析下。
int CreatGList(GList &L, HString &ch);
void sever(HString &str, HString &hstr);
1:分析第一个函数—-int CreatGList(GList &L, HString &ch);
(1):这个是多层递归,并且多层递归调用(头)p->ptr.hp

CreatGList(p->ptr.hp, hsub);                                                    //创建广义表某个元素的表(就好像葡萄树上的一串葡萄),”ptr.hp“

(2):建立单个原子‘()’–它分解的最小单位是原子与’()’,那么建立原子后,返回上层递归点,必建立’()’–即不会进while(这是由于截断里的ClearString(str)已经清空了sub)
这里写图片描述
(3): 建立侧枝,想想??我们已经建好了最底层的原子与’()’–相当于葡萄,正如一串葡萄,有好多分支的,这里我称为侧枝,必须要每个侧枝啊,那就是q啊,p新建一大堆东西连上q即可
这里写图片描述

2:分析第二个函数—-void sever(HString &str, HString &hstr);
1:i:指向现在获取的ch的位置,一但发现完整的‘,’如’((B),C),’就出while,截取两段
2:k:是左右括号匹配问题,左括号加1,右括号减1。 比如’((B),C)’k遇左括号自增两次,遇左括号自两次,结果为0,出while
这里写图片描述

相关代码:

/******************************************************************************
**                               创建广义表
**  p   :p是最新的,需要不断的malloc
    q   :q是之前的p,用于创建之前还未建的侧枝
    sub :‘,’后字符串
    hsub:‘,’前字符串
** 简单的分析下思路(这个不好理解,学完树,图还好点.....)
    1:首先明白,这个是多层递归,变量是放在函数体内 or 外?
    2:这里递归是递归的’头(p->ptr.hp)‘,用啥数据建呢?答:hsub:‘,’前字符串
    3:这里需要的东西
       建立单个原子及'()'--它分解的最小单位是原子与'()',那么建立原子后,返回上层递归点,必建立'()'--即不会进while(这是由于截断里的ClearString(str)已经清空了sub)
       建立侧枝,想想??我们已经建好了最底层的原子与'()'--相当于葡萄,正如一串葡萄,有好多分支的,这里我称为侧枝,必须要每个侧枝啊,那就是q啊,p新建一大堆东西连上q即可

*******************************************************************************/
int CreatGList(GList &L, HString &StringCh)
{
    GList p;                                                                                    //p是最新的,需要不断的malloc
    GList q;                                                                                    //q是之前的p,用于创建之前还未建的侧枝
    HString sub;                                                                                //‘,’后字符串
    HString hsub;                                                                               //‘,’前字符串

    const char *emp = "()";                                                                     //提前预设空串
    HString temp;
    StrAssign(temp, emp);

    if (!StrCompare(StringCh, temp))                                                            //若空串,则空表
        L = NULL;
    else{
        if (!(L = (GList)malloc(sizeof(GNode))))
            return OVERFLOW;
        if (strLength(StringCh) == 1) {
            L->tag = ATOM;
            L->data = StringCh.firstAddress[0];                                                 //如果是原子,取StringCh[0]即可
        }else {
            L->tag = LIST;      p = L;                                                          //不是原子,当然是表
            SubString(sub, StringCh, 2, strLength(StringCh) - 2);                               //退去外层括弧
            do {
                sever(sub, hsub);                                                               //截取‘,’前面的,也就是α1,α2···
                CreatGList(p->ptr.hp, hsub);                                                    //创建广义表某个元素的表(就好像葡萄树上的一串葡萄),”ptr.hp“
                q = p;                                                                          //p是下一个,q是上一个
                if (!StrEmpty(sub)) {
                    if (!(p = (GList)malloc(sizeof(GNode))))
                        return OVERFLOW;
                    p->tag = LIST;      q->ptr.tp = p;                                          //相当于一条侧枝,侧枝从哪儿开始呢?从q开始啊,递归调用批永远是最新的,q就是过去的p
                }
            } while (!StrEmpty(sub));
            q->ptr.tp = NULL;                                                                   //不存在侧枝,侧枝为空
        }
    }
}


/***************************************************************************************
**      功能:截取‘,’前面的,也就是α1,α2···
**i:指向现在获取的ch的位置,一但发现完整的‘,’如'((B),C),'就出while,截取两段
**k:是左右括号匹配问题,左括号加1,右括号减1。
     比如'((B),C)'k遇左括号自增两次,遇左括号自两次,结果为0,出while
****************************************************************************************/
void sever(HString &str, HString &hstr)
{
    int n = strLength(str);                                                         //获取str的长度
    int i = 0, k = 0;                                                               //k是匹配左右括号数,左括号加1,右括号减1。i可以说是记录这次应该判断哪个字符了(位置)
    HString ch;
    do {
        ++i;                                                                        //ch的位置
        SubString(ch, str, i, 1);                                                   //取一个字符分析
        if (ch.firstAddress[0] == '(')                                              //左括号加1
            ++k;
        else if (ch.firstAddress[0] == ')')                                         //右括号减1
            --k;
    } while (i < n && (ch.firstAddress[0] != ',' || k != 0));                       //遇到','或者k为0。退出循环(当然最好的情况只有一个α1,一把到i=n)
    if (i < n)                                                                      //i < n:需要截断
    {
        SubString(hstr, str, 1, i - 1);                                             //hstr是α1,α2···中的α1
        SubString(str, str, i + 1, n - i);                                          //把‘,’后字符串给str
    }else{                                                                          //传进来只可能是单字母
        StrCopy(hstr, str);                                                         //单原子(如A)
        ClearString(str);                                                           //清空str,也就是sub,这样递归回去,就不会进入创建子表if里了
    }
}
6:求广义表深度

1:广义表的深度:所包括括号的重数。“原子”深度为0,“空表”深度为1
2:递归表示:基本项 + 归纳项
这里写图片描述
3:整体思路
这里写图片描述

相关代码:

//求广义表深度
int GListDepth(GList &L)
{
    if (!L)                                                                         //空表深度为1
        return 1;
    if (L->tag == ATOM)                                                             //原子深度为0
        return 0;

    GNode *pp;
    int depth;
    int max = 0;
    for (max = 0, pp = L; pp; pp = pp->ptr.tp)                                      //横着走
    {
        depth = GListDepth(pp->ptr.hp);                                             //(竖着走)其中一项的深度
        if (depth > max)    
            max = depth;                                                            //不断更新更深的
    }
    return max + 1;                                                                 //最深的加1
}
7:复制广义表

1:递归调用,只需要考虑一次即可,不要一层一层想,很容易晕,如果你要去想,会很清晰明白问题。
2:递归调用,先递归建立头,在递归建立尾
这里写图片描述
3:整体思路
这里写图片描述
相关代码:

//复制广义表
int CopyGList(GList &T, GList L)
{
    if (!L)                                                                         //L为空,T复制为空
        T = NULL;
    else
    {
        if (!(T = (GList)malloc(sizeof(GNode))))
            return OVERFLOW;
        T->tag = L->tag;                                                            //复制标志位
        if (T->tag == ATOM)
            T->data = L->data;                                                      //复制data
        else
        {
            CopyGList(T->ptr.hp, L->ptr.hp);                                        //复制L的头                 
            CopyGList(T->ptr.tp, L->ptr.tp);                                        //复制L的尾     
        } 
    }
}
8:主函数与输出

1:主函数

#include "stdafx.h"
#include "GList.h"
#include "myString.h"                                                               //自己写的常见字符串操作

int main()
{
    GList L;                                                                        //原表
    GList LCpoy;                                                                    //复制表LCpoy
    HString StringCh;                                                               
    const char *string = "((A),((B),C),D)";                                         //输入的字符串(英文条件下输入的),这里要转化成HString,因为之后设计字符串操作
    StrAssign(StringCh, string);                                                    //char * 转化成 HString
    CreatGList(L, StringCh);                                                        //创建广义表

    printf("广义表L深度:%d\n", GListDepth(L));                                       //求广义表的深度
    printf("输出广义表L:\n");
    PrintGList(L);                                                                  //输出广义表

    CopyGList(LCpoy, L);
    printf("\n\n广义表LCpoy深度:%d\n", GListDepth(LCpoy));                           //求广义表的深度
    printf("输出广义表LCpoy:\n");
    PrintGList(LCpoy);                                                              //输出广义表

    return 0;
}

2:输出
这里写图片描述

二:串的基本操作

1:基本概括

作为计算机处理的最基本数据对象。比整数,浮点数复杂的多。有效的处理串,要根据实际情况采用不同的存储结构。

2:串的定义

串(string)(或字符串)是由一个或多个字符组成的有序数列,记为:
s = ‘a1a2······an’
s是串名,n表示长度.

3:注意一下

零个字符称为空串,长度为0。
空格串:由一个或多个空格组成的串,长度是空格的数目。
串相等:长度与对应位置的元素一致。
重点强调下:串是一个整体作为操作对象,而线性表是单个元素而已。
串第一个元素标号是1。

4:储存结构

堆分配(动态分配),因为这样实用性会更好些
两个成员:串的首地址,串长度。

#define ERROR        0
#define OK           1
#define TRUE         2
#define OVERFLOW    -2

typedef int Status;

/******************************************************
**      串的存储机构
**1:循序存储
        定长顺序表示--------涉及“截断”,很大的弊端
        堆分配(动态分配)--本次重点
 **2:链式存储
******************************************************/
typedef struct {
    char *firstAddress;
    int length;
}HString;
5:重要函数

(1):连接字符串
自己灵活处理串的两个成员即可。
这里写图片描述
(2):求字串
这里写图片描述
(3):比较串
这里写图片描述
(4):创建串
注意一下:const char *chars
不用const编译器不给过
这里写图片描述
相关代码:

#include "stdafx.h"
#include "myString.h"

//将常量赋值给变量串T
Status StrAssign(HString &T, const char *chars)
{
    //if (T.firstAddress)
    //  free(T.firstAddress);
    int len;
    const char *c;
    for (len = 0, c = chars; *c; len++, ++c);                                       //计算chars的长度
    if (!len)                                                                       //长度为0,不需要创建串
        T.firstAddress = NULL;
    else
    {
        if (!(T.firstAddress = (char *)malloc(len * sizeof(char))))
            return OVERFLOW;
        for (int i = 0; i < len; i++)
        {
            T.firstAddress[i] = chars[i];                                           //一个一个赋值
        }
        T.length = len;                                                             //记录长度
    }
    return OK;
}


//求串的长度
int strLength(HString S)
{
    return S.length;                                                                //返回串长度
}


/**************************************************
**              比较大小(按字典顺序)
**  S > T 返回 >0
    S = T 返回 =0
    S < T 返回 <0
**************************************************/
int StrCompare(HString S, HString T) 
{
    for (int i = 0; i < S.length && i < T.length; i++)
        if (S.firstAddress[i] != T.firstAddress[i])                                 //比字母大小
            return S.firstAddress[i] - T.firstAddress[i];
    return S.length - T.length;                                                     //字母大小相同,比长度
}


//连接字符串(新串T = S1 + S2)
Status Concat(HString &T, HString &S1, HString &S2)
{
    //if (T.firstAddress)
    //  free(T.firstAddress);
    if (!(T.firstAddress = (char *)malloc((S1.length + S2.length) * sizeof(char))))
        return OVERFLOW;
    for (int i = 0; i < S1.length; i++)                                             //先复制S1
        T.firstAddress[i] = S1.firstAddress[i];
    for (int i = 0; i < S2.length; i++)
        T.firstAddress[S1.length + i] = S2.firstAddress[i];                         //之后复制S2,记得起始地址增加了!
    T.length = S1.length + S2.length;                                               //记录新的串长度
    return OK;
}


//求字串
Status SubString(HString &Sub, HString S, int pos, int len)
{
    if (pos < 1 || len > S.length || len < 0 || len > S.length - pos + 1)           //剔除不正确的输入值
        return ERROR;
    //if (Sub.firstAddress)
    //  free(Sub.firstAddress);
    if (!len)                                                                       //长度为0,返回NULL
    { 
        Sub.firstAddress = NULL;
        Sub.length = 0;
    }else
    {
        if (!(Sub.firstAddress = (char *)malloc(len * sizeof(char))))
            return OVERFLOW;
        for (int i = 0; i < len; i++)                                               //提取子串,从pos起到pos - 1 + i(记得减1)
            Sub.firstAddress[i] = S.firstAddress[pos - 1 + i];
;       Sub.length = len;                                                           //记录子串长度
    }
    return OK;
}


//判串空
Status StrEmpty(HString S)
{
    if (S.firstAddress)
        return 0;
    return 1;
}


//串的复制
void StrCopy(HString &S, HString &T)
{
    S.firstAddress = T.firstAddress;                                                //复制首地址
    S.length = T.length;                                                            //复制长度
}

//清空串,并释放内存(看实际情况是否释放空间)
Status ClearString(HString &S)                                  
{
    if (S.firstAddress)
    {
        //free(S.firstAddress);                                                     //并释放内存
        S.firstAddress = NULL;                                                      //清空串
    }
    S.length = 0;                                                                   //长度请0
    return OK;
}


//输出串
void PrintString(HString &S)
{
    for (int i = 0; i < S.length; i++)
        printf("%c",S.firstAddress[i]); 
    printf("\n");
}
8:主函数与输出

1:主函数

#include "stdafx.h"
#include "GList.h"
#include "myString.h"                                                               //自己写的常见字符串操作

int main()
{
    /***************************************************
    **          串(6个基本函数)测试
    ****************************************************/
    const char *ceshi = "ZhongGuoMeng";
    const char *ceshi1 = "WoDeMeng";
    HString S;
    HString S1;
    HString newStringConcat;
    HString Sub;

    StrAssign(S, ceshi);
    printf("串1:\n");
    PrintString(S);

    //求字串
    printf("串1长度:%d\n", strLength(S));
    SubString(Sub, S, 6, 3);
    printf("字串[SubString(Sub, S, 6, 3)]:\n");
    PrintString(Sub);

    StrAssign(S1, ceshi1);
    printf("串2:\n");
    PrintString(S1);
    Concat(newStringConcat, S, S1);
    printf("连接串:\n");
    PrintString(newStringConcat);

    printf("比较串:\n");
    printf("%d\n", StrCompare(S, S1)); 


    ClearString(S);
    printf("是否为空:%d\n", StrEmpty(S));
    printf("复制串(S1-->S):\n");
    StrCopy(S, S1);
    PrintString(S);
    return 0;
}

2:输出
这里写图片描述

三:源代码

链接: https://pan.baidu.com/s/1WBKd8K5YqT4Ga252SCwtHg 密码: 31k3

猜你喜欢

转载自blog.csdn.net/weixin_39956356/article/details/80777474
今日推荐