顺序表(Sequential List)详解:从数组到 C 语言实现

一、什么是顺序表?

顺序表(Sequential List)是用一段地址连续的存储单元依次存储数据元素的线性结构。换句话说,就是把数据元素一个接一个地放在连续的内存空间里。

听起来是不是很熟悉?没错,这和我们日常使用的数组(Array)几乎是一模一样的概念。

💡 一句话总结:顺序表是数据结构中的"学术名称",数组是编程语言中的"实现工具"。在大多数场景下,它们描述的是同一种存储方式。


二、数组与顺序表的关系

对比维度 数组(Array) 顺序表(Sequential List)
本质 编程语言提供的数据类型 数据结构中的逻辑概念
存储方式 连续的内存空间 连续的存储单元
元素访问 通过下标 arr[i] 通过位置索引
元素类型 通常要求相同类型 通常要求相同类型
大小 定义时确定(静态)或动态扩展 可静态可动态

核心关系:数组是顺序表最常见的实现方式。当我们用数组来存储一组逻辑上相邻的数据时,这个数组就是一个顺序表。


三、顺序表的特点

✅ 优点

  1. 随机访问快:通过下标可以直接定位到任意元素,时间复杂度为 O(1)。
  2. 存储密度高:只存储数据本身,不需要额外的空间存储关系。
  3. 实现简单:利用数组即可实现,代码直观易懂。

❌ 缺点

  1. 插入和删除效率低:中间插入或删除元素时,需要移动大量元素,时间复杂度为 O(n)。
  2. 容量固定:静态顺序表的大小在创建时就确定了,难以灵活扩展。

四、用 C 语言实现静态顺序表

由于我们目前还没接触指针和动态内存,这里先用静态数组来实现一个完整的顺序表。这种方式虽然容量固定,但非常适合理解顺序表的核心思想。

4.1 定义顺序表

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

#define MAXSIZE 100 // 顺序表的最大容量

// 定义顺序表结构
typedef struct {
    int data[MAXSIZE]; // 存储数据的数组
    int length;        // 当前顺序表的长度(元素个数)
} SeqList;

这里我们用结构体 SeqList 来封装数组和当前长度,这样操作起来更加清晰。

4.2 初始化顺序表

c 复制代码
// 初始化顺序表
void initList(SeqList *list) {
    list->length = 0; // 初始时没有任何元素
}

4.3 在末尾添加元素(插入)

c 复制代码
// 在顺序表末尾添加一个元素
int append(SeqList *list, int value) {
    if (list->length >= MAXSIZE) {
        printf("顺序表已满,无法添加!\n");
        return 0; // 添加失败
    }
    list->data[list->length] = value; // 放到当前末尾位置
    list->length++;                   // 长度加 1
    return 1; // 添加成功
}

4.4 在指定位置插入元素

c 复制代码
// 在位置 pos(从 0 开始)插入元素 value
int insert(SeqList *list, int pos, int value) {
    // 检查合法性
    if (pos < 0 || pos > list->length) {
        printf("插入位置不合法!\n");
        return 0;
    }
    if (list->length >= MAXSIZE) {
        printf("顺序表已满,无法插入!\n");
        return 0;
    }

    // 从后往前,把 pos 位置及之后的元素都后移一位
    for (int i = list->length; i > pos; i--) {
        list->data[i] = list->data[i - 1];
    }

    list->data[pos] = value; // 放入新元素
    list->length++;          // 长度加 1
    return 1;
}

🔍 思考一下:为什么循环要从后往前移动?如果从前往后移动会发生什么?

4.5 删除指定位置的元素

c 复制代码
// 删除位置 pos 的元素
int delete(SeqList *list, int pos) {
    if (pos < 0 || pos >= list->length) {
        printf("删除位置不合法!\n");
        return 0;
    }

    // 从 pos 位置开始,后面的元素依次前移
    for (int i = pos; i < list->length - 1; i++) {
        list->data[i] = list->data[i + 1];
    }

    list->length--; // 长度减 1
    return 1;
}

4.6 查找元素

c 复制代码
// 按值查找,返回元素位置,找不到返回 -1
int find(SeqList list, int value) {
    for (int i = 0; i < list.length; i++) {
        if (list.data[i] == value) {
            return i; // 找到了,返回下标
        }
    }
    return -1; // 没找到
}

