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

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

综述由AI生成顺序表是一种使用连续存储单元存放数据的线性结构,分为静态定长和动态扩容两种形式。详细讲解了顺序表的底层原理,包括手动实现动态顺序表的结构定义、初始化、扩容策略及增删改查操作,同时对比了竞赛中静态数组的应用场景。此外,还介绍了 C++ STL 中 vector 容器的常用接口,如迭代器遍历、resize 扩容及 insert/erase 操作,帮助读者从底层实现到上层应用全面掌握顺序表技术。

王初壹发布于 2026/3/28更新于 2026/6/1019 浏览
数据结构:顺序表原理与实现

线性表

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

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

顺序表

概念与结构

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

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

顺序表和数组的区别

顺序表的底层结构是数组,可以看作是对数组的封装,在此基础上实现了常用的增删改查接口。

关系:数组 ⊆ 线性表

分类

静态顺序表

使用定长数组存储元素。

顺序表示意图

静态顺序表的缺陷比较明显:空间给少了不够用,给多了又造成浪费。不过在竞赛场景中,为了效率经常直接使用静态申请数组的方式。

动态顺序表

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

动态顺序表示意图

动态顺序表模拟实现

下面我们来手动实现一个动态顺序表,理解其内部机制。

定义动态顺序表结构

在头文件中定义类型和结构体:

// 定义数据类型
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);
    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 位置插入前,需将该位置及之后的数据后移。

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;
}
多个顺序表

如果需要管理多个顺序表,可以将数组作为引用参数传入。

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

// 测试
void test() {
    int a1[N], n1 = 0;
    int a2[N], n2 = 0;
    push_back(a1, n1, 1);
    push_back(a2, n2, 2);
}

封装静态顺序表

通过类或结构体封装,可以像调用 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;
}

这种封装方式让我们无需重复造轮子,就像使用 C++ STL 的 vector 一样方便。

动态顺序表 – 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;         // 二维可变长数组

// 数组与 Vector 混合
int a10[N];                     // C 风格数组
vector<int> a9[N];              // Vector 数组
size / empty
// size 返回实际元素个数
// empty 判断是否为空,返回 bool
if (a2.empty()) cout << "空" << endl;
else cout << "不空" << endl;
begin / end

迭代器指向起始位置(左闭),end 指向终点下一个位置(右开)。支持范围 for 遍历。

void test_it() {
    vector<int> a(10, 1);
    
    // 传统迭代器遍历
    for (auto it = a.begin(); it != a.end(); it++) {
        cout << *it << " ";
    }
    cout << endl << endl;
    
    // 范围 for 遍历
    for (auto x : a) {
        cout << x << " ";
    }
    cout << endl << endl;
}
push_back / pop_back
for (int i = 0; i < 5; i++) {
    a1.push_back(i);
    print(a1);
}
while (!a1.empty()) {
    a1.pop_back();
    print(a1);
}
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

调整大小。大于原大小时补默认值(通常为 0),小于原大小时截断尾部元素。

void print(vector<int>& a) {
    for (auto x : a) {
        cout << x << " ";
    }
    cout << endl;
}

void test_resize() {
    vector<int> a(5, 1);
    a.resize(10);       // 扩大,补 0
    print(a);
    a.resize(3);        // 缩小
    print(a);
    a.resize(5, 2);     // 扩大成 5,多余部分填 2
    print(a);
}
clear

清空容器,时间复杂度 O(N)。

cout << a.size() << endl;
a.clear();
cout << a.size() << endl;
insert / erase

基于迭代器进行插入和删除操作。

// 在 begin+2 处插入 0
a4.insert(a4.begin() + 2, 0);
print(a4);

// 删除 begin+2 处的元素
a4.erase(a4.begin() + 2);
print(a4);

总结

顺序表的核心在于连续内存存储带来的随机访问优势,以及动态扩容时的内存管理策略。手动实现有助于深入理解底层机制,而在实际开发中,合理使用 STL 的 vector 能显著提升开发效率和安全性。

目录

  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. 多个顺序表
  25. 封装静态顺序表
  26. 动态顺序表 – vector
  27. 创建 vector
  28. size / empty
  29. begin / end
  30. pushback / popback
  31. front / back
  32. resize
  33. clear
  34. insert / erase
  35. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 基于大数据的校园美食推荐系统设计
  • Ubuntu 系统更新完整指南
  • 医疗 AI 场景下的 k-均值算法:患者分群与精准医疗实战
  • Python 安装 OpenCV(cv2)的正确方法及常见问题解决
  • Docker Registry 私有仓库搭建实战 (v1/v2 详解)
  • 基于 Llama-Factory 微调中文小说续写模型实践
  • OpenClaw 多 Agent 路由:Gateway 托管多个 AI 大脑
  • MyBatis Dynamic SQL 核心优势与价值解析
  • Python EXE 解包工具使用指南与逆向工程解析
  • C++ 多态:从概念到虚函数表底层原理
  • 飞算 JavaAI 插件实战:从安装到工程代码生成
  • 飞算 Java AI 编程助手功能介绍与实践
  • Python AI 语音交互实战:从零构建高可用语音助手
  • Java 泛型:编译期类型安全与实战应用
  • JavaScript 内存管理机制与变量作用域优化
  • faster-whisper 快速安装与使用指南:AI 语音识别
  • Photoshop AI 插件实现 ComfyUI 与 Stable Diffusion 深度整合
  • 读李宁《AIGC 自动化编程》:大模型时代的复杂问题分解与合并
  • Motrix WebExtension 浏览器扩展配置指南
  • OpenClaw 30+ 真实使用案例开源,参考 AI 助理落地方案

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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