文章目录
1、广义表的定义
2、广义表的存储结构
3、代码结构描述
5、广义表的各类操作
6、代码测试
7、完整代码
作者建议:为了方便读者能够更加理解代码实现,建议各位读者在看代码的时候可以参考广义表的内存模型。
广义表是线性表的推广,也被称为列表(lists)。
广义表一般记作:
LS=(a1,a2,a3,…,a n)
LS是表的名称,n是表的长度。在线性表的定义中,a i (1<=i<=n)只能限于单个元素,但是在广义表中a i 可以是 单个元素,也可以是广义表,分别称为广义表LS的原子和子表。 当广义表非空时,第一个元素(a1)被称为表头,其余元素(a2,a3,…,a n)被称为子表。
由于广义表的数据元素可以是原子和子表,因此很难用顺序存储结构来表示,通常采用链式存储结构,每个数据元素可以用一个结点来表示。
由此我们需要2种数据结构来表示结点:一个是表结点,用来表示列表,另一个是原子结点,用来表示原子。
//节点类型枚举
typedef enum
{
ATOM, //原子类型
LIST //列表类型
} ElemTag;
//表数据结构
struct LS_Data
{
int data; //存储数据
char number; //存储编号
};
typedef struct LS_Data LS_Data;
//表结点结构
struct LS_Node
{
ElemTag Tag; //结点类型标识
union
{
LS_Data value;
struct
{
struct LS_Node *hp; //表头指针
struct LS_Node *tp; //表尾指针
} ptr;
};
};
typedef struct LS_Node LS_Node;
//广义表
struct Mul_Lists
{
LS_Node *headPtr;//头结点
unsigned int len;//表的长度
LS_Node *lastNode;//末尾节点
};
typedef struct Mul_Lists Mul_Lists;
下面我们以广义表LS=(a,b,(c,d),e,f)为例。
下面是该广义表的内存模型:
空的方框表示列表结点
//返回一个空的广义表
Mul_Lists GetMulLists(void);
//构造广义表
void InitMulLists(Mul_Lists *LS, char *Num, LS_Data Data[], int Len);
//广义表是否为空
bool Empty(Mul_Lists *LS);
//销毁广义表
void Destroy(Mul_Lists *LS);
//获取广义表的长度
unsigned int Length(Mul_Lists *LS);
//获取广义表的深度
unsigned int Depth(Mul_Lists *LS);
//获取表头数据
LS_Data *GetHead(Mul_Lists *LS);
//获取表尾数据
LS_Data *GetTail(Mul_Lists *LS);
//插入元素(第一个元素)
void InsertFirst(Mul_Lists *LS, const LS_Data Data);
//删除元素(第一个元素)
LS_Data DeleteFirst(Mul_Lists *LS);
//遍历广义表
void Traverse(Mul_Lists* LS);
首先我们先定义一个用于构造结点的函数。
//返回结点
static LS_Node *GetListsNode(ElemTag Tag)
{
LS_Node *newNode = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(newNode))
return NULL;
//构造原子结点
if (Tag == ATOM)
{
newNode->value.data = 0;
newNode->value.number = ' ';
newNode->Tag = ATOM;
}
//构造列表结点
else if (Tag == LIST)
{
newNode->Tag = LIST;
newNode->ptr.hp = newNode->ptr.tp = NULL;
}
return newNode;
}
在构造表的时候,我们采用递归建表。
//构造广义表
void InitMulLists(Mul_Lists *LS, char *Num, LS_Data Data[], int Len)
{
int len = 0;//记录表长度
LS_Node *curPtr = NULL;
//如果表的编号为空就直接返回
if (!STRING_LEN(Num))
return;
//给头结点赋值,只做一次(即进入递归之后不能修改头结点)
if (!LS->len)
{
LS->lastNode = LS->headPtr = GetListsNode(LIST);
curPtr = LS->lastNode;
}
//进入递归后会执行else中的代码
else
{
curPtr = GetListsNode(LIST);
LS->lastNode->ptr.hp = curPtr;
LS->lastNode = curPtr;
}
LS->len++;
//表长加一
++len;
//删除首字符
DeleteString(Num);
while (STRING_LEN(Num))
{
//修改LS中的表长
LS->len = len;
if (Num[0] == '(')
{
//记录尾结点
LS->lastNode = curPtr;
//进入递归
InitMulLists(LS, Num, Data, Len);
}
else if (Num[0] == ',')
{
curPtr = LS->lastNode;
curPtr->ptr.tp = GetListsNode(LIST);
curPtr = curPtr->ptr.tp;
LS->lastNode = curPtr;
//表长加一
++len;
}
else if (Num[0] == ')')
{
break;
}
else
{
//链接新节点
curPtr->ptr.hp = GetListsNode(ATOM);
//给节点数据赋值
curPtr->ptr.hp->value = Data[0];
//移除第一个数据
DeleteArray(Data, &Len);
}
//删除首字符
DeleteString(Num);
}
}
在遍历时,我们需要一个辅助函数来输出结点信息。
//输出结点信息
static void ShowNodeInfo(LS_Node *Node)
{
while (Node)
{
if (Node->ptr.hp->Tag == ATOM)
{
printf("结点编号:%c 结点数据:%d\n", Node->ptr.hp->value.number, Node->ptr.hp->value.data);
}
else if (Node->ptr.hp->Tag == LIST)
{
ShowNodeInfo(Node->ptr.hp);
}
Node = Node->ptr.tp;
}
}
遍历时也需要使用递归的方式来访问到所有的节点。
//遍历广义表
void Traverse(Mul_Lists *LS)
{
LS_Node *head = LS->headPtr;
if (PTR_IS_NULL(head))
return;
ShowNodeInfo(head);
}
在销毁广义表时,我们定义一个辅助函数来帮助我们销毁。
//销毁结点
static void FreeMulListsNode(LS_Node *Node)
{
LS_Node *temp = NULL;
while (Node)
{
if (Node->ptr.hp->Tag == ATOM)
{
//释放原子结点
free(Node->ptr.hp);
}
else if (Node->ptr.hp->Tag == LIST)
{
//递归删除列表结点
FreeMulListsNode(Node->ptr.hp);
}
//保存下一个结点的位置
temp = Node->ptr.tp;
//释放列表结点
free(Node);
Node = temp;
}
}
和构建,遍历广义表一样,在销毁时,我们也需要递归的删除。
//销毁广义表
void Destroy(Mul_Lists *LS)
{
LS_Node *head = LS->headPtr;
if (PTR_IS_NULL(head))
return;
FreeMulListsNode(head);
}
该操作非常简单,直接判断头结点指针是否为空即可。
//广义表是否为空
bool Empty(Mul_Lists *LS)
{
return PTR_IS_NULL(LS->headPtr);
}
由于在我们的表结构中存储了头结点指针,因此我们只需访问头指针数据即可。
//获取表头数据
LS_Data *GetHead(Mul_Lists *LS)
{
return &LS->headPtr->ptr.hp->value;
}
由于在我们的表结构中存储了尾结点指针,因此我们只需访问尾指针数据即可。
//获取表尾数据
LS_Data *GetTail(Mul_Lists *LS)
{
return &LS->lastNode->ptr.hp->value;
}
广义表的长度实际就是广义表中数据元素的数量,子表算一个元素,原子算一个元素。
例如表LS=(a,(b,c),d,(e,(f)))
该表的长度就为4。其中a、(b,c)、d、(e,(f))都是一个元素。
由于在我们构建广义表的时候就已经记录过表的长度了,所以不需要再去重新遍历表,重新计算长度。
//获取广义表的长度
unsigned int Length(Mul_Lists *LS)
{
return LS->len;
}
广义表的深度定义为广义表中括弧(括号)的重数,是广义表的一种度量。如果广义表的结点为原子结点其深度就为0,如果是空表,那么其深度就为1。因此广义表的深度计算其实就是计算元素中子表的嵌套层数,取最大值,然后将其层数+1,就是广义表的深度。
例如:广义表LS=((),a,(b,c),d,(e,(f)))
将该表拆分:
结点()为空表,深度为1
子表LS1=a,(b,c),d,(e,f)
结点a为原子,深度为0
子表LS2=(b,c),d,(e,(f))
结点(b,c)为列表,深度为1
子表LS3=d,(e,(f))
结点d为原子,深度为0
结点(e,(f))为列表,该表中又嵌套了列表(f),因此他的深度为2
由此可得列表(e,(f))的深度最大,所以该表的深度就为2+1=3。
所以广义表的深度计算递归算法就有2个终结状态,空表和原子。在此我们需要一个辅助函数来帮助我们实现递归算法。
//计算广义表深度
static int ListsDepth(LS_Node *Node)
{
//如果表为空表时,直接返回长度1;
if (PTR_IS_NULL(Node))
{
return 1;
}
//如果表为原子时,直接返回0;
if (Node->Tag == ATOM)
{
return 0;
}
int max = 0; //设置表的初始长度为0;
LS_Node *temp = Node;
while (temp)
{
int dep = ListsDepth(temp->ptr.hp);
if (dep > max)
{
max = dep; //每次找到表中遍历到深度最大的表,并用max记录
}
temp = temp->ptr.tp;
}
//程序运行至此处,表明广义表不是空表,由于原子返回的是0,而实际长度是1,所以,此处要+1;
return max + 1;
}
//获取广义表的深度
unsigned int Depth(Mul_Lists *LS)
{
//判断广义表是否为空
if (PTR_IS_NULL(LS->headPtr))
return 0;
return ListsDepth(LS->headPtr);
}
在这里小编只为大家展示如何在广义表的表头插入数据。首先我们要先分配一个列表结点和一个原子结点。
然后将新节点的tp指针指向头结点,将头指针headPtr指向新的节点
这里小编只展示插入类型为原子类型的数据元素,如果要插入数据类型为列表的元素,其原理和插入原子类型数据是一样的,在此小编就不演示了。
//插入元素(第一个元素)
void InsertFirst(Mul_Lists *LS, const LS_Data Data)
{
//构造列表结点
LS_Node *pList = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(pList))
return;
//标记结点类型
pList->Tag = LIST;
//链接新的结点
pList->ptr.tp = LS->headPtr;
//构造原子结点
pList->ptr.hp = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(pList->ptr.hp))
return;
//标记结点类型
pList->ptr.hp->Tag = ATOM;
//更新结点数据
pList->ptr.hp->value = Data;
//更新头结点
LS->headPtr = pList;
//表长加一
LS->len++;
}
删除操作则与插入操作相反,我们要先来一个临时指针记录一下要删除的节点的位置,然后再修改头指针指向要删除节点的后一个节点。
最后在释放要删除节点的内存
//删除元素(第一个元素)
LS_Data DeleteFirst(Mul_Lists *LS)
{
LS_Data d = {0, '0'};
if (PTR_IS_NULL(LS->headPtr))
return d;
//如果头结点是列表结点,我们什么也不做
if (LS->headPtr->ptr.hp->Tag == LIST)
return d;
LS_Node *delNode = LS->headPtr;
//拷贝一下删除节点的数据
d = delNode->ptr.hp->value;
//后续节点变为头结点
LS->headPtr = delNode->ptr.tp;
//释放节点
free(delNode->ptr.hp);
free(delNode);
//表长减一
LS->len--;
return d;
}
在这里小编也只展示在表头删除元素类型为原子类型的节点。
#include "MultipleLists.h"
#include
int main(void)
{
Mul_Lists LS=GetMulLists();
LS_Data data[6];
for (size_t i = 0; i < 6; i++)
{
data[i].data = i + 100;
data[i].number = 'a' + i;
}
char s[CHAR_LEN] = "(a,b,(c,d),e,f)";
int i = 6;
InitMulLists(&LS, s, data, i);
Traverse(&LS);
printf("尾结点:%c\n", GetTail(&LS)->number);
printf("头结点:%c\n", GetHead(&LS)->number);
printf("长度:%d\n", Length(&LS));
printf("深度:%d\n", Depth(&LS));
LS_Data d = {100, 'p'};
InsertFirst(&LS, d);
printf("插入元素后:\n");
Traverse(&LS);
printf("删除元素后:\n");
d= DeleteFirst(&LS);
printf("删除的结点编号: %c 数值:%d\n",d.number,d.data);
Traverse(&LS);
printf("销毁广义表后:\n");
Destroy(&LS);
Traverse(&LS);
getchar();
return 0;
}
.h文件
#ifndef _MULTIPLELISTS_H_
#define _MULTIPLELISTS_H_
#include
#define CHAR_LEN 20
//节点类型枚举
typedef enum
{
ATOM, //原子类型
LIST //列表类型
} ElemTag;
struct LS_Data
{
int data; //存储数据
char number; //存储编号
};
typedef struct LS_Data LS_Data;
struct LS_Node
{
ElemTag Tag; //结点类型标识
union
{
LS_Data value;
struct
{
struct LS_Node *hp; //表头指针
struct LS_Node *tp; //表尾指针
} ptr;
};
};
typedef struct LS_Node LS_Node;
struct Mul_Lists
{
LS_Node *headPtr;//头结点
unsigned int len;//表的长度
LS_Node *lastNode;//末尾节点
};
typedef struct Mul_Lists Mul_Lists;
//返回一个空的广义表
Mul_Lists GetMulLists(void);
//构造广义表
void InitMulLists(Mul_Lists *LS, char *Num, LS_Data Data[], int Len);
//广义表是否为空
bool Empty(Mul_Lists *LS);
//销毁广义表
void Destroy(Mul_Lists *LS);
//获取广义表的长度
unsigned int Length(Mul_Lists *LS);
//获取广义表的深度
unsigned int Depth(Mul_Lists *LS);
//获取表头数据
LS_Data *GetHead(Mul_Lists *LS);
//获取表尾数据
LS_Data *GetTail(Mul_Lists *LS);
//插入元素(第一个元素)
void InsertFirst(Mul_Lists *LS, const LS_Data Data);
//删除元素(第一个元素)
LS_Data DeleteFirst(Mul_Lists *LS);
//遍历广义表
void Traverse(Mul_Lists* LS);
#endif
.cpp文件
#include "MultipleLists.h"
#include
#include
#include
#define PTR_IS_NULL(PTR) (PTR == NULL ? true : false)
#define STRING_LEN(STR) (strlen(STR))
//返回结点
static LS_Node *GetListsNode(ElemTag Tag);
//从数据数组中删除一个元素(伪删除)
static LS_Data *DeleteArray(LS_Data Data[], int *Len);
//从字符数组中删除首字符
static char *DeleteString(char **Str);
//输出结点信息
static void ShowNodeInfo(LS_Node *Node);
//销毁结点
static void FreeMulListsNode(LS_Node *Node);
//计算广义表深度
static int ListsDepth(LS_Node *Node);
//返回一个空的广义表
Mul_Lists GetMulLists(void)
{
Mul_Lists LS;
LS.headPtr = LS.lastNode = NULL;
LS.len = 0;
return LS;
}
//构造广义表
void InitMulLists(Mul_Lists *LS, char *Num, LS_Data Data[], int Len)
{
int len = 0;//记录表长度
LS_Node *curPtr = NULL;
//如果表的编号为空就直接返回
if (!STRING_LEN(Num))
return;
//给头结点赋值,只做一次(即进入递归之后不能修改头结点)
if (!LS->len)
{
LS->lastNode = LS->headPtr = GetListsNode(LIST);
curPtr = LS->lastNode;
}
//进入递归后会执行else中的代码
else
{
curPtr = GetListsNode(LIST);
LS->lastNode->ptr.hp = curPtr;
LS->lastNode = curPtr;
}
LS->len++;
//表长加一
++len;
//删除首字符
DeleteString(Num);
while (STRING_LEN(Num))
{
//修改LS中的表长
LS->len = len;
if (Num[0] == '(')
{
//记录尾结点
LS->lastNode = curPtr;
//进入递归
InitMulLists(LS, Num, Data, Len);
}
else if (Num[0] == ',')
{
curPtr = LS->lastNode;
curPtr->ptr.tp = GetListsNode(LIST);
curPtr = curPtr->ptr.tp;
LS->lastNode = curPtr;
//表长加一
++len;
}
else if (Num[0] == ')')
{
break;
}
else
{
//链接新节点
curPtr->ptr.hp = GetListsNode(ATOM);
//给节点数据赋值
curPtr->ptr.hp->value = Data[0];
//移除第一个数据
DeleteArray(Data, &Len);
}
//删除首字符
DeleteString(Num);
}
}
//广义表是否为空
bool Empty(Mul_Lists *LS)
{
return PTR_IS_NULL(LS->headPtr);
}
//销毁广义表
void Destroy(Mul_Lists *LS)
{
LS_Node *head = LS->headPtr;
if (PTR_IS_NULL(head))
return;
FreeMulListsNode(head);
}
//获取广义表的长度
unsigned int Length(Mul_Lists *LS)
{
return LS->len;
}
//获取广义表的深度
unsigned int Depth(Mul_Lists *LS)
{
//判断广义表是否为空
if (PTR_IS_NULL(LS->headPtr))
return 0;
return ListsDepth(LS->headPtr);
}
//获取表头数据
LS_Data *GetHead(Mul_Lists *LS)
{
return &LS->headPtr->ptr.hp->value;
}
//获取表尾数据
LS_Data *GetTail(Mul_Lists *LS)
{
return &LS->lastNode->ptr.hp->value;
}
//插入元素(第一个元素)
void InsertFirst(Mul_Lists *LS, const LS_Data Data)
{
//构造列表结点
LS_Node *pList = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(pList))
return;
//标记结点类型
pList->Tag = LIST;
//链接新的结点
pList->ptr.tp = LS->headPtr;
//构造原子结点
pList->ptr.hp = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(pList->ptr.hp))
return;
//标记结点类型
pList->ptr.hp->Tag = ATOM;
//更新结点数据
pList->ptr.hp->value = Data;
//更新头结点
LS->headPtr = pList;
//表长加一
LS->len++;
}
//删除元素(第一个元素)
LS_Data DeleteFirst(Mul_Lists *LS)
{
LS_Data d = {0, '0'};
if (PTR_IS_NULL(LS->headPtr))
return d;
//如果头结点是列表结点,我们什么也不做
if (LS->headPtr->ptr.hp->Tag == LIST)
return d;
LS_Node *delNode = LS->headPtr;
//拷贝一下删除节点的数据
d = delNode->ptr.hp->value;
//后续节点变为头结点
LS->headPtr = delNode->ptr.tp;
//释放节点
free(delNode->ptr.hp);
free(delNode);
//表长减一
LS->len--;
return d;
}
//输出结点信息
static void ShowNodeInfo(LS_Node *Node)
{
while (Node)
{
if (Node->ptr.hp->Tag == ATOM)
{
printf("结点编号:%c 结点数据:%d\n", Node->ptr.hp->value.number, Node->ptr.hp->value.data);
}
else if (Node->ptr.hp->Tag == LIST)
{
ShowNodeInfo(Node->ptr.hp);
}
Node = Node->ptr.tp;
}
}
//遍历广义表
void Traverse(Mul_Lists *LS)
{
LS_Node *head = LS->headPtr;
if (PTR_IS_NULL(head))
return;
ShowNodeInfo(head);
}
//返回结点
static LS_Node *GetListsNode(ElemTag Tag)
{
LS_Node *newNode = (LS_Node *)malloc(sizeof(LS_Node));
if (PTR_IS_NULL(newNode))
return NULL;
//构造原子结点
if (Tag == ATOM)
{
newNode->value.data = 0;
newNode->value.number = ' ';
newNode->Tag = ATOM;
}
//构造列表结点
else if (Tag == LIST)
{
newNode->Tag = LIST;
newNode->ptr.hp = newNode->ptr.tp = NULL;
}
return newNode;
}
//从数组中删除一个元素
static LS_Data *DeleteArray(LS_Data Data[], int *Len)
{
for (size_t i = 0; i < (*Len) - 1; i++)
{
(Data[i]) = (Data[i + 1]);
}
--(*Len);
return Data;
}
//从字符数组中删除首字符
static char *DeleteString(char **Str)
{
char s[CHAR_LEN] = "";
strcpy(s, (char *)(&*Str) + 1);
strcpy((char *)(&*Str), s);
return NULL;
}
//销毁结点
static void FreeMulListsNode(LS_Node *Node)
{
LS_Node *temp = NULL;
while (Node)
{
if (Node->ptr.hp->Tag == ATOM)
{
//释放原子结点
free(Node->ptr.hp);
}
else if (Node->ptr.hp->Tag == LIST)
{
//递归删除列表结点
FreeMulListsNode(Node->ptr.hp);
}
//保存下一个结点的位置
temp = Node->ptr.tp;
//释放列表结点
free(Node);
Node = temp;
}
}
//计算广义表深度
static int ListsDepth(LS_Node *Node)
{
//如果表为空表时,直接返回长度1;
if (PTR_IS_NULL(Node))
{
return 1;
}
//如果表为原子时,直接返回0;
if (Node->Tag == ATOM)
{
return 0;
}
int max = 0; //设置表的初始长度为0;
LS_Node *temp = Node;
while (temp)
{
int dep = ListsDepth(temp->ptr.hp);
if (dep > max)
{
max = dep; //每次找到表中遍历到深度最大的表,并用max记录
}
temp = temp->ptr.tp;
}
//程序运行至此处,表明广义表不是空表,由于原子返回的是0,而实际长度是1,所以,此处要+1;
return max + 1;
}
main文件
#include "MultipleLists.h"
#include
int main(void)
{
Mul_Lists LS=GetMulLists();
LS_Data data[6];
for (size_t i = 0; i < 6; i++)
{
data[i].data = i + 100;
data[i].number = 'a' + i;
}
char s[CHAR_LEN] = "(a,b,(c,d),e,f)";
int i = 6;
InitMulLists(&LS, s, data, i);
Traverse(&LS);
printf("尾结点:%c\n", GetTail(&LS)->number);
printf("头结点:%c\n", GetHead(&LS)->number);
printf("长度:%d\n", Length(&LS));
printf("深度:%d\n", Depth(&LS));
LS_Data d = {100, 'p'};
InsertFirst(&LS, d);
printf("插入元素后:\n");
Traverse(&LS);
printf("删除元素后:\n");
d= DeleteFirst(&LS);
printf("删除的结点编号: %c 数值:%d\n",d.number,d.data);
Traverse(&LS);
printf("销毁广义表后:\n");
Destroy(&LS);
Traverse(&LS);
getchar();
return 0;
}