当前位置: 首页 > news >正文

实现一个动态顺序表(C++)

** 引言:基于C++语法的学习,同时加强对于数据结构的理解,后续选择用C++实现基础的数据结构。欲实现的目标:对C++的类和模板的特性进一步理解,同时思考对应stl容器基础实现。 **

简单实现数据结构–顺序表

一、 什么是顺序表?

顺序表是线性表的一种存储结构。它的核心思想是:用一段地址连续的存储单元(通常是数组),依次存储线性表中的数据元素。你可以把它想象成一排连续的房间(存储单元),每个房间的门牌号(地址)是连续的。每个房间里住着一个数据元素,并且这些元素是按照顺序(第一个、第二个…第n个)依次入住这些房间的。

在这里插入图片描述

** 核心特征:逻辑上相邻的元素,在物理存储位置上也相邻。**

  • 逻辑结构:线性关系。(a₁, a₂, a₃, …, aₙ)
  • 物理结构:存储在一片连续的内存空间中。

二、 顺序表的基本实现

顺序表通常由两部分组成:

存储数据的数组 (data[]):用于存放实际的数据元素。
记录当前长度的变量 (length或size):用于记录当前顺序表中实际有多少个元素。

下面为具体的代码实现:

SeqList.h

#pragma once
#include<iostream>
#include<stdexcept>template <typename T>
class SeqList
{
public://构造函数   -->> 初始化类对象SeqList(int N = 10) :_size(0), _capacity(N) {//检查if (N < 0){throw std::invalid_argument("初始容量不能为负数");}_a = (N > 0) ? new T[N] : nullptr;}//拷贝构造函数  -->>1.只有单个形参// 2.参数为类类型的const引用,不能使用值传递,会引发无限调用// 3.已存在的类类型对象创建新对象时由编译器自动调用SeqList(const SeqList& other) :_size(other._size), _capacity(other._capacity) {_a = new T[_capacity];for (int i = 0; i < _size; i++){_a[i] = other._a[i];}}//赋值运算符重载 -->>1.通常接受同类对象的引用(常为const引用),但也可以接受其他类型(如转换构造函数的参数类型)//2.返回值类型:必须返回当前对象的引用(ClassName& ),以支持链式赋值(如a = b = c)//3.检测是否自己给自己赋值//4.返回 * this :要复合连续赋值的含义SeqList& operator=(const SeqList& other) {if (this != &other) {delete[] _a;_size = other._size;_capacity = other._capacity;_a = new T[_capacity];for (int i = 0; i < _size; ++i) {_a[i] = other._a[i];}}return *this;}//析构函数~SeqList() {delete[] _a;_a = nullptr;_size = 0;_capacity = 0;}//顺序表的基本操作--增删查改//1.在指定位置插入元素void insert(int pos, const T& x) {if (pos < 0 || pos>_size) {throw std::out_of_range("插入位置越界");}if (_size == _capacity) {resize();}//移动元素,腾出位置for (int i = _size; i > pos; --i) {_a[i] = _a[i - 1];}_a[pos] = x;_size++;}// 在末尾添加元素void push_back(const T& x) {if (_size == _capacity) {resize();}_a[_size++] = x;}// 在头部添加元素void push_front(const T&  x) {insert(0, x);}//2.在指定位置删除元素void erase(int pos) {if (pos < 0 || pos>_size) {throw std::out_of_range("插入位置越界");}for (int i = pos; i < _size - 1; ++i) {_a[i] = _a[i +1];}_size--;}// 在末尾删除元素void pop_back() {if (_size == 0) {throw std::runtime_error("顺序表为空,无法删除元素");}_size--;}// 在头部删除元素void pop_front() {erase(0);}//3. 查找元素位置int find(const T& x) const {for (int i = 0; i < _size; ++i) {if (_a[i] == x) {return i;}}return -1;}// 获取指定位置元素T get(int pos) const {if (pos < 0 || pos >= _size) {throw std::out_of_range("访问位置越界");}return _a[pos];}// 修改指定位置元素void set(int pos, const T& x) {if (pos < 0 || pos >= _size) {throw std::out_of_range("修改位置越界");}_a[pos] = x;}// 判断是否包含元素bool contains(const T& x) const {return find(x) != -1;}// 清空顺序表void clear() {_size = 0;}// 获取当前元素数量int size() const {return _size;}// 获取容量int getCapacity() const {return _capacity;}// 判断是否为空bool isEmpty() const {return _size == 0;}// 打印顺序表内容void print() const {std::cout << "[";for (int i = 0; i < _size; ++i) {std::cout <<_a[i];if (i != _size - 1) {std::cout << ", ";}}std::cout << "]" << std::endl;}private:T* _a; //存储动态数据的数组int _size;//当前元素数量int _capacity;//容量//扩容函数void resize() {int new_capacity = (_capacity == 0)?1:_capacity * 2;T* new_a = new T[new_capacity];//复制原有函数for (int i = 0; i < _size; i++) {new_a[i] = _a[i];}//释放旧内存,并更新新指针delete[] _a;_a = new_a;_capacity = new_capacity;}
};

