前面分享了串的模式匹配后,今天我们来看看是《数据机构与算法》这本书最容易忽略的一种数据结构——广义表。广义表在日常应用中也不算冷门,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 << ')';
}
}
好啦,关于广义表的基本知识就分享到这啦。
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.