跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

数据结构详解:顺序表原理与实现

顺序表是线性表的顺序存储结构,底层通常基于数组实现。详细讲解了顺序表的概念、分类及动态扩容机制,并通过 C++ 代码模拟了初始化、增删改查等核心接口的实现过程。同时对比了静态数组在竞赛中的应用以及 STL vector 容器的便捷用法,帮助读者深入理解顺序表的原理与实际应用。

全栈工匠发布于 2026/3/16更新于 2026/10/2595 浏览
数据结构详解:顺序表原理与实现

线性表

线性表(linear list)是 n 个具有相同特性的数据元素的有限序列。作为一种在实际中广泛使用的数据结构,常见的线性表包括顺序表、链表、栈、队列和字符串等。

从逻辑结构上看,线性表是连续的直线;但在物理存储上并不一定连续,通常以数组或链式结构形式存储。

顺序表

概念与结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组来实现。

  • 逻辑结构:线性
  • 物理结构:线性(连续存储)

顺序表和数组的区别

顺序表的底层结构是数组,它是对数组的封装,实现了常用的增删改查等接口。

数组 ⊆ 线性表

分类

静态顺序表

概念上使用定长数组存储元素。

静态顺序表示意图

静态顺序表的缺陷在于空间分配固定:给少了不够用,给多了造成浪费。不过在竞赛场景中,经常使用静态申请数组的方式。

动态顺序表

动态顺序表按需申请内存,不会造成空间浪费。

动态顺序表示意图

动态顺序表模拟实现

下面我们通过 C++ 代码来模拟一个动态顺序表的核心功能。

定义动态顺序表结构

首先定义数据类型和结构体:

// 头文件中
typedef int SLTDataType;

typedef struct SeqList {
    SLTDataType* arr; // 存储数据的指针
    int size;         // 有效数据个数
    int capacity;     // 当前空间大小
} SL;
顺序表初始化

初始化时,将指针置空,大小和容量设为 0。

void SLInit(SL* ps) {
    ps->arr = NULL;
    ps->size = ps->capacity = ;
}
0
顺序表销毁

销毁时需要释放动态分配的内存,并重置状态,避免重复释放。

void SLDestroy(SL* ps) {
    if (ps->arr) {
        free(ps->arr);
    }
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}
顺序表打印

遍历有效数据部分进行输出。

void SLPrint(SL* ps) {
    for (int i = 0; i < ps->size; i++) {
        printf("%d ", ps->arr[i]);
    }
    printf("\n");
}
顺序表动态扩容

当有效数据个数等于容量时,需要扩容。这里采用倍增策略,初始为 4。