test .h

#include"SeqList.h"#include <iostream>
#include <string>// 测试构造函数和基本属性
void testConstructorAndProperties() {std::cout << "=== 测试构造函数和基本属性 ===" << std::endl;// 测试默认构造函数SeqList<int> list1;std::cout << "默认构造函数 - 初始大小: " << list1.size() << " (预期: 0)" << std::endl;std::cout << "默认构造函数 - 初始容量: " << list1.getCapacity() << " (预期: 10)" << std::endl;std::cout << "默认构造函数 - 是否为空: " << (list1.isEmpty() ? "是" : "否") << " (预期: 是)" << std::endl;// 测试指定容量的构造函数SeqList<std::string> list2(5);std::cout << "\n指定容量构造函数 - 初始容量: " << list2.getCapacity() << " (预期: 5)" << std::endl;// 测试异常构造(负容量)try {SeqList<double> list3(-3);std::cout << "负容量构造未抛出异常(错误)" << std::endl;}catch (const std::invalid_argument& e) {std::cout << "负容量构造捕获预期异常: " << e.what() << " (正确)" << std::endl;}std::cout << "=== 构造函数测试结束 ===\n" << std::endl;
}// 测试插入功能
void testInsertOperations() {std::cout << "=== 测试插入功能 ===" << std::endl;SeqList<int> list;// 测试尾插list.push_back(10);list.push_back(20);list.push_back(30);std::cout << "尾插后: ";list.print();  // 预期: [10, 20, 30]// 测试头插list.push_front(5);list.push_front(0);std::cout << "头插后: ";list.print();  // 预期: [0, 5, 10, 20, 30]// 测试指定位置插入list.insert(3, 15);std::cout << "指定位置插入后: ";list.print();  // 预期: [0, 5, 10, 15, 20, 30]// 测试扩容(超过初始容量10)for (int i = 40; i <= 100; i += 10) {list.push_back(i);}std::cout << "扩容后: ";list.print();  // 预期包含11个元素std::cout << "扩容后容量: " << list.getCapacity() << " (预期: 20)" << std::endl;// 测试插入异常(越界)try {list.insert(-1, 99);std::cout << "负索引插入未抛出异常(错误)" << std::endl;}catch (const std::out_of_range& e) {std::cout << "负索引插入捕获预期异常: " << e.what() << " (正确)" << std::endl;}try {list.insert(20, 99);  // 当前大小为11,插入索引20越界std::cout << "超范围插入未抛出异常(错误)" << std::endl;}catch (const std::out_of_range& e) {std::cout << "超范围插入捕获预期异常: " << e.what() << " (正确)" << std::endl;}std::cout << "=== 插入测试结束 ===\n" << std::endl;
}// 测试删除功能
void testDeleteOperations() {std::cout << "=== 测试删除功能 ===" << std::endl;SeqList<int> list;for (int i = 1; i <= 5; ++i) {list.push_back(i * 10);}std::cout << "初始列表: ";list.print();  // 预期: [10, 20, 30, 40, 50]// 测试尾删list.pop_back();std::cout << "尾删后: ";list.print();  // 预期: [10, 20, 30, 40]// 测试头删list.pop_front();std::cout << "头删后: ";list.print();  // 预期: [20, 30, 40]// 测试指定位置删除list.erase(2);list.print();  // 预期: [20, 40]// 测试空表删除异常SeqList<int> emptyList;try {emptyList.pop_back();std::cout << "空表尾删未抛出异常(错误)" << std::endl;}catch (const std::runtime_error& e) {std::cout << "空表尾删捕获预期异常: " << e.what() << " (正确)" << std::endl;}try {emptyList.erase(0);std::cout << "空表指定删除未抛出异常(错误)" << std::endl;}catch (const std::out_of_range& e) {std::cout << "空表指定删除捕获预期异常: " << e.what() << " (正确)" << std::endl;}std::cout << "=== 删除测试结束 ===\n" << std::endl;
}// 测试查找和修改功能
void testFindAndModify() {std::cout << "=== 测试查找和修改功能 ===" << std::endl;SeqList<std::string> list;list.push_back("Apple");list.push_back("Banana");list.push_back("Cherry");list.push_back("Date");std::cout << "初始列表: ";list.print();  // 预期: [Apple, Banana, Cherry, Date]// 测试查找功能int index = list.find("Cherry");std::cout << "Cherry的索引: " << index << " (预期: 2)" << std::endl;index = list.find("Grape");std::cout << "Grape的索引: " << index << " (预期: -1)" << std::endl;// 测试包含功能bool hasBanana = list.contains("Banana");std::cout << "是否包含Banana: " << (hasBanana ? "是" : "否") << " (预期: 是)" << std::endl;bool hasGrape = list.contains("Grape");std::cout << "是否包含Grape: " << (hasGrape ? "是" : "否") << " (预期: 否)" << std::endl;// 测试修改功能list.set(1, "Blueberry");std::cout << "修改后列表: ";list.print();  // 预期: [Apple, Blueberry, Cherry, Date]// 测试修改异常(越界)try {list.set(10, "Mango");std::cout << "越界修改未抛出异常(错误)" << std::endl;}catch (const std::out_of_range& e) {std::cout << "越界修改捕获预期异常: " << e.what() << " (正确)" << std::endl;}std::cout << "=== 查找和修改测试结束 ===\n" << std::endl;
}// 测试拷贝构造和赋值运算
void testCopyAndAssignment() {std::cout << "=== 测试拷贝构造和赋值运算 ===" << std::endl;// 准备原始列表SeqList<int> original;for (int i = 1; i <= 3; ++i) {original.push_back(i * 100);}std::cout << "原始列表: ";original.print();  // 预期: [100, 200, 300]// 测试拷贝构造SeqList<int> copyConstructed(original);std::cout << "拷贝构造的列表: ";copyConstructed.print();  // 预期: [100, 200, 300]// 修改拷贝的列表,验证原始列表不受影响copyConstructed.set(0, 999);std::cout << "修改后拷贝列表: ";copyConstructed.print();  // 预期: [999, 200, 300]std::cout << "原始列表是否变化: ";original.print();  // 预期: [100, 200, 300] (不变)// 测试赋值运算SeqList<int> assigned;assigned = original;std::cout << "赋值后的列表: ";assigned.print();  // 预期: [100, 200, 300]// 修改赋值的列表,验证原始列表不受影响assigned.push_back(400);std::cout << "修改后赋值列表: ";assigned.print();  // 预期: [100, 200, 300, 400]std::cout << "原始列表是否变化: ";original.print();  // 预期: [100, 200, 300] (不变)std::cout << "=== 拷贝和赋值测试结束 ===\n" << std::endl;
}// 测试清空和其他功能
void testClearAndOthers() {std::cout << "=== 测试清空和其他功能 ===" << std::endl;SeqList<double> list;list.push_back(3.14);list.push_back(2.718);list.push_back(1.618);std::cout << "初始列表: ";list.print();  // 预期: [3.14, 2.718, 1.618]// 测试获取元素double val = list.get(1);std::cout << "索引1的元素: " << val << " (预期: 2.718)" << std::endl;// 测试清空list.clear();std::cout << "清空后列表: ";list.print();  // 预期: []std::cout << "清空后大小: " << list.size() << " (预期: 0)" << std::endl;std::cout << "清空后是否为空: " << (list.isEmpty() ? "是" : "否") << " (预期: 是)" << std::endl;// 测试获取元素异常(越界)try {list.get(0);std::cout << "空表获取元素未抛出异常(错误)" << std::endl;}catch (const std::out_of_range& e) {std::cout << "空表获取元素捕获预期异常: " << e.what() << " (正确)" << std::endl;}std::cout << "=== 清空和其他测试结束 ===\n" << std::endl;
}int main() {// 执行所有测试testConstructorAndProperties();testInsertOperations();testDeleteOperations();testFindAndModify();testCopyAndAssignment();testClearAndOthers();std::cout << "所有测试执行完毕!" << std::endl;return 0;
}