// 按位置获取元素
int get(SeqList list, int pos) {
    if (pos < 0 || pos >= list.length) {
        printf("位置不合法!\n");
        return -1;
    }
    return list.data[pos];
}

4.7 遍历输出

c 复制代码
// 打印顺序表中的所有元素
void printList(SeqList list) {
    printf("顺序表内容:");
    for (int i = 0; i < list.length; i++) {
        printf("%d ", list.data[i]);
    }
    printf("\n当前长度:%d\n", list.length);
}

五、完整测试代码

把上面的代码组合起来,写一个简单的 main 函数测试一下:

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

#define MAXSIZE 100

typedef struct {
    int data[MAXSIZE];
    int length;
} SeqList;

void initList(SeqList *list) {
    list->length = 0;
}

int append(SeqList *list, int value) {
    if (list->length >= MAXSIZE) return 0;
    list->data[list->length++] = value;
    return 1;
}

int insert(SeqList *list, int pos, int value) {
    if (pos < 0 || pos > list->length || list->length >= MAXSIZE) return 0;
    for (int i = list->length; i > pos; i--) {
        list->data[i] = list->data[i - 1];
    }
    list->data[pos] = value;
    list->length++;
    return 1;
}

int delete(SeqList *list, int pos) {
    if (pos < 0 || pos >= list->length) return 0;
    for (int i = pos; i < list->length - 1; i++) {
        list->data[i] = list->data[i + 1];
    }
    list->length--;
    return 1;
}

void printList(SeqList list) {
    printf("顺序表:");
    for (int i = 0; i < list.length; i++) {
        printf("%d ", list.data[i]);
    }
    printf("(长度=%d)\n", list.length);
}

int main() {
    SeqList list;
    initList(&list);

    // 添加元素
    append(&list, 10);
    append(&list, 20);
    append(&list, 30);
    printList(list); // 输出:10 20 30

    // 在位置 1 插入 15
    insert(&list, 1, 15);
    printList(list); // 输出:10 15 20 30

    // 删除位置 2 的元素(即 20)
    delete(&list, 2);
    printList(list); // 输出:10 15 30

    return 0;
}

六、数组 vs 顺序表:一张图看懂

复制代码
内存中的存储方式(两者完全相同):

地址:  0x1000   0x1004   0x1008   0x100C   0x1010
       ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
       │  10 │ │  20 │ │  30 │ │  40 │ │     │
       └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
         ↑       ↑       ↑       ↑
       arr[0]  arr[1]  arr[2]  arr[3]

       这既是"数组",也是"顺序表"

关键区别在于视角:

  • 数组视角:关注内存中的一块连续区域,强调物理存储。
  • 顺序表视角:关注数据之间的逻辑关系(线性、有序),强调数据结构层面的抽象。

七、小结

要点 内容
核心概念 顺序表 = 用连续空间存储的线性表,数组是其最常见的实现
访问特性 随机访问 O(1),插入删除 O(n)
实现方式 静态数组(容量固定)/ 动态数组(容量可变,需用到指针和动态内存)
学习建议 先掌握静态实现,理解核心操作逻辑,后续再学习动态扩展

📌 下节预告:当我们学习了指针和动态内存管理后,就可以实现动态顺序表,让表的大小随需扩展,解决静态顺序表容量固定的问题。


希望这篇博客能帮助你理清数组和顺序表的关系!如果有任何疑问,欢迎随时交流。😊

相关推荐
扬大平仔2 小时前
# 小深:用 AgentScope Java 2.0 Harness 做私人助手(上)mysql
java·开发语言·mysql
蓝速科技2 小时前
口岸政务窗口双屏翻译机落地应用指南
运维·数据结构·数据库·人工智能·科技·政务
阿里云基础软件2 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·人工智能·操作系统·sysom 诊断 skill
Lokey8683 小时前
自注意力机制与多头注意力机制
算法
不会就选b3 小时前
数据结构之树&&二叉树<5>---二叉树OJ
数据结构
ctlover3 小时前
Python高级与正则表达式
开发语言·python
泡海椒3 小时前
生产环境安全配置:JQuickJavaInvocationGuard自定义防护规则实战
开发语言·python·安全
yume_sibai3 小时前
07-Rust 异步编程完全指南(async/await + Tokio + Future + 并发原语 + 异步流)
开发语言·后端·rust
linx2953 小时前
单元七 · 零基础路线图-第 1–3 周·变量、类型与字符串
c语言·开发语言·数据结构·c++·算法