跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

数据结构详解:顺序表

综述由AI生成顺序表是线性表的顺序存储结构,底层通常基于数组实现。详细讲解了顺序表的概念、静态与动态分类,并通过 C/C++ 代码模拟了动态顺序表的完整实现,涵盖初始化、扩容、增删改查等核心逻辑。同时对比了竞赛中常用的静态数组优化方案,并介绍了 C++ STL 中 vector 容器的使用方法及其底层原理。重点在于理解内存分配机制及不同操作的时间复杂度差异。

忘忧发布于 2026/3/21更新于 2026/5/45 浏览
数据结构详解:顺序表

数据结构详解:顺序表

线性表概述

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

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

顺序表基础

概念与结构

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

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

顺序表与数组的区别

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

注意:数组是线性表的一种实现方式,但线性表的概念更抽象。

分类

静态顺序表

使用定长数组存储元素。

静态顺序表示意图

缺陷:空间给少了不够用,给多了造成浪费。不过在竞赛场景中,经常直接使用静态申请数组的方式,后续会提到。

动态顺序表

按需申请内存,不会造成空间浪费。

动态顺序表示意图

动态顺序表模拟实现

下面我们通过 C/C++ 代码来手动实现一个动态顺序表,理解其背后的内存管理逻辑。

定义结构体

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

// 定义数据类型
typedef int SLTDataType;

// 定义顺序表结构
typedef struct SeqList {
    SLTDataType* arr;   // 存储数据的指针
    int size;           // 有效数据个数
    int capacity;       // 当前容量大小
} SL;

初始化

初始化时,将指针置空,计数归零。

void SLInit(SL* ps) {
    ps->arr = NULL;
    ps->size = 0;
    ps->capacity = 0;
}

销毁

释放动态分配的内存,并重置状态,避免野指针。

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

打印

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

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

动态扩容

当 size 等于 capacity 时,需要扩容。通常策略是初始为 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;
}

头插需要将现有数据整体后移一位,效率较低(O(N))。

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++;
}

删除指定位置数据逻辑类似,将后续数据前移。

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 clear() {
    n = 0;
}

如果需要多个顺序表,只需传入数组引用即可复用逻辑。

封装静态顺序表

利用类或结构体封装,提高代码复用性。

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

实际开发中,C++ 标准库提供了 vector 容器,它本质上就是一个自动扩容的动态顺序表。

创建方式

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

// 支持模板,可存任何类型
vector<string> a5;
vector<vector<int>> a7;      // 二维数组

基本操作

  • size / empty:获取元素个数或判断是否为空。
  • begin / end:获取迭代器,支持范围 for 遍历。
  • push_back / pop_back:尾部插入和删除。
  • front / back:访问首尾元素。
  • resize:调整大小,超出部分补默认值,不足则截断。
  • clear:清空内容。
  • insert / erase:支持迭代器位置的插入和删除。
void test_resize() {
    vector<int> a(5, 1);
    a.resize(10); // 扩大,多出的补 0
    a.resize(3);  // 缩小,截断多余元素
    a.resize(5, 2); // 扩大并指定新元素值
}

void test_iter() {
    vector<int> a(10, 1);
    // 范围 for 遍历
    for (auto x : a) {
        cout << x << " ";
    }
}

提示:vector 的底层实现已经非常成熟,日常开发建议优先使用,除非有特殊的内存控制需求。

目录

  1. 数据结构详解:顺序表
  2. 线性表概述
  3. 顺序表基础
  4. 概念与结构
  5. 顺序表与数组的区别
  6. 分类
  7. 静态顺序表
  8. 动态顺序表
  9. 动态顺序表模拟实现
  10. 定义结构体
  11. 初始化
  12. 销毁
  13. 打印
  14. 动态扩容
  15. 尾插与头插
  16. 删除操作
  17. 查找与插入
  18. 竞赛中的静态顺序表
  19. 静态申请数组
  20. 封装静态顺序表
  21. C++ STL 中的 vector
  22. 创建方式
  23. 基本操作
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • LeetCode 面试经典 150 题回顾
  • Flutter 基于 web3dart 连接以太坊构建 DApp 及 OpenHarmony 适配
  • C++ 数据结构:哈希表原理及 STL 实现
  • AI 时代初级开发者如何保持创意优势:从代码写手到情境大师
  • C++ 类与对象进阶:构造函数、静态成员与友元详解
  • 用老 Mac 跑本地 AI:OpenClaw 环境一键搭建
  • 数据结构之带头双向循环链表
  • GraphRAG-UI:本地版 GraphRAG 用户界面与实现指南
  • Git 原理与进阶使用:远程协作、分支管理及企业实践
  • RIDE 屏蔽 INFO 级别日志输出
  • 支付宝 SDK 集成:深入理解两种支付回调区别
  • RPA 机器人流程自动化技术解析与应用
  • Python 使用 Plottable 库绘制精美数据表格
  • Python 零基础入门:环境安装与第一个程序
  • Wildcard AI 服务评测:低价背后的模型真相
  • GitNexus 核心引擎深度解析
  • 算法进阶:前缀和原理与实战应用
  • 飞算 JavaAI 插件体验:从需求分析到项目构建的高效开发实践
  • OpenClaw 新手指南:AI 机器人搭建与配置
  • ASP.NET 4.7 微服务化实践:Windows Docker 环境搭建

相关免费在线工具

  • 加密/解密文本

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