这个顺序表类具有以下特点:

  • 模板设计:支持任意数据类型(如 int、float、string 等)
    *动态扩容:当元素数量达到容量上限时,自动将容量翻倍
    *完整操作集:
    1. 插入操作:头部插入、尾部插入、指定位置插入
    2. 删除操作:头部删除、尾部删除、指定位置删除
    3. 查找操作:按值查找位置、判断是否包含某元素
    4. 修改操作:修改指定位置元素
    5. 辅助操作:清空、获取大小、判断是否为空等
  • 安全机制:
    1. 对越界访问等非法操作抛出异常
    2. 实现拷贝构造函数和赋值运算符,避免浅拷贝问题
    3. 析构函数正确释放动态分配的内存
      提供了完整的测试用例,验证了整数和字符串两种类型的使用(也可以自行测试)

文章转载自:

http://8kOWljVI.tgrrb.cn
http://4Mxhnppo.tgrrb.cn
http://DgsgxGnV.tgrrb.cn
http://6N2iXiWS.tgrrb.cn
http://IcyQxYTx.tgrrb.cn
http://ZzaObfcf.tgrrb.cn
http://hO3UjrAi.tgrrb.cn
http://uTyGhd9n.tgrrb.cn
http://CH1vugur.tgrrb.cn
http://GTcJlSLE.tgrrb.cn
http://u03kFggN.tgrrb.cn
http://f3RdnLhu.tgrrb.cn
http://HflonNHj.tgrrb.cn
http://G2wsUSKi.tgrrb.cn
http://AEinBeyR.tgrrb.cn
http://24YSrX2y.tgrrb.cn
http://Lfe58Rlm.tgrrb.cn
http://YOZFmuYn.tgrrb.cn
http://H3MGhybl.tgrrb.cn
http://GEGTaeJb.tgrrb.cn
http://uBgtj1sk.tgrrb.cn
http://gs1FMZfn.tgrrb.cn
http://UdcYsc9O.tgrrb.cn
http://ovsQwWiV.tgrrb.cn
http://KGfCtyPD.tgrrb.cn
http://zECTeW37.tgrrb.cn
http://A5cly2L0.tgrrb.cn
http://7k8x9MGx.tgrrb.cn
http://Pbp7ZTk7.tgrrb.cn
http://nP12NjTy.tgrrb.cn
http://www.dtcms.com/a/377457.html

