您的位置:首页 > 理论基础 > 数据结构算法

8.数据结构之广义表

2015-12-18 12:50 543 查看

数据结构之广义表

广义表定义

广义表从结构上来说是对链表的一种扩展,也就是说其中的某个节点可能又指向了另外一个链表(这样的节点叫做子表节点),那么对于广义表这种数据类型的设定一定会大量的涉及递归的算法,因为不管是广义表还是二叉树类型,它们从结构上都具有不断递归的过程。

如下图所示是广义表的常见结构:



从应用的角度来看,广义表被广泛的应用于人工智能等领域的表处理语言LISP中,在LISP语言中,广义表化i一种最基本的数据结构。下面让我么不展示LISP的程序:

[code]
//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)。在枚举类型中对它们进行了定义:

[code]//广义表节点类型
typedef enum {
    HEAD = 0,    //子表类型(头结点)
    INT,         //整型类型     
    CHARACTER,   //子符类型
    LIST         //子表地址
}Node_type;


广义表中的节点内容需要有以下几个:

1.节点类型

2.实际存储的值(不同的类型值不同,可以采用共用体)

3.指向下一个广义表节点的指针

[code]//广义表的节点结构
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;


下面我们就广义表的常见接口进行介绍:

[code]//广义表接口
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)     ;    //显示广义表信息


上述介绍了广义表的基本操作,下面我们来实现广义表的接口:

[code]//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");
}


在实现了广义表的基本操作后我们需要进行测试:

[code]//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文件。

[code]//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


[code]//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真的是一个不错的语言,大家可以尝试尝试。^_^
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: