Linux环境下的C语言编程(五十二)

数组和广义表的对比

一、核心概念对比

1. 基本定义

复制代码
// 数组(固定大小,同类型)
int array[5] = {1, 2, 3, 4, 5};  // 5个整数的连续存储

// 广义表(递归结构,可嵌套)
typedef struct GLNode {
    int tag;  // 0:原子,1:表
    union {
        int atom;
        struct { struct GLNode *head, *tail; } ptr;
    };
} GLNode;

// 广义表示例:(1, (2, 3), 4)
GLNode* glist = ...;  // 复杂的链式结构

2. 内存结构对比

数组的内存布局
复制代码
内存地址: 1000   1004   1008   1012   1016
数组元素: arr[0] arr[1] arr[2] arr[3] arr[4]
值:        1      2      3      4      5
           ↓      ↓      ↓      ↓      ↓
         连续存储,固定间隔(sizeof(int)=4)
广义表的内存布局
复制代码
广义表:(1, (2, 3), 4)

内存结构:
┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐
│ tag=1│─head→│tag=0│─head→│tag=1│─head→│tag=0│
├─────┤      ├─────┤      ├─────┤      ├─────┤
│head─┼─┐    │atom=2│    │head─┼─┐    │atom=4│
│tail─┼─┼─┐  │     │    │tail─┼─┼─┐  │     │
└─────┘ │ │  └─────┘    └─────┘ │ │  └─────┘
        ↓ ↓                    ↓ ↓
    原子1     子表(2,3)          原子4

二、详细特性对比表

特性维度 数组 广义表
数据元素 必须同类型 可以是不同类型(通过tag区分)
结构形式 线性结构 非线性结构(树状)
存储方式 顺序存储(连续) 链式存储(非连续)
维度限制 固定维度(一维/多维) 无维度限制,可无限嵌套
大小 固定大小(编译时确定) 动态大小(运行时可变)
访问方式 随机访问(O(1)) 顺序访问(O(n))
内存效率 高(无额外开销) 低(指针开销+结构标记)
插入/删除 O(n)(需要移动元素) O(1)(修改指针)
查找效率 O(1)(已知下标) O(n)(需要遍历)

三、访问方式对比详解

1. 数组:随机访问

复制代码
数组访问:直接计算地址
int array[100];
array[50] = 123;  // 直接访问第51个元素

内存地址计算:
array[i]的地址 = 基地址 + i × 元素大小
这是一个常量时间操作:O(1)

2. 广义表:顺序访问

复制代码
// 广义表访问:需要遍历
int getNthElement(GList L, int n) {
    GLNode *p = L;
    int count = 0;
    
    while (p && count < n) {
        if (p->tag == LIST) {
            p = p->ptr.tail;  // 必须沿着指针走
            count++;
        } else {
            break;
        }
    }
    
    if (p && p->ptr.head && p->ptr.head->tag == ATOM) {
        return p->ptr.head->atom;
    }
    return -1;  // 访问失败
}

// 访问第n个元素需要遍历前n-1个元素:O(n)

四、存储效率对比分析

1. 数组存储开销

复制代码
假设存储1000个int
int dense_array[1000];
内存使用:1000 × 4字节 = 4000字节
额外开销:0字节
空间利用率:100%(假设全用)

2. 广义表存储开销

复制代码
假设存储相同的1000个int作为原子
每个节点需要:
struct GLNode {
    int tag;        // 4字节
    union {
        int atom;   // 4字节
        struct {
            GLNode *head;  // 8字节(64位)
            GLNode *tail;  // 8字节
        } ptr;
    };
};  // 至少12-16字节

内存使用:1000 × 16字节 ≈ 16000字节
额外开销:12000字节(指针和tag)
空间利用率:25%(仅原子值有用)

五、操作复杂度对比

数组操作复杂度

复制代码
int array[N];

// 1. 访问元素
array[i] = x;           // O(1)

// 2. 查找元素
for (i = 0; i < N; i++) // O(n)
    if (array[i] == x) break;

// 3. 插入元素(中间)
for (i = N-1; i >= pos; i--)  // O(n)
    array[i+1] = array[i];
array[pos] = new_value;

// 4. 删除元素(中间)
for (i = pos; i < N-1; i++)   // O(n)
    array[i] = array[i+1];

广义表操作复杂度

复制代码
GList list;

// 1. 访问第n个元素
getNthElement(list, n);  // O(n) - 需要遍历

// 2. 查找元素
findElement(list, x);    // O(n) - 递归遍历

// 3. 在头部插入
newList = createList(newElement, list);  // O(1)

// 4. 在尾部插入
appendElement(list, newElement);         // O(n) - 需要找到尾部

// 5. 复制操作
copyList(list);                          // O(n) - 递归复制所有节点

六、内存管理对比

数组内存管理

复制代码
// 1. 栈上分配(自动管理)
int localArray[100];  // 函数结束时自动释放

// 2. 堆上分配(手动管理)
int *dynamicArray = malloc(100 * sizeof(int));
// 使用...
free(dynamicArray);  // 必须手动释放

// 特点:一次性分配/释放,管理简单

广义表内存管理

复制代码
// 1. 节点逐个分配
GLNode* createAtom(int value) {
    GLNode *node = malloc(sizeof(GLNode));
    node->tag = ATOM;
    node->atom = value;
    return node;
}

// 2. 递归释放
void destroyGList(GList L) {
    if (!L) return;
    if (L->tag == LIST) {
        destroyGList(L->ptr.head);
        destroyGList(L->ptr.tail);
    }
    free(L);
}

// 特点:分散分配,需要递归释放,容易内存泄漏
相关推荐
pwn蒸鱼2 小时前
buuctf中的ciscn_2019_es_2(栈迁移)
linux·安全
牛奶咖啡132 小时前
Linux的实用技巧——终端安全会话、命令提示工具安装使用、端口连通性测试与rm命令无法使用解决方案
linux·tmux·linux实现后台安全运行会话·linux的端口连通性测试·linux的命令提示工具·rm命令无法使用解决方法·tldr
BD_Marathon2 小时前
Spring是什么
java·后端·spring
我命由我123452 小时前
Android 消息机制 - Looper(Looper 静态方法、Looper 静态方法注意事项、Looper 实例方法、Looper 实例方法注意事项)
android·java·android studio·安卓·android jetpack·android-studio·android runtime
Warren982 小时前
MySQL 8 中的保留关键字陷阱:当表名“lead”引发 SQL 语法错误
linux·数据库·python·sql·mysql·django·virtualenv
Hard but lovely2 小时前
linux: pthread库---posix线程创建使用接口&&状态
linux·开发语言·c++
月明长歌2 小时前
【码道初阶】Leetcode138:随机链表的复制:用 HashMap 做深拷贝的标准解法
java·数据结构·算法·leetcode·链表·哈希算法
柏木乃一2 小时前
进程(7)命令行参数与环境变量
linux·服务器·shell·环境变量·鸣潮
消失的旧时光-19432 小时前
从 JVM 到 Linux:一次真正的系统级理解
android·linux·jvm