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

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

综述由AI生成顺序表是线性表的顺序存储结构,底层基于连续内存。对比了静态与动态顺序表的区别,详细讲解了动态顺序表的模拟实现过程,涵盖初始化、销毁、扩容策略及增删查改操作的核心逻辑。同时介绍了竞赛中常用的静态数组方案,并对比了 C++ STL 中 vector 容器的便捷用法,帮助读者理解从底层内存管理到上层封装的完整知识体系。

萤火微光发布于 2026/3/24更新于 2026/5/98 浏览
数据结构:顺序表的原理与实现

数据结构:顺序表

线性表基础

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

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

顺序表概述

概念与结构

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

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

顺序表与数组的区别

顺序表的底层结构是数组,可以看作是对数组的封装。它在数组的基础上实现了常用的增删改查接口,提供了更高级的操作抽象。

关系上:数组 ⊆ 线性表。

分类

静态顺序表

使用定长数组存储元素。

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

动态顺序表

按需申请内存,不会造成空间浪费,灵活性更高。

动态顺序表模拟实现

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

1. 定义结构体

在头文件中定义顺序表的结构,包含数据指针、有效元素个数和当前容量。

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

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

2. 初始化

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

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

3. 销毁

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

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

4. 打印功能

遍历有效数据范围输出,方便调试。

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

5. 动态扩容

这是顺序表的核心机制。当 size == capacity 时,需要重新分配更大的内存块。

void SLCheckCapacity(SL* ps) {
    if (ps->size == ps->capacity) {
        // 初始为 4,否则翻倍
        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;
    }
}

6. 插入操作

尾插

先检查空间,再放入数据,最后更新 size。

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

7. 删除操作

尾删

直接减少有效计数即可。

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--;
}
指定位置删除
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--;
}

8. 查找

线性遍历,找到返回下标,未找到返回 -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;
}

竞赛中的静态顺序表

在算法竞赛中,为了追求极致性能,往往直接使用全局静态数组模拟顺序表。

静态申请数组

#include <iostream>
using namespace std;

const int N = 1e6 + 10; // 根据题目规模设定

int a[N]; // 模拟数组
int n;    // 当前元素个数

// 尾插
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 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;
    }
};

动态顺序表——vector

如果不需要手动管理内存,C++ STL 提供了 vector 容器。它的底层就是一个会自动扩容的顺序表,封装了所有常用接口。

创建方式

vector<int> a1;              // 空向量
vector<int> a2(N);           // 大小为 N,默认值 0
vector<int> a3(N, 2);        // 大小为 N,初始化为 2
vector<int> a4 = {1, 2, 3};  // 初始化列表

常用接口

  • size / empty: 获取元素个数或判断是否为空。
  • push_back / pop_back: 尾部插入和删除。
  • front / back: 访问首尾元素。
  • resize: 调整大小,超出部分补默认值,不足则截断。
  • clear: 清空内容。
  • insert / erase: 支持迭代器位置的插入和删除。

迭代器与遍历

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

在实际开发中,除非是为了学习底层原理或特定场景优化,否则优先使用 vector,既安全又高效。

目录

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

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

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

更多推荐文章

查看全部
  • HarmonyOS RcList 组件缩略图、角标与图标系统设计
  • 2026 年前端跨端框架选型指南:Flutter、RN 与 uni-app 深度对比
  • HarmonyOS6 RcInput 组件核心架构与类型系统设计
  • 家庭用电数据分析与 Prophet 预测
  • 构建 AI 鸿蒙应用:架构逻辑的转变
  • llama.cpp 量化模型部署实战:从模型转换到 API 服务
  • 三年前端转 CS 硕士:韩国留学复盘与回归前端
  • LFU 缓存算法详解:双哈希 + 双向链表实现 O(1) 操作
  • 个人电脑运行 Llama 3 70B 大模型部署指南
  • Python 临床知识问答与检索项目架构设计与实现
  • SpringBoot 整合 Spring Data JDBC
  • FPGA 开发入门:Vivado 下载与烧录流程详解
  • 基于 Spring Boot 和 WebSocket 的实时聊天室系统实现
  • Dify 集成 MySQL 实战:基于 MCP 协议的数据查询方案
  • AMD 显卡笔记本 Windows 11 运行 Stable Diffusion 指南
  • Shell 数组基础用法与注意事项
  • 前端地图开发基础:服务类型、坐标系与 SDK 选型指南
  • Web3 前端安全:钱包连接风险与防护指南
  • VS Code 远程连接服务器时 GitHub Copilot 提示未授权的解决方法
  • VS Code 关闭 Copilot 代码自动补全

相关免费在线工具

  • 加密/解密文本

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