广义表从结构上来说是对链表的一种扩展,也就是说其中的某个节点可能又指向了另外一个链表(这样的节点叫做子表节点),那么对于广义表这种数据类型的设定一定会大量的涉及递归的算法,因为不管是广义表还是二叉树类型,它们从结构上都具有不断递归的过程。
从应用的角度来看,广义表被广泛的应用于人工智能等领域的表处理语言LISP中,在LISP语言中,广义表化i一种最基本的数据结构。下面让我么不展示LISP的程序:
//LISP语言
(defun search-start (open closed)
(let ((q (first open))
(seq (rest open)))
(cond ((null open) nil)
((find q finish-status :test 'equal) (list seq (cons q closed))) (t (search-start (funcall update-open-lst seq (expand-vertex q rules-set expand-func (cons q closed)) ) (cons q closed))))))
可以看到lisp的代码里充斥着“()”,这些就是广义表中的子表元素,针对一个广义表,我们需要了解其组织结构、元素数量、广义表深度等信息。下节我们将会设计一个广义表,如果有兴趣的同学可以学习下LISP的使用,据说掌握LISP的程序员都是天才。
为了简化问题的规模,我们对于广义表内数据类型设定为以下几种:整型(INT)、字符型(CHARACTER)、头结点(HEAD)和子表类型(LIST)。在枚举类型中对它们进行了定义:
//广义表节点类型
typedef enum {
HEAD = 0, //子表类型(头结点)
INT, //整型类型
CHARACTER, //子符类型
LIST //子表地址
}Node_type;
广义表中的节点内容需要有以下几个:
1.节点类型
2.实际存储的值(不同的类型值不同,可以采用共用体)
3.指向下一个广义表节点的指针
//广义表的节点结构
typedef struct Gen_node{
//1.标志(类型) int char list head
Node_type n_type;
//2.实际存储的值 int char (Gen_node *)
union {
int int_value;
char char_value;
int head_flag;
struct Gen_node *head;
} value; //根据节点类型选取不同的值
//3.next指针
struct Gen_node *next; //指向广义表的下一个元素
}Gen_node;
typedef struct Gen_node *Gen_list;
下面我们就广义表的常见接口进行介绍:
//广义表接口
Gen_list init_genlist(char *input_str) ; //广义表的创建
void destroy_genlist(Gen_list *gen_list) ; //广义表的销毁
int get_genlist_count(Gen_list gen_list); //得到广义表的元素个数
int get_genlist_depth(Gen_list gen_list); //得到广义表的深度
Gen_list copy_genlist(Gen_list gen_list) ; //广义表的拷贝
void show_genlist(Gen_list gen_list) ; //显示广义表信息
上述介绍了广义表的基本操作,下面我们来实现广义表的接口:
//gen_list.c
#include "gen_list.h"
#include "tools.h"
#include <ctype.h>
#include <strings.h>
#include <stdio.h>
static Boolean is_input_empty(const char *string);
static Boolean is_braket_match1(const char *string); //括号匹配
static Boolean is_braket_match(const char *string); //括号匹配
static Gen_list create_genlist(char *string);
//创建广义表节点
static Gen_node *create_node(void);
static Gen_node *create_head_node(int head_flag);
static Gen_node *create_int_node(int int_value);
static Gen_node *create_char_node(char character);
static Gen_node *create_list_node(Gen_node *p_list);
//删除空格
static char *delete_blank(char *string);
//去掉最外层括号
static void delete_braket(const char *src_str,
char *des_str,
int src_len);
static void get_item(char *sub_str, char *item_str);
static void show_genlist_value(Gen_list gen_list); //显示广义表信息
static void get_item(char *sub_str, char *item_str)
{
//15,'c',(20,'d',(30,'f','i')),(12,32),60
//
// ((), 'a', 12)
// (12, 'd', 34), 'a', 12
//
// 12
int i = 0;
int flag = 0;
int sub_len = strlen(sub_str);
while(i < sub_len){
if(sub_str[i] == '('){
flag++;
}
if(sub_str[i] == ')'){
flag--;
}
//当前没有在子表里,逗号可以进行元素分割
if(flag == 0 && sub_str[i] == ','){
break ;
}
i++;
}
//获取广义表元素
if(i == sub_len){ //只有一个元素
strcpy(item_str, sub_str);
sub_str[0] = '\0';
}else{ //把当前的元素复制给item_str,并且在原列表删除复制的元素
strncpy(item_str, sub_str, i);
item_str[i] = '\0';
strcpy(sub_str, sub_str + i + 1);
}
}
static void delete_braket(const char *src_str,
char *des_str,
int src_len)
{
// (asdfasd)
// asdfasd\0
strncpy(des_str, src_str + 1, src_len - 2);
des_str[src_len - 2] = '\0';
}
static char *delete_blank(char *string)
{
// adsfad a dsd\0
// adsfadadsd\0
int i = 0;
int j = 0;
if(string == NULL){
return string;
}
while(string[j] = string[i]){
if(isblank(string[i])){
i++;
continue ;
}
i++;
j++;
}
return string;
}
static Gen_node *create_head_node(int head_flag)
{
Gen_node *node = create_node();
node->n_type = HEAD;
node->value.head_flag = head_flag;
return node;
}
static Gen_node *create_int_node(int int_value)
{
Gen_node *node = create_node();
node->n_type = INT;
node->value.int_value = int_value;
return node;
}
static Gen_node *create_char_node(char character)
{
Gen_node *node = create_node();
node->n_type = CHARACTER;
node->value.char_value = character;
return node;
}
static Gen_node *create_list_node(Gen_node *p_list)
{
Gen_node *node = create_node();
node->n_type = LIST;
node->value.head = p_list;
return node;
}
static Gen_node *create_node(void)
{
Gen_node *result = (Gen_node *)Malloc(sizeof(Gen_node));
bzero(result, sizeof(Gen_node));
return result;
}
static Gen_list create_genlist(char *string)
{
char *sub_str = NULL;
char *item_str = NULL;
int str_len = strlen(string);
Gen_node *p_node = NULL;
//判断子表是否为空
if(is_input_empty(string) == TRUE){
fprintf(stderr, "input illegal!\n");
return NULL;
}
//(15, 'c', (20, 'd', (30, 'f', 'i')),(12, 32), 60)
//15, 'c', (20, 'd', (30, 'f', 'i')),(12, 32), 60
//
// ((12, 'c', 'd'))
//
Gen_list start = create_head_node(1); //广义表的头结点
p_node = start;
sub_str = (char *)Malloc(sizeof(char) * str_len);
item_str = (char *)Malloc(sizeof(char) * str_len);
//1.首先去掉最外层括号
delete_braket(string, sub_str, str_len);
// printf("%s\n", sub_str);
while(strlen(sub_str)){
//2.通过逗号分隔广义表元素
get_item(sub_str, item_str);
if(item_str[0] != '(' && item_str[0] != '\''){ //整型类型
p_node->next = create_int_node(atoi(item_str));
}else if(item_str[0] != '(' && item_str[0] == '\''){ //子符类型
p_node->next = create_char_node(item_str[1]);
}else{ //子表类型
p_node->next = create_list_node(create_genlist(item_str));
}
p_node = p_node->next;
}
//3.根据元素类型构造节点(如果遇到子表要递归调用)
// strncpy
free(sub_str);
free(item_str);
return start;
}
static Boolean is_input_empty(const char *string)
{
// "()"
return strlen(string) == ZERO || strcmp(string, "()") == 0;
}
static Boolean is_braket_match(const char *string) //括号匹配
{
int flag = MATCH;
int i = 1;
if(string[0] != '('){
return NOT_MATCH;
}
flag++; //说明第一个是左括号成立
while(string[i] != '\0'){
if(string[i] == '('){
flag++;
}else if(string[i] == ')'){
flag--;
}
if(flag == MATCH && string[i+1] != '\0'){
return NOT_MATCH;
}
i++;
}
return flag == MATCH ? MATCH : NOT_MATCH;
}
static Boolean is_braket_match1(const char *string)
{
int flag = MATCH;
int length = strlen(string);
int i = 1;
if(string[0] != '(' || string[length - 1] != ')'){
return NOT_MATCH;
}
while(i < length - 1){
if(string[i] == '('){
flag++;
}else if(string[i] == ')'){
flag--;
}
if(flag < MATCH){
return NOT_MATCH;
}
i++;
}
return flag == MATCH ? MATCH : NOT_MATCH;
}
Gen_list init_genlist(char *input_str) //广义表的创建
{
//判断输入字符串是否符合条件
if(input_str == NULL || is_input_empty(input_str) == TRUE
|| is_braket_match(input_str) == NOT_MATCH){
return NULL;
}
delete_blank(input_str);
//使用字符串构建广义表,遇到子表结构递归调用
return create_genlist(input_str);
}
void destroy_genlist(Gen_list *gen_list) //广义表的销毁
{
Gen_node *p_node = NULL;
if(gen_list == NULL || *gen_list == NULL){
return ;
}
p_node = *gen_list;
while(p_node != NULL){
*gen_list = p_node->next;
if(p_node->n_type == LIST){
//如果是子表,则递归调用销毁函数
destroy_genlist(&(p_node->value.head));
}
free(p_node);
p_node = *gen_list;
}
}
int get_genlist_count(Gen_list gen_list) //得到广义表的元素个数
{
int count = 0;
Gen_node *p_node = NULL;
if(gen_list == NULL){
return 0;
}
for(p_node = gen_list->next; p_node; p_node = p_node->next){
count++;
}
return count;
}
int get_genlist_depth(Gen_list gen_list) //得到广义表的深度
{
int max = 0; //记录最高子表的高度
int child_depth = 0;
Gen_node *p_node = NULL;
if(gen_list == NULL || gen_list->next == NULL){
return max;
}
//找到当前广义表中子表内深度最深的(max),当前广义表深度等于: max + 1
p_node = gen_list->next;
while(p_node != NULL){
if(p_node->n_type == LIST){ //子表类型
child_depth = get_genlist_depth(p_node->value.head);
if(max < child_depth){ //当前子表深度大于之前的子表
max = child_depth;
}
}
p_node = p_node->next;
}
return max + 1;
}
Gen_list copy_genlist(Gen_list gen_list) //广义表的拷贝
{
Gen_list result = NULL;
Gen_node *p_node = NULL;
Gen_node *q_node = NULL;
if(gen_list == NULL || gen_list->next == NULL){
return result;
}
result = create_head_node(1);
p_node = gen_list->next;
q_node = result;
while(p_node != NULL){
if(p_node->n_type == INT){
q_node->next = create_int_node(p_node->value.int_value);
}else if(p_node->n_type == CHARACTER){
q_node->next = create_char_node(p_node->value.char_value);
}else{
q_node->next = create_list_node(copy_genlist(p_node->value.head));
}
p_node = p_node->next;
q_node = q_node->next;
}
return result;
}
static void show_genlist_value(Gen_list gen_list) //显示广义表信息
{
Gen_node *p_node = NULL;
if(gen_list == NULL){
return ;
}
printf("(");
p_node = gen_list->next;
while(p_node != NULL){
if(p_node->n_type == INT){
printf("%d", p_node->value.int_value);
}else if(p_node->n_type == CHARACTER){
printf("'%c'", p_node->value.char_value);
}else{
show_genlist_value(p_node->value.head);
}
if(p_node->next != NULL){
printf(", ");
}
p_node = p_node->next;
}
printf(")");
}
void show_genlist(Gen_list gen_list) //显示广义表信息
{
show_genlist_value(gen_list);
printf("\n");
}
在实现了广义表的基本操作后我们需要进行测试:
//main.c测试文件
#include <stdio.h>
#include <stdlib.h>
#include "gen_list.h"
int main(int argc, char **argv)
{
Gen_list list = NULL;
Gen_list list2 = NULL;
char str[] = "(15, 'c', (20, 'd', (30, 'f')), ('g', 'i'), 60)";
list = init_genlist(str);
show_genlist(list); //显示广义表信息
printf("the count of list:%d\n", get_genlist_count(list));
printf("the depth of list:%d\n", get_genlist_depth(list));
list2 = copy_genlist(list); //广义表的拷贝
show_genlist(list2); //显示广义表信息
destroy_genlist(&list); //广义表的销毁
destroy_genlist(&list2);
return 0;
}
//(15, 'c', (20, 'd', (30, 'f')), ('g', 'i'), 60)
在广义表的实现中需要借助工具类操作,所以和往常一样,我们需要介绍tool.c和tool.h文件。
//tools.h
#ifndef _TOOLS_H_
#define _TOOLS_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE (1)
#define FALSE (0)
#define ZERO (0)
typedef unsigned char Boolean;
//工具类接口
void *Malloc(size_t size);
void swap(void *a, void *b, int length);
#endif
//tools.c
#include "tools.h"
void *Malloc(size_t size)
{
void *result = malloc(size);
if(result == NULL){
fprintf(stderr, "the memory is full!\n");
exit(1);
}
return result;
}
void swap(void *a, void *b, int length)
{
void *temp = Malloc(length);
memcpy(temp, a, length);
memcpy(a, b, length);
memcpy(b, temp, length);
free(temp);
}
关于广义表的操作介绍到这里,借助广义表我们了解了递归的过程,为以后学习二叉树打好了基础,另外值的一提的是,LISP真的是一个不错的语言,大家可以尝试尝试。^_^