数据结构之广义表(C语言)

文章目录
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种数据结构来表示结点:一个是表结点,用来表示列表,另一个是原子结点,用来表示原子。
数据结构之广义表(C语言)_第1张图片
数据结构之广义表(C语言)_第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)为例。
下面是该广义表的内存模型:
数据结构之广义表(C语言)_第3张图片
空的方框表示列表结点

3.1, 广义表所需的操作


//返回一个空的广义表
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);

4, 操作代码实现

4.1,构造广义表

 首先我们先定义一个用于构造结点的函数。

//返回结点
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);
    }
}

4.2 ,遍历广义表

 在遍历时,我们需要一个辅助函数来输出结点信息。


//输出结点信息
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);
}

4.3,销毁广义表

 在销毁广义表时,我们定义一个辅助函数来帮助我们销毁。


//销毁结点
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);
}

4.4,判断广义表是否为空

 该操作非常简单,直接判断头结点指针是否为空即可。

//广义表是否为空
bool Empty(Mul_Lists *LS)
{
    return PTR_IS_NULL(LS->headPtr);
}

4.5,获取表头数据

 由于在我们的表结构中存储了头结点指针,因此我们只需访问头指针数据即可。

//获取表头数据
LS_Data *GetHead(Mul_Lists *LS)
{
    return &LS->headPtr->ptr.hp->value;
}

4.6,获取表尾数据

 由于在我们的表结构中存储了尾结点指针,因此我们只需访问尾指针数据即可。

//获取表尾数据
LS_Data *GetTail(Mul_Lists *LS)
{
    return &LS->lastNode->ptr.hp->value;
}

4.7,计算广义表的长度

 广义表的长度实际就是广义表中数据元素的数量,子表算一个元素,原子算一个元素。
例如表LS=(a,(b,c),d,(e,(f)))
该表的长度就为4。其中a、(b,c)、d、(e,(f))都是一个元素。
  由于在我们构建广义表的时候就已经记录过表的长度了,所以不需要再去重新遍历表,重新计算长度。

//获取广义表的长度
unsigned int Length(Mul_Lists *LS)
{
    return LS->len;
}

4.8,计算广义表的深度

 广义表的深度定义为广义表中括弧(括号)的重数,是广义表的一种度量。如果广义表的结点为原子结点其深度就为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);
}

4.9,插入操作

 在这里小编只为大家展示如何在广义表的表头插入数据。首先我们要先分配一个列表结点和一个原子结点。
数据结构之广义表(C语言)_第4张图片
然后将新节点的tp指针指向头结点,将头指针headPtr指向新的节点
数据结构之广义表(C语言)_第5张图片
这里小编只展示插入类型为原子类型的数据元素,如果要插入数据类型为列表的元素,其原理和插入原子类型数据是一样的,在此小编就不演示了。

//插入元素(第一个元素)
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++;
}

4.10,删除操作

 删除操作则与插入操作相反,我们要先来一个临时指针记录一下要删除的节点的位置,然后再修改头指针指向要删除节点的后一个节点。
数据结构之广义表(C语言)_第6张图片
最后在释放要删除节点的内存

//删除元素(第一个元素)
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;
}

在这里小编也只展示在表头删除元素类型为原子类型的节点。

5,代码测试

#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;
}

数据结构之广义表(C语言)_第7张图片

6,完整代码

.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;
}

你可能感兴趣的:(C语言,数据结构,数据结构,算法,c语言)