一、试编写递归算法,输出广义表中所有原子项及其所在层次。
答:
分析题意,我们很容易知道我们首先根据广义表的tag标志判断该节点是否是原子项。如果是原子项,则直接输出其原子项和所在的层数。如果不是原子项则通过对表头结点和表尾结点进行递归然后实现对整个广义表的遍历过程。
该算法实现的伪代码如下:
/* 函数名称:输出广义表中所有原子项及其所在层次 传入参数:广义表g,初始层数layer 返回值:void类型 */ void PrintAtom(GList *g, int layer) { if(g != NULL){ //如果广义表非空 //如果是原子节点,直接输出 if(g -> tag == ATOM) { print(g -> un.atom, layer); } else{ PrintAtom(g -> un.ptr.hp, layer + 1);//表头节点递归 PrintAtom(g -> un.ptr.tp, layer);//表尾节点递归 } } }
算法分析:该算法是一个递归算法,对空间会有一定的开销。但是代码更加间接。同时,运行时间也比较快,是一个高效的算法。
二、试编写递归算法,删除广义表中所有值等于x的原子项。
答:
分析题意后,我们先需要对广义表g进行判断。判断其是否为空表。然后声明一个广义表类型指针s用于遍历。通过判断此状态对应下的tag的值是否是List来决定后续的操作。如果tag是List继续进行递归遍历。如果不是的话我们需要判断原子项是否等于x。如果是x则删除,不是的话则继续递归查询。
该算法实现的伪代码如下:
/* 函数名称:删除广义表中值为x的原子项 传入参数:广义表g,原子项的值x 返回值:void类型 */ void deleteGlistWhereAtomIsX(GList *g, AtomType x) { GList *s; if(g != NULL) { //判断广义表是否为空 if(g -> tag == List) { //判断是否为原子项 deleteGlistWhereAtomIsX(g -> hp, x); //继续递归 deleteGlistWhereAtomIsX(g -> tp, x); } else { if(g -> atom == x) { //判断原子项的值是否为x s = g //删除操作 g = g -> tp; free(s); s = NULL; deleteGlistWhereAtomIsX(g, x); } else { deleteGlistWhereAtomIsX(g -> tp, x); //继续递归 } } } }
算法分析:该算法是个递归算法。同样也会带来额外的空间开销。写法相对优雅、简洁。
三、试编写算法,依次从左至右输出广义表中第l层的原子项。例如:广义表(a,(b,(c)),(d))中的a为第一层的原子项;b和d为第二层的原子项;c为第三层的原子项。
答:
分析题意后,我们首先判断此时的层数i是否小于等于l,弱国小于等于继续执行,反之则结束执行。同时我们需要判断广义表是否为空。当指针对应的tag为Atom时我们要继续验证层数是否符合,如果符合我们就打印结果。反之,我们继续进行递归遍历,直至找到结果或者遍历完广义表。
该算法实现的伪代码如下:
/* 函数名称:输出广义表中第l层的原子项 传入参数:广义表g,当前层数i,目标层数l 返回值:void类型 */ void PrintGlistWhereLayerIsL(GList *g, int i, int l) { if(g != NULL && l >= i) { //判断广义表是否为空和当前深度与目标深度关系 if(g -> tag == Atom) { //判断是否为原子项 if(l == i) { Print(g -> atom); } } else { PrintGlistWhereLayerIsL(g -> hp, i + 1, l); //继续进行递归操作 PrintGlistWhereLayerIsL(g -> tp, i, l); } } }
算法分析:通过递归的方法实现了输出第l层的原子项,代码比较间接,可读性也较高。同时,递归会代码栈空间的开销,整体来说该算法效率较好。