数据结构:广义表( Generalized List)及其实现

什么是广义表?

广义表(Generalized List)是一种扩展的线性表,它可以存储原子(单个数据元素)或子表(另一个广义表)。广义表的特点是:它可以递归定义,也就是说,一个广义表的元素可以是另一个广义表。

举个例子:

  • A = (1, 2, 3):这是一个普通的线性表,包含 3 个原子。

  • B = (1, (2, 3), 4):这是一个广义表,包含 2 个原子和 1 个子表 (2, 3)

  • C = (1, (2, (3, 4)), 5):这是一个更复杂的广义表,包含嵌套的子表。

广义表的灵活性使其非常适合表示复杂的数据结构,比如树、图等。


广义表的存储方式

1. 顺序存储

顺序存储是用数组来存储广义表。由于广义表的元素可以是原子或子表,因此需要额外的标记来区分原子和子表。

优点

  • 访问速度快,可以通过下标直接访问元素。

  • 实现简单。

缺点

  • 插入和删除操作需要移动大量数据,效率低。

  • 数组大小固定,不适合动态变化的广义表。


2. 链式存储

链式存储是用链表来存储广义表。每个节点可以是原子节点或子表节点,通过指针连接。

优点

  • 插入和删除操作效率高,不需要移动大量数据。

  • 适合动态变化的广义表。

缺点

  • 访问速度慢,需要从头开始遍历。

  • 内存开销大,每个节点需要额外存储指针。


广义表的基本概念

1. 长度

广义表的长度是指广义表中元素的个数。例如:

  • A = (1, 2, 3) 的长度是 3。

  • B = (1, (2, 3), 4) 的长度是 3(原子 1、子表 (2, 3) 和原子 4)。

2. 深度

广义表的深度是指广义表中嵌套的层数。例如:

  • A = (1, 2, 3) 的深度是 1。

  • B = (1, (2, 3), 4) 的深度是 2。

  • C = (1, (2, (3, 4)), 5) 的深度是 3。


广义表的基本操作

广义表的基本操作包括:增、删、查、改。下面我们用大白话解释这些操作。

  1. 增加元素

    • 在广义表的指定位置插入一个原子或子表。
  2. 删除元素

    • 删除广义表中指定位置的原子或子表。
  3. 查找元素

    • 查找广义表中是否存在指定的原子或子表。
  4. 修改元素

    • 修改广义表中指定位置的原子或子表。

C 语言实现广义表

下面是一个简单的广义表实现代码,包含初始化、插入、删除、查找和打印功能。

复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义广义表节点类型
typedef enum { ATOM, LIST } NodeType;

// 定义广义表节点结构体
typedef struct GLNode {
    NodeType tag;  // 节点类型:ATOM(原子)或 LIST(子表)
    union {
        char data;  // 原子数据
        struct GLNode *sublist;  // 子表指针
    };
    struct GLNode *next;  // 指向下一个节点的指针
} GLNode;

// 创建原子节点
GLNode* CreateAtom(char data) {
    GLNode *node = (GLNode*)malloc(sizeof(GLNode));
    node->tag = ATOM;
    node->data = data;
    node->next = NULL;
    return node;
}

// 创建子表节点
GLNode* CreateList(GLNode *sublist) {
    GLNode *node = (GLNode*)malloc(sizeof(GLNode));
    node->tag = LIST;
    node->sublist = sublist;
    node->next = NULL;
    return node;
}

// 插入元素
void InsertElement(GLNode *list, int pos, GLNode *element) {
    GLNode *current = list;
    for (int i = 0; i < pos && current != NULL; i++) {
        current = current->next;
    }
    if (current != NULL) {
        element->next = current->next;
        current->next = element;
    }
}

// 删除元素
void DeleteElement(GLNode *list, int pos) {
    GLNode *current = list;
    for (int i = 0; i < pos && current != NULL; i++) {
        current = current->next;
    }
    if (current != NULL && current->next != NULL) {
        GLNode *temp = current->next;
        current->next = temp->next;
        free(temp);
    }
}

// 查找元素
GLNode* FindElement(GLNode *list, char data) {
    GLNode *current = list;
    while (current != NULL) {
        if (current->tag == ATOM && current->data == data) {
            return current;
        }
        current = current->next;
    }
    return NULL;
}

// 打印广义表
void PrintList(GLNode *list) {
    GLNode *current = list;
    printf("广义表内容:");
    while (current != NULL) {
        if (current->tag == ATOM) {
            printf("%c ", current->data);
        } else if (current->tag == LIST) {
            printf("(");
            PrintList(current->sublist);
            printf(") ");
        }
        current = current->next;
    }
}

int main() {
    // 创建广义表 B = (1, (2, 3), 4)
    GLNode *sublist = CreateList(CreateAtom('2'));
    sublist->next = CreateAtom('3');
    GLNode *list = CreateAtom('1');
    list->next = CreateList(sublist);
    list->next->next = CreateAtom('4');

    PrintList(list);  // 打印广义表
    printf("\n");

    // 插入元素
    InsertElement(list, 1, CreateAtom('5'));
    PrintList(list);  // 打印广义表
    printf("\n");

    // 删除元素
    DeleteElement(list, 2);
    PrintList(list);  // 打印广义表
    printf("\n");

    // 查找元素
    GLNode *target = FindElement(list, '2');
    if (target != NULL) {
        printf("找到元素:%c\n", target->data);
    } else {
        printf("未找到元素\n");
    }

    return 0;
}

广义表的复制

广义表的复制需要递归地复制每个节点。如果是原子节点,直接复制数据;如果是子表节点,递归复制子表。


广义表的使用场景

  1. 表示树结构:广义表可以表示树结构,比如文件系统的目录结构。

  2. 表示图结构:广义表可以表示图结构,比如社交网络的关系图。

  3. 数学表达式 :广义表可以表示数学表达式,比如 (a + b) * (c - d)


应用实例:文件系统

文件系统的目录结构可以用广义表表示:

  • 原子节点:表示文件。

  • 子表节点:表示目录,目录中可以包含文件或其他目录。


总结

广义表是一种非常灵活的数据结构,适合表示复杂的数据结构。通过学习广义表的基本操作和实现,你可以更好地解决实际问题。希望通过这篇文章,你能轻松掌握广义表的相关知识!

相关推荐
董董灿是个攻城狮1 天前
AI视觉连载8:传统 CV 之边缘检测
算法
RuoZoe1 天前
重塑WPF辉煌?基于DirectX 12的现代.NET UI框架Jalium
c语言
AI软著研究员1 天前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish1 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱1 天前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者2 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮2 天前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者2 天前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考2 天前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx2 天前
CART决策树基本原理
算法·机器学习