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