void SLCheckCapacity(SL* ps) {
    if (ps->size == ps->capacity) {
        int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLTDataType* tmp = (SLTDataType*)realloc(ps->arr, newCapacity * sizeof(SLTDataType));
        if (tmp == NULL) {
            perror("realloc fail!");
            exit(1);
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}
尾插

插入前先检查空间是否足够,若不足则扩容,然后直接写入末尾。

void SLPushBack(SL* ps, SLTDataType x) {
    assert(ps);
    SLCheckCapacity(ps);
    ps->arr[ps->size++] = x;
}
头插

头插需要将现有数据整体向后移动一位,腾出位置给新元素。

void SLPushFront(SL* ps, SLTDataType x) {
    assert(ps != NULL);
    SLCheckCapacity(ps);
    // 数据整体向后挪动一位
    for (int i = ps->size; i > 0; i--) {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[0] = x;
    ps->size++;
}
尾删

只需减少有效数据个数即可。

void SLPopBack(SL* ps) {
    assert(ps && ps->size);
    ps->size--;
}
头删

头删需要将后续数据整体向前移动一位。

void SLPopFront(SL* ps) {
    assert(ps && ps->size);
    // 数据整体向前挪动一位
    for (int i = 0; i < ps->size - 1; i++) {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}
查找

线性遍历查找目标值,返回下标,未找到返回 -1。

int SLFind(SL* ps, SLTDataType x) {
    assert(ps);
    for (int i = 0; i < ps->size; i++) {
        if (ps->arr[i] == x) {
            return i;
        }
    }
    return -1;
}
指定位置之前插入

在 pos 位置插入前,需将 pos 及之后的数据后移。

void SLInsert(SL* ps, int pos, SLTDataType x) {
    assert(ps);
    assert(pos >= 0 && pos < ps->size);
    SLCheckCapacity(ps);
    // pos 及之后数据向后挪动一位
    for (int i = ps->size; i > pos; i--) {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[pos] = x;
    ps->size++;
}
删除 pos 位置的数据

删除后需将 pos 后面的数据前移覆盖。

void SLErase(SL* ps, int pos) {
    assert(ps);
    assert(pos >= 0 && pos < ps->size);
    // pos 后面的数据向前挪动一位
    for (int i = pos; i < ps->size - 1; i++) {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

竞赛中的静态顺序表

在算法竞赛中,为了效率往往直接使用静态数组模拟顺序表。

静态申请数组

单个顺序表可以通过全局大数组实现:

#include<iostream>
using namespace std;
const int N = 1e6 + 10;

int a[N]; // 创建顺序表
int n;    // 标记顺序表里面有多少个元素

// 打印顺序表
void print() {
    for (int i = 1; i <= n; i++) {
        cout << a[i] << " ";
    }
    cout << endl << endl;
}

// 尾插
void push_back(int x) {
    a[++n] = x;
}

// 头插
void push_front(int x) {
    for (int i = n; i >= 1; i--) {
        a[i + 1] = a[i];
    }
    a[1] = x;
    n++;
}

// 任意位置插入
void insert(int p, int x) {
    for (int i = n; i >= p; i--) {
        a[i + 1] = a[i];
    }
    a[p] = x;
    n++;
}

// 尾删
void pop_back() {
    n--;
}

// 头删
void pop_front() {
    for (int i = 2; i <= n; i++) {
        a[i - 1] = a[i];
    }
    n--;
}

// 任意位置删除
void erase(int p) {
    for (int i = p + 1; i <= n; i++) {
        a[i - 1] = a[i];
    }
    n--;
}

// 按值查找
int find(int x) {
    for (int i = 1; i <= n; i++) {
        if (a[i] == x) return i;
    }
    return 0;
}

// 按位查找
int at(int p) {
    return a[p];
}

// 按位修改
void change(int p, int x) {
    a[p] = x;
}

// 清空操作
void clear() {
    n = 0;
}

如果需要多个顺序表,可以传入数组引用:

int a1[N], n1;
int a2[N], n2;
int a3[N], n3;

void push_back(int a[], int& n, int x) {
    a[++n] = x;
}

封装静态顺序表

通过类或结构体封装,可以像调用 STL 接口一样使用:

class SqList {
    int a[N];
    int n;
public:
    SqList() { n = 0; }
    void push_back(int x) { a[++n] = x; }
    void pop_back() { n--; }
    void print() {
        for (int i = 1; i <= n; i++) {
            cout << a[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    SqList s1, s2;
    for (int i = 1; i <= 5; i++) {
        s1.push_back(i);
        s2.push_back(i * 2);
    }
    s1.print();
    s2.print();
    return 0;
}

动态顺序表 -- vector

实际开发中,C++ 的 STL 提供了 vector 容器,它是已经封装好的可变长数组,底层就是自动扩容的顺序表。

创建 vector

// 四种常用创建方式
vector<int> a1;                  // 空的可变长数组
vector<int> a2(N);               // 大小为 N,默认值为 0
vector<int> a3(N, 2);            // 大小为 N,初始化为 2
vector<int> a4 = {1, 2, 3, 4, 5}; // 初始化列表

// 支持模板,可存任意类型
vector<string> a5;               // 存字符串
vector<node> a6;                 // 存结构体
vector<vector<int>> a7;          // 二维可变长数组

size / empty

// size 返回实际元素个数
// empty 返回是否为空
if (a2.empty()) cout << "空" << endl;
else cout << "不空" << endl;

begin / end

利用迭代器访问整个 vector,支持范围 for 遍历。

void test_it() {
    vector<int> a(10, 1);
    // 迭代器遍历
    for (auto it = a.begin(); it != a.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    // 范围 for 遍历
    for (auto x : a) {
        cout << x << " ";
    }
    cout << endl;
}

push_back / pop_back

for (int i = 0; i < 5; i++) {
    a1.push_back(i);
}
while (!a1.empty()) {
    a1.pop_back();
}

front / back

void test_fb() {
    vector<int> a(5);
    for (int i = 0; i < 5; i++) {
        a[i] = i + 1;
    }
    cout << a.front() << " " << a.back() << endl;
}

resize

调整大小,超出部分补默认值,缩小时截断尾部。

void test_resize() {
    vector<int> a(5, 1);
    a.resize(10); // 扩大
    a.resize(3);  // 缩小
    a.resize(5, 2); // 扩大并填充 2
}

clear

清空所有元素。

a.clear();

insert / erase

参数为迭代器。

a4.insert(a4.begin() + 2, 0);
a4.erase(a4.begin() + 2);

目录

  1. 线性表
  2. 顺序表
  3. 概念与结构
  4. 顺序表和数组的区别
  5. 分类
  6. 静态顺序表
  7. 动态顺序表
  8. 动态顺序表模拟实现
  9. 定义动态顺序表结构
  10. 顺序表初始化
  11. 顺序表销毁
  12. 顺序表打印
  13. 顺序表动态扩容
  14. 尾插
  15. 头插
  16. 尾删
  17. 头删
  18. 查找
  19. 指定位置之前插入
  20. 删除 pos 位置的数据
  21. 竞赛中的静态顺序表
  22. 静态申请数组
  23. 封装静态顺序表
  24. 动态顺序表 -- vector
  25. 创建 vector
  26. size / empty
  27. begin / end
  28. pushback / popback
  29. front / back
  30. resize
  31. clear
  32. insert / erase

更多推荐文章

查看全部
  • DeepSeek-R1 大模型基于 MS-Swift 框架的部署、推理与微调实践
  • Spring 参数校验 Validation
  • Python 使用 json-repair 修复大模型返回的非法 JSON
  • 数据结构:队列核心原理与 C 语言实战
  • OpenClaw Gateway 常见问题与配置指南
  • GitNexus 纯本地代码知识图谱与可视化分析方案
  • Python+AI 学习路线:从零基础到实战专家
  • llama.cpp 多 GPU 分布式计算优化实践
  • CTF 逆向:RC4、TEA 与 Base64 加密算法特征识别与魔改分析
  • 生信零基础到独立项目:3 个月模块化学习计划
  • MiniMax 海螺 AI:图片与文本生成高质量视频指南
  • Python 字符串格式化:% 运算符详解
  • 《Agent Runtime 工程化》第一章 Agent Runtime 全景:导读
  • LazyLLM 多 Agent 应用实战:源码部署与可视化调试指南
  • 数据结构:双向链表详解与实现
  • GLM-4.5-Air-Base 开源:1060 亿参数智能推理模型免费商用
  • 进阶数据结构:AVL 树
  • llama.cpp 多 GPU 分布式计算优化指南
  • 基于 Java SpringBoot 的企业设备信息一体化管理系统
  • Spring Boot 整合 WebService 客户端:基于 WSDL 自动生成代码

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online