所谓的广义表就是单链表的扩展,就是节点可以是一个元素也可以是一个链表。官方的说法是,原子节点和子表节点。广义表用递归的方法来创建是最简单易懂的,也符合广义表的思想。
1.广义表的各种形态
(1)、A = (); 空表
(2)、B = (()); 长度为1,深度为2,原子个数为0,这个实质上就是广义表中有一个元素,它是子表,该子表为空。
(3)、C = (a); 长度为1,深度为0,原子个数为1
(4)、D = (a,A,b);长度为3,深度为2,原子个数为2,其中A是一个空表。
(5)、E = (A,B,C,D);长度为4,深度为3,原子个数为3,其中ABCD都是表。
(6)、F = (a,F); 无限递归下去。
2.广义表C语言实现思路
先来看结构体定义
enum NodeType { ATOM, LIST };
typedef struct _GList
{
NodeType nodeType; //节点类型
union
{
char c; //原子
_GList* subList; //子表
};
_GList* next; //指向后继节点
}GList, *PGList;
(1)创建表
给定一个串,该串必须遵守相关规则,例如,层次分隔符用‘()’,元素分隔符用‘,’。
伪代码描述如下:
Create(PGList* gl,char* s)
{
如果s="()",则(*gl)=NULL
如果s='(',则递归创建子表,Create(&(*gl)->subList,s);
否则创建原子节点
如果s=',',则递归创建后继节点(该节点可能是ATOM或LIST)Create(&(*gl)->next,s);
如果s=')',则置(*gl)->next为NULL,表示该子表已经结束
如果s='\0',则置(*gl)->next为NULL,表示该广义表已经创建完成
}
重点!!! :为什么这里要传进去二级指针?刚开始我传进一级指针的时候,我发现创建完成后,gl = NULL,为什么?因为没有一个变量记录gl第一个节点的地址啊,所以最后找不到它了。这时候就需要一个二级指针,指向该头节点的地址。如图:
(2)计算广义表长度
这个就比较简单了,就和单链表一样,依次遍历第一层节点即可。
需要注意的是,因为创建广义表的时候,一开始就创建了一个子表节点,所以该节点才是广义表的头节点。
(3)计算广义表深度
递归遍历子表,取子表深度最大值。例如:(A,(B,C),(D,E,(F,G)),H)
此时最大深度在(F,G)这个子表处得到。
(4)添加原子到广义表前面或后面
这个操作和单链表的操作是一样的,广义表如果要实现更深层次的插入和添加会比较麻烦。
(5)打印广义表
一样的递归遍历,若是原子节点,则直接输出,所示子表节点,则递归进去。
2.广义表C++实现思路
广义表的节点类如下:
enum NodeType { ATOM, LIST }; //节点类型
private:
class Node //广义表结点类
{
public:
Node(NodeType nodeType, T atom)
:nodeType(nodeType), atom(atom) {}
private:
NodeType nodeType; //节点类型
union
{
T atom; //原子
Node* subList; //子表
};
Node* next; //指向下一个元素
friend class GList;
};
和C语言类似。
只不过C++我用模板实现,这样就能存储字符、整形、浮点型了。
基本思路和C语言类似,所以,我一般都是用C语言实现了简单的功能,然后用C++封装成一个完善的类。这里省略对C++的描述,具体看代码,里面有注释。