相关文章:

  • 10 分钟上手 ECharts:从“能跑”到“生产级”的完整踩坑之旅
  • Decode Global新官网上线披露核心数据
  • 【Redis】Scan 命令使用教程:高效遍历海量数据
  • 深度解析:抗辐射电源芯片 ASP4644S2B 在空间环境中的单粒子效应表现
  • 多链冷换仓攻略:如何在 Solana、BSC 与 Base 隐身管理资产
  • 【博弈论和SG函数 | 那忘算10】巴什博奕 尼姆博弈及其变种 威佐夫博弈(附例题)
  • Cubemx+Fatfs(解决挂载Fatfs失败的问题)
  • UVa1063/LA3807 Marble Game
  • leetcode LCR 170.交易逆序对的总数
  • 【学习笔记】Https证书如何升级到国密
  • 期权行权期限一般多久?
  • 0基础Java学习过程记录——枚举、注解
  • 【C++】C++ 内存管理
  • C++ STL之list的使用
  • Midjourney绘画创作入门操作创作(宣传创意)
  • 【数据库约束】
  • 小白成长之路-centos7部署ceph存储
  • python学习进阶之面向对象(二)
  • 【芯片设计-信号完整性 SI 学习 1.1.1 -- Unit Interval,比特周期】
  • sudo apt update sudo apt upgrade -y 两个命令的作用
  • 每日算法刷题Day68:9.10:leetcode 最短路6道题,用时2h30min
  • apache详细讲解(apache介绍+apache配置实验+apache实现https网站)
  • 一些常用的激活函数及绘图
  • 第3节-使用表格数据-数据库设计
  • 同步时钟系统在体育场游泳馆的应用
  • QT里获取UUID当做唯一文件名称
  • 【Python】pytorch数据操作
  • iOS应用启动深度解析:dyld动态链接器的工作机制与优化实践
  • [硬件电路-175]:multisim中如何给让光电二极管产生光电流?
  • 小巧精准,安全无忧:安科瑞ADL200N-CT/D16-WF防逆流电表守护阳台光伏