From 502fad11287d160cfdc250aabc925a7e2a965a29 Mon Sep 17 00:00:00 2001 From: CherryLanterns Date: Fri, 26 Jun 2026 19:58:32 +0800 Subject: [PATCH] Init Repo --- .gitignore | 16 +++ LICENSE | 18 ++++ Makefile | 29 ++++++ README.md | 250 ++++++++++++++++++++++++++++++++++++++++++++++ TODO_组员A.md | 51 ++++++++++ TODO_组员B.md | 63 ++++++++++++ TODO_组员C.md | 70 +++++++++++++ data/orders.csv | 11 ++ data/stations.csv | 19 ++++ data/trains.csv | 39 ++++++++ src/common.h | 106 ++++++++++++++++++++ src/graph.cpp | 104 +++++++++++++++++++ src/graph.h | 66 ++++++++++++ src/hash.cpp | 82 +++++++++++++++ src/hash.h | 54 ++++++++++ src/main.cpp | 146 +++++++++++++++++++++++++++ src/order.cpp | 125 +++++++++++++++++++++++ src/order.h | 64 ++++++++++++ src/stack.cpp | 64 ++++++++++++ src/stack.h | 40 ++++++++ src/train.cpp | 129 ++++++++++++++++++++++++ src/train.h | 80 +++++++++++++++ 22 files changed, 1626 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 TODO_组员A.md create mode 100644 TODO_组员B.md create mode 100644 TODO_组员C.md create mode 100644 data/orders.csv create mode 100644 data/stations.csv create mode 100644 data/trains.csv create mode 100644 src/common.h create mode 100644 src/graph.cpp create mode 100644 src/graph.h create mode 100644 src/hash.cpp create mode 100644 src/hash.h create mode 100644 src/main.cpp create mode 100644 src/order.cpp create mode 100644 src/order.h create mode 100644 src/stack.cpp create mode 100644 src/stack.h create mode 100644 src/train.cpp create mode 100644 src/train.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e09771e --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# 编译产物 +obj/ +*.o +*.exe +train_ticket + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# 系统文件 +.DS_Store +Thumbs.db diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fc546b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2026 JYUGod + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d9b8a91 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +# Makefile - 火车票务管理系统 (C++) +# 编译:mingw32-make 或 make(Linux/Mac) +# 清理:mingw32-make clean + +CXX = g++ +CXXFLAGS = -Wall -Wextra -std=c++17 -g +TARGET = train_ticket.exe +SRCDIR = src +OBJDIR = obj + +SRCS = $(wildcard $(SRCDIR)/*.cpp) +OBJS = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(SRCS)) + +$(TARGET): $(OBJDIR) $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJS) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/%.o: $(SRCDIR)/%.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +run: $(TARGET) + ./$(TARGET) + +clean: + rm -rf $(OBJDIR) $(TARGET) + +.PHONY: run clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c92de1 --- /dev/null +++ b/README.md @@ -0,0 +1,250 @@ +# 数据结构课程设计 —— 设计文档 + +## 〇、选题名称 + +**《火车票务管理系统》** + +--- + +## 一、项目背景与意义 + +模拟一个小型火车票务平台,乘客可以查询车次、购票、退票、查看换乘方案;管理员可以维护车次信息。 +目的是综合运用本学期学过的**线性表、栈、队列、图、查找、排序**这几类数据结构,做一个"看得见、能跑起来"的系统,而不是单独练习某一种数据结构。 + +--- + +## 二、需求分析 + +### 2.1 用户角色 + +| 角色 | 权限 | +|---|---| +| 管理员 | 增/删/改车次信息,查看销售统计 | +| 普通乘客 | 查询车次、购票、退票、查看自己的订单、查询换乘方案 | + +### 2.2 功能需求(对应任务书"增删改查排序统计"要求) + +- **增**:新增车次(管理员);生成购票订单(乘客) +- **删**:删除/停运车次(管理员);退票/取消订单(乘客) +- **改**:修改车次信息(票价、时间、余票);改签订单 +- **查**:按车次号/出发站/到达站/日期查询车次;按身份证号/订单号查询订单;模糊查询 +- **排序**:按出发时间、票价、剩余票数排序展示 +- **统计**:统计某车次销售量、某线路客流量、某时间段总收入 + +### 2.3 非功能需求 + +- 界面采用菜单驱动(一级菜单 + 二级菜单),文字交互即可,不强制图形界面 +- 数据用文件持久化(`.csv`),程序启动时加载,操作后及时写回 +- 输入要做基本合法性校验(如车次号不能重复、余票不能为负) + +--- + +## 三、个体数据描述(数据项设计) + +### 3.1 车次信息 TrainInfo + +| 字段 | 说明 | 类型示例 | +|---|---|---| +| trainNo | 车次号(唯一,如 G1234) | char[8] | +| startStation | 始发站 | char[20] | +| endStation | 终点站 | char[20] | +| departTime | 出发时间 | char[10] | +| arriveTime | 到达时间 | char[10] | +| price | 票价 | float | +| totalSeats | 总票数 | int | +| remainSeats | 余票 | int | + +### 3.2 订单信息 OrderInfo + +| 字段 | 说明 | +|---|---| +| orderId | 订单号(唯一) | +| trainNo | 关联车次号 | +| passengerName | 乘客姓名 | +| idCard | 身份证号(唯一标识乘客,可作哈希查找键) | +| seatNo | 座位号 | +| orderTime | 购票时间 | +| status | 状态:已支付 / 候补中 / 已退票 | + +### 3.3 站点/线路信息 Station(供图结构使用) + +| 字段 | 说明 | +|---|---| +| stationId | 站点编号 | +| stationName | 站点名称 | +| adjList | 相邻站点及边权(时长或票价)—— 即图的邻接表 | + +--- + +## 四、数据结构设计(核心部分) + +这是整个课程设计评分的重点,下面把"用在哪、为什么用、要实现哪些算法"都列清楚,方便写报告时直接展开。 + +### 4.1 车次信息表 —— 顺序表(顺序存储结构) + +作为本系统的**主线性表**,对应任务书"线性表若选顺序存储结构"的要求: + +- 查找:**顺序查找** + **二分查找**(按车次号排序后二分)—— 满足"顺序查找 + 其他查找方式选一种" +- 排序:**直接插入排序**(按车次号初始化排序)、**快速排序**(按票价排序)、**堆排序**(按余票数排序)—— 满足"三种以上排序算法" + +```c +typedef struct { + TrainInfo data[MaxSize]; + int length; +} TrainList; + +bool ListInsert(TrainList *&L, int i, TrainInfo e); +bool ListDelete(TrainList *&L, int i, TrainInfo &e); +int SeqSearch(TrainList *L, char trainNo[]); // 顺序查找 +int BinSearch(TrainList *L, char trainNo[]); // 二分查找(要求按trainNo有序) +void QuickSort(TrainList *&L, int low, int high); // 按票价快排 +void HeapSort(TrainList *&L); // 按余票堆排序 +``` + +### 4.2 线路网络 —— 图(邻接表存储) + +这是本系统的**创新亮点**,对应任务书"图存储结构"的加分项: + +- 顶点 = 车站,边 = 两站之间存在的车次(边权可设为"时长"或"票价") +- **图的两种遍历**:DFS(深度优先,找某站出发能到达的所有站点)、BFS(广度优先,找最少换乘次数的路径) +- **最短路径算法(Dijkstra)**:实现"最优换乘路径推荐"——给定起点和终点,算出耗时最短或票价最低的换乘方案 +- (可选加分)**最小生成树**:模拟"以最小总里程把所有站点连通"的线路规划场景 + +```c +typedef struct { + int stationId; + char stationName[20]; + EdgeNode *firstEdge; // 邻接表头指针 +} VertexNode; + +typedef struct EdgeNode { + int toStationId; + float weight; // 时长或票价 + struct EdgeNode *next; +} EdgeNode; + +void DFS(Graph G, int v); +void BFS(Graph G, int v); +void Dijkstra(Graph G, int src, float dist[], int path[]); // 最优换乘路径 +``` + +### 4.3 候补购票队列 —— 队列 + +当某车次余票为0时,乘客可加入候补队列;一旦有人退票释放余票,按**先进先出**原则自动给候补队列里排在最前面的乘客出票。 + +```c +typedef struct { + OrderInfo data[MaxSize]; + int front, rear; +} WaitQueue; + +bool EnQueue(WaitQueue *&Q, OrderInfo e); // 进入候补 +bool DeQueue(WaitQueue *&Q, OrderInfo &e); // 候补转正出票 +``` + +### 4.4 操作撤销栈 —— 栈 + +记录最近若干次"购票/退票/改签"操作,支持"撤销上一步",避免误操作(也是一个容易讲的创新点)。 + +```c +typedef struct { + OperationLog data[MaxSize]; + int top; +} OpStack; + +bool Push(OpStack *&S, OperationLog op); +bool Pop(OpStack *&S, OperationLog &op); // 撤销最近一次操作 +``` + +### 4.5 订单快速查找 —— 哈希表 + +按身份证号哈希定位订单,避免每次都要顺序扫描全部订单,对应"分块查找/哈希表查找选一种"的要求(这里选哈希表)。 + +```c +int Hash(char idCard[]); // 简单除留余数法或字符累加法 +bool HashInsert(OrderInfo orders[], OrderInfo e); +OrderInfo* HashSearch(OrderInfo orders[], char idCard[]); +``` + +> 小结:本系统用到 **顺序表 + 图 + 队列 + 栈 + 哈希表** 五种数据结构,覆盖面广,五个模块分给3人小组刚好可以人均2类结构、4个以上核心函数模块,工作量和创新点都比较容易达标。 + +--- + +## 五、系统功能模块划分(建议3人分工) + +| 负责人 | 模块 | 核心数据结构 | +|---|---|---| +| 组员A — 李 | ① 车次信息增删改 ② 顺序/二分查找 ③ 三种排序 ④ 文件读写(trains.csv) | 顺序表 | +| 组员B — 刘 | ① 购票下单 ② 退票/改签 ③ 候补排队 ④ 操作撤销栈 | 队列、栈 | +| 组员C — 朱 | ① 线路图构建 ② 图的遍历(DFS/BFS) ③ 最短路径换乘推荐 ④ 订单哈希查找与销量统计 | 图、哈希表 | +| 共同 | 菜单系统、登录权限、主程序整合 | — | + +每人独立完成的模块均≥4个,预计人均代码量400行以上,总代码量轻松超过900行(任务书最低要求)。 + +--- + +## 六、文件存储格式设计 + +**trains.csv** +``` +车次号,始发站,终点站,出发时间,到达时间,票价,总票数,余票 +G1907,北京,上海,08:00,13:28,553.0,800,800 +``` + +**orders.csv** +``` +订单号,车次号,乘客姓名,身份证号,座位号,购票时间,状态 +O00001,G1907,张三,1101**********1234,03A,2026-07-08 09:12,已支付 +``` + +**stations.csv**(辅助构建图) +``` +站点编号,站点名称,相邻站点(车次号:边权) +1,北京,2:553.0 +2,上海,1:553.0;3:120.0 +``` + +> 建议至少录入30条以上"看起来真实"的车次/乘客数据,可以参考12306的真实城市和车次号编一套,满足"30条以上相对真实个体信息"的要求。 + +--- + +## 七、业务流程(文字版,写报告时可画成流程图) + +**购票流程**: +登录 → 查询车次(条件查询+排序展示)→ 选择车次 → 判断余票 +  ├ 有余票 → 生成订单、扣减余票、写回文件 +  └ 无余票 → 询问是否进入候补队列 → 进入WaitQueue +退票时若有候补,自动从队首取出一人补票。 + +**换乘查询流程**: +输入起点/终点 → 在图中用Dijkstra计算最短路径(按时长或票价)→ 输出换乘方案(经过哪些站、换乘几次) + +**数据流向**: +用户输入 → 内存数据结构(顺序表/图/队列/栈/哈希表)→ 算法处理(查找/排序/最短路径)→ 显示结果 → 写回csv文件 + +--- + +## 八、创新点设计(用于答辩加分) + +1. **最优换乘路径推荐**(图+Dijkstra)—— 力度最大的创新点 +2. **候补购票自动转正**(队列) +3. **操作撤销/回退功能**(栈) +4. **模糊查询/多条件组合查询**(如同时按出发站+价格区间筛选) +5. (可选)简单密码哈希存储,提升登录安全性 + +> 任务书规定:每个创新点加5分,一组总加分不超过60分。上面5个点选3-4个做扎实,比贪多但都做得浅更划算。 + +--- + +## 九、工作量自查清单 + +- [ ] 每人源码 ≥ 300行(建议人均400行留余量) +- [ ] 总代码量 ≥ 900行 +- [ ] 录入 ≥ 30条相对真实的车次/乘客数据 +- [ ] 顺序表满足:顺序查找 + 二分查找;三种以上排序算法 +- [ ] 图结构满足:两种遍历 + 最短路径算法 +- [ ] 报告正文(不含图、代码、运行截图)≥ 3000字 +- [ ] 与参考书同类型选题代码重复率 < 50%(本选题与参考书的"通讯录系统"差异较大,基本不存在重复风险) +- [ ] 全程使用 C/C++(过程性语言),未使用Python等被禁止的语言 + +--- diff --git a/TODO_组员A.md b/TODO_组员A.md new file mode 100644 index 0000000..3ad38d4 --- /dev/null +++ b/TODO_组员A.md @@ -0,0 +1,51 @@ +# TODO List — 组员A + +**负责模块**: 车次信息管理(顺序表) +**核心数据结构**: 顺序表 `TrainList` +**文件**: `src/train.h`、`src/train.cpp` +**分支**: `feature/train` + +--- + +## 一、顺序表基础操作 + +- [ ] `ListInsert` — 在位置 i 插入车次(检查越界/满/重复) +- [ ] `ListDelete` — 删除位置 i 的车次(元素前移) +- [ ] `ListUpdate` — 更新位置 i 的车次信息 + +## 二、查找算法(两种) + +- [ ] `SeqSearch` — 顺序查找(按车次号逐个比较) +- [ ] `BinSearch` — 二分查找(需先按 trainNo 排序) +- [ ] `SearchByStation` — 按始发站/终点站模糊查找 +- [ ] `AdvancedSearch` — 多条件组合查询(站名 + 票价区间) + +## 三、排序算法(三种) + +- [ ] `InsertSort` — 直接插入排序(按车次号升序) +- [ ] `QuickSort` — 快速排序(按票价排序) +- [ ] `HeapSort` — 堆排序(按余票数排序) + +## 四、文件读写 + +- [ ] `loadTrainsFromFile` — 从 `data/trains.csv` 读取车次数据 +- [ ] `saveTrainsToFile` — 将车次数据写回 `data/trains.csv` + +## 五、管理员交互接口 + +- [ ] `adminAddTrain` — 交互式新增车次(录入各字段,校验合法性) +- [ ] `adminDeleteTrain` — 按车次号查找并删除 +- [ ] `adminModifyTrain` — 查找后选择字段修改 +- [ ] `adminListTrains` — 遍历输出所有车次(格式化表格) +- [ ] `adminSearchTrain` — 菜单选择查找方式(顺序/二分/模糊) +- [ ] `adminSortTrains` — 菜单选择排序方式(票价快排 / 余票堆排) +- [ ] `adminShowStatistics` — 调用 hash 模块统计函数展示 + +## 六、乘客交互接口 + +- [ ] `passengerSearchTrain` — 乘客查车次(按站名/时间/票价筛选) + +--- + +> **依赖关系**: 先完成基础操作(增删改)和文件读写 → 再做查找 → 再做排序 → 最后做交互接口 +> **验收标准**: 管理员能完成车次增删改查全流程,排序结果正确,数据重启后不丢失 diff --git a/TODO_组员B.md b/TODO_组员B.md new file mode 100644 index 0000000..a22d13a --- /dev/null +++ b/TODO_组员B.md @@ -0,0 +1,63 @@ +# TODO List — 组员B + +**负责模块**: 订单管理 + 撤销栈 +**核心数据结构**: 候补队列 `WaitQueue`(循环队列)、撤销栈 `OpStack`(顺序栈) +**文件**: `src/order.h`、`src/order.cpp`、`src/stack.h`、`src/stack.cpp` +**分支**: `feature/order` + +--- + +## 一、候补队列(循环队列) + +- [ ] `InitQueue` — 初始化队列(front = rear = count = 0) +- [ ] `EnQueue` — 候补入队(循环队列,检查队满) +- [ ] `DeQueue` — 候补转正出票(循环队列,检查队空) +- [ ] `QueueEmpty` — 判空 +- [ ] `QueueFull` — 判满 + +## 二、订单操作 + +- [ ] `GenerateOrderId` — 生成 O00001 格式自增订单号 +- [ ] `BuyTicket` — 购票核心逻辑 + - 查找车次 → 判断余票 + - 有余票:`remainSeats--`,生成订单,`Push` 到撤销栈 + - 无余票:询问是否进入候补队列 → `EnQueue` +- [ ] `RefundTicket` — 退票核心逻辑 + - 订单状态改为 `REFUNDED`,车次 `remainSeats++` + - 检查候补队列是否非空 → 自动 `DeQueue` 补票 + - `Push` 到撤销栈 +- [ ] `ChangeTicket` — 改签核心逻辑 + - 退原票 + 购新票,更新订单车次号和座位号 + - `Push` 到撤销栈 +- [ ] `SearchOrdersByIdCard` — 按身份证号查询订单(遍历数组) +- [ ] `SearchOrderById` — 按订单号精确查找 + +## 三、文件读写 + +- [ ] `loadOrdersFromFile` — 从 `data/orders.csv` 读取订单数据 +- [ ] `saveOrdersToFile` — 将订单数据写回 `data/orders.csv` + +## 四、撤销栈(顺序栈) + +- [ ] `InitStack` — 初始化(top = -1) +- [ ] `Push` — 入栈记录操作日志 +- [ ] `Pop` — 出栈获取最近操作 +- [ ] `StackEmpty` — 判空 +- [ ] `StackFull` — 判满 +- [ ] `ExecuteUndo` — 执行撤销 + - 购票 → 退票、退票 → 恢复购票、改签 → 改回原车次 + - 撤销操作本身不再次入栈 + +## 五、乘客交互接口 + +- [ ] `passengerBuyTicket` — 购票菜单交互(选择车次 → 输入姓名/身份证 → 确认购票) +- [ ] `passengerRefundTicket` — 退票菜单交互(输入订单号 → 确认退票) +- [ ] `passengerChangeTicket` — 改签菜单交互(输入订单号 + 新车次号 → 确认改签) +- [ ] `passengerViewOrders` — 查看我的订单(输入身份证号 → 显示所有订单) +- [ ] `passengerUndo` — 撤销菜单交互(显示最近操作 → 确认撤销 → `ExecuteUndo`) + +--- + +> **依赖关系**: `order.cpp` 需要 `#include "train.h"`(操作余票)和 `#include "stack.h"`(记录操作) +> **时序关键**: 购票扣减余票 / 退票释放余票 / 候补自动转正 这三个联动的正确性最重要 +> **验收标准**: 乘客能完成购票→退票→撤销→候补自动补票全流程,队列 FIFO 顺序正确 diff --git a/TODO_组员C.md b/TODO_组员C.md new file mode 100644 index 0000000..dcf6614 --- /dev/null +++ b/TODO_组员C.md @@ -0,0 +1,70 @@ +# TODO List — 组员C + +**负责模块**: 线路网络图 + 订单哈希查找与统计 +**核心数据结构**: 图 `StationGraph`(邻接表)、哈希表 `HashNode`(链地址法) +**文件**: `src/graph.h`、`src/graph.cpp`、`src/hash.h`、`src/hash.cpp` +**分支**: `feature/graph` + +--- + +## 一、图基础操作(邻接表) + +- [ ] `InitGraph` — 初始化图(`vertexCount = 0`,`firstEdge = nullptr`) +- [ ] `AddVertex` — 添加站点顶点(检查重复 / 越界) +- [ ] `AddEdge` — 添加线路边(无向图,需同时添加两条边) +- [ ] `FindVertex` — 按 `stationId` 查找顶点下标 + +## 二、图遍历算法 + +- [ ] `DFS` — 深度优先遍历(递归实现) + - 从 `startIdx` 出发,标记已访问,输出能到达的所有站点 +- [ ] `BFS` — 广度优先遍历(队列辅助) + - 输出最少换乘次数的路径 + +## 三、最短路径算法(核心加分项) + +- [ ] `Dijkstra` — Dijkstra 最短路径算法 + - 初始化 `dist[] = INF`,`visited[] = false`,`dist[src] = 0` + - 循环选最小未访问顶点,遍历邻接表松弛 +- [ ] `PrintPath` — 递归回溯 path[] 数组打印路径 +- [ ] `RecommendTransfer` — 换乘推荐入口 + - 输入起点/终点站名 → 调用 Dijkstra → 输出换乘方案 + - 方案包含:经过站点、换乘次数、总时长/总票价 + +## 四、最小生成树(可选加分) + +- [ ] `PrimMST` — Prim 算法,以最小总里程连通所有站点 + +## 五、站点文件读写 + +- [ ] `loadStationsFromFile` — 从 `data/stations.csv` 读取站点和边数据 + - 解析顶点行和邻接边(格式: `相邻站点(车次号:边权);...`) + - 调用 `AddVertex` / `AddEdge` 构建图 + +## 六、哈希表操作(链地址法) + +- [ ] `Hash` — 哈希函数(身份证号字符累加 % `HASH_SIZE`) +- [ ] `InitHashTable` — 初始化所有桶为 `nullptr` +- [ ] `HashInsert` — 插入订单(头插法) +- [ ] `HashSearch` — 按身份证号查找订单 +- [ ] `HashSearchByOrderId` — 按订单号查找(遍历所有桶) +- [ ] `HashDelete` — 从哈希表中删除订单 +- [ ] `DestroyHashTable` — 释放所有链表节点内存 + +## 七、统计功能 + +- [ ] `CountSalesByTrain` — 统计某车次已支付订单数 +- [ ] `CountTrafficByRoute` — 统计某线路(始发站-终点站)客流量 +- [ ] `CountRevenueByPeriod` — 统计某时间段总收入 +- [ ] `HotTrainRanking` — 热门车次排行(按销量降序,取前 N) + +## 八、乘客交互接口 + +- [ ] `passengerFindTransfer` — 换乘查询菜单交互 + - 输入起点站名和终点站名 → 调用 `RecommendTransfer` + +--- + +> **依赖关系**: `graph.cpp` 需 `#include `(BFS 辅助队列),`hash.cpp` 需 `#include "train.h"`(统计关联车次表) +> **算法重点**: Dijkstra 是最大加分项,务必写清注释便于答辩 +> **验收标准**: 能正确构建线路图,DFS/BFS 遍历正确,Dijkstra 给出最优换乘方案,哈希查找快速定位,统计数据准确 diff --git a/data/orders.csv b/data/orders.csv new file mode 100644 index 0000000..3f46227 --- /dev/null +++ b/data/orders.csv @@ -0,0 +1,11 @@ +订单号,车次号,乘客姓名,身份证号,座位号,购票时间,状态 +O00001,G101,张三,110101199001011234,01A,2026-06-20 08:00,已支付 +O00002,G101,李四,110102199102022345,02B,2026-06-20 08:15,已支付 +O00003,G301,王五,320101199203033456,03C,2026-06-20 09:00,已支付 +O00004,G401,赵六,440101199304044567,01A,2026-06-20 09:30,已支付 +O00005,G501,孙七,610101199405055678,05A,2026-06-20 10:00,已支付 +O00006,G101,周八,110103199506066789,03A,2026-06-21 07:30,已支付 +O00007,G201,吴九,420101199607077890,02A,2026-06-21 08:00,已退票 +O00008,G601,郑十,510101199708088901,01A,2026-06-21 09:00,已支付 +O00009,G101,冯十一,110104199809099012,04D,2026-06-22 06:30,已支付 +O00010,G701,陈十二,430101199910101023,01A,2026-06-22 07:45,候补中 diff --git a/data/stations.csv b/data/stations.csv new file mode 100644 index 0000000..c0bfb63 --- /dev/null +++ b/data/stations.csv @@ -0,0 +1,19 @@ +站点编号,站点名称,相邻站点(车次号:边权) +1,北京,2:553.0;3:862.0;5:520.0;9:515.0;17:360.0;21:309.0;31:54.5 +2,上海,1:553.0;6:145.0;11:73.0;33:39.5 +3,广州,1:862.0;7:74.5;27:314.0 +4,深圳,3:74.5;35:225.0 +5,武汉,1:520.0;13:164.0;25:130.0 +6,南京,2:145.0;15:117.5 +7,西安,9:515.0;23:174.5;29:263.0 +8,成都,11:154.0;29:263.0 +9,重庆,8:154.0 +10,长沙,13:164.0;27:314.0 +11,杭州,15:117.5;11:73.0 +12,沈阳,17:360.0 +13,郑州,21:309.0 +14,兰州,23:174.5 +15,合肥,25:130.0 +16,天津,31:54.5 +17,苏州,33:39.5 +18,厦门,35:225.0 diff --git a/data/trains.csv b/data/trains.csv new file mode 100644 index 0000000..d0b9dd6 --- /dev/null +++ b/data/trains.csv @@ -0,0 +1,39 @@ +车次号,始发站,终点站,出发时间,到达时间,票价,总票数,余票 +G101,北京,上海,06:00,10:28,553.0,800,800 +G102,上海,北京,07:00,11:28,553.0,800,800 +G103,北京,广州,08:00,16:00,862.0,600,600 +G104,广州,北京,09:00,17:00,862.0,600,600 +G201,北京,武汉,06:30,10:30,520.0,500,500 +G202,武汉,北京,07:30,11:30,520.0,500,500 +G301,上海,南京,08:00,09:15,145.0,400,400 +G302,南京,上海,09:30,10:45,145.0,400,400 +G401,广州,深圳,06:00,06:36,74.5,300,300 +G402,深圳,广州,07:00,07:36,74.5,300,300 +G501,北京,西安,08:00,12:30,515.0,450,450 +G502,西安,北京,09:00,13:30,515.0,450,450 +G601,成都,重庆,08:00,09:30,154.0,350,350 +G602,重庆,成都,10:00,11:30,154.0,350,350 +G701,武汉,长沙,07:00,08:15,164.0,300,300 +G702,长沙,武汉,08:30,09:45,164.0,300,300 +G801,南京,杭州,09:00,10:15,117.5,380,380 +G802,杭州,南京,10:30,11:45,117.5,380,380 +G901,北京,沈阳,07:00,10:00,360.0,350,350 +G902,沈阳,北京,08:00,11:00,360.0,350,350 +G1001,上海,杭州,06:00,06:50,73.0,400,400 +G1002,杭州,上海,07:00,07:50,73.0,400,400 +G1101,北京,郑州,08:00,10:30,309.0,300,300 +G1102,郑州,北京,11:00,13:30,309.0,300,300 +G1201,西安,兰州,07:30,10:00,174.5,250,250 +G1202,兰州,西安,10:30,13:00,174.5,250,250 +G1301,武汉,合肥,08:00,10:00,130.0,200,200 +G1302,合肥,武汉,10:30,12:30,130.0,200,200 +G1401,广州,长沙,09:00,11:30,314.0,300,300 +G1402,长沙,广州,12:00,14:30,314.0,300,300 +G1501,成都,西安,07:00,10:30,263.0,280,280 +G1502,西安,成都,11:00,14:30,263.0,280,280 +G1601,北京,天津,06:30,07:00,54.5,500,500 +G1602,天津,北京,07:30,08:00,54.5,500,500 +G1701,上海,苏州,08:00,08:25,39.5,450,450 +G1702,苏州,上海,08:50,09:15,39.5,450,450 +G1801,深圳,厦门,07:00,10:30,225.0,220,220 +G1802,厦门,深圳,11:00,14:30,225.0,220,220 diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..7b01a0c --- /dev/null +++ b/src/common.h @@ -0,0 +1,106 @@ +/** + * common.h - 火车票务管理系统公共头文件 + * 定义所有模块共享的数据类型、常量、枚举 + * C++ 版本 + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +using namespace std; + +/* ==================== 系统常量 ==================== */ + +constexpr int MAX_TRAINS = 100; // 最大车次数 +constexpr int MAX_ORDERS = 500; // 最大订单数 +constexpr int MAX_STATIONS = 50; // 最大站点数 +constexpr int MAX_QUEUE_SIZE = 100; // 候补队列最大长度 +constexpr int MAX_STACK_SIZE = 100; // 撤销栈最大深度 + +/* ==================== 枚举 ==================== */ + +// 订单状态 +enum class OrderStatus { + PAID, // 已支付 + WAITING, // 候补中 + REFUNDED // 已退票 +}; + +// 用户角色 +enum class UserRole { + ADMIN, // 管理员 + PASSENGER // 普通乘客 +}; + +// 操作类型(用于撤销栈) +enum class OpType { + BUY, // 购票 + REFUND, // 退票 + CHANGE // 改签 +}; + +/* ==================== 结构体定义 ==================== */ + +// --- 车次信息 --- +struct TrainInfo { + string trainNo; // 车次号(唯一,如 G1234) + string startStation; // 始发站 + string endStation; // 终点站 + string departTime; // 出发时间 + string arriveTime; // 到达时间 + float price = 0.0f; // 票价 + int totalSeats = 0; // 总票数 + int remainSeats = 0; // 余票 +}; + +// --- 订单信息 --- +struct OrderInfo { + string orderId; // 订单号(唯一,如 O00001) + string trainNo; // 关联车次号 + string passengerName; // 乘客姓名 + string idCard; // 身份证号 + int seatNo = 0; // 座位号 + string orderTime; // 购票时间 + OrderStatus status = OrderStatus::PAID; // 订单状态 +}; + +// --- 站点/线路信息 --- +struct EdgeNode { + int toStationId; // 相邻站点编号 + float weight; // 边权(时长或票价) + EdgeNode* next = nullptr;// 下一条边 +}; + +struct VertexNode { + int stationId; // 站点编号 + string stationName; // 站点名称 + EdgeNode* firstEdge = nullptr; // 邻接表头指针 +}; + +// --- 撤销操作日志 --- +struct OperationLog { + OpType type; // 操作类型 + string orderId; // 相关订单号 + string trainNo; // 相关车次号 + int seatNo = 0; // 座位号 + string idCard; // 乘客身份证号 + string timestamp; // 操作时间 +}; + +// --- 订单状态输出辅助 --- +inline const char* OrderStatusStr(OrderStatus s) { + switch (s) { + case OrderStatus::PAID: return "已支付"; + case OrderStatus::WAITING: return "候补中"; + case OrderStatus::REFUNDED:return "已退票"; + default: return "未知"; + } +} + +#endif /* COMMON_H */ diff --git a/src/graph.cpp b/src/graph.cpp new file mode 100644 index 0000000..aa0bbb4 --- /dev/null +++ b/src/graph.cpp @@ -0,0 +1,104 @@ +/** + * graph.cpp - 线路网络图模块实现(邻接表存储) + * 负责人:组员C + * + * TODO: 组员C 请在此文件中实现所有声明的函数 + * C++ 版本 + */ + +#include "graph.h" +#include // BFS 可用 STL queue 辅助,或自己用数组模拟 +#include // INT_MAX + +/* ==================== 全局变量 ==================== */ + +StationGraph stationGraph; + +/* ==================== 图基础操作 ==================== */ + +void InitGraph(StationGraph& G) { + // TODO: vertexCount = 0, edgeCount = 0 + // 遍历 vertices[],firstEdge = nullptr +} + +int AddVertex(StationGraph& G, int stationId, const string& stationName) { + // TODO: 检查是否已存在、是否超过 MAX_STATIONS + // 创建新顶点,vertexCount++ + return -1; +} + +bool AddEdge(StationGraph& G, int fromId, int toId, float weight) { + // TODO: 无向图,需同时添加两条边 + // 1. 创建 EdgeNode{toId, weight} 插入 from 的邻接表头 + // 2. 创建 EdgeNode{fromId, weight} 插入 to 的邻接表头 + // edgeCount++ + return false; +} + +int FindVertex(const StationGraph& G, int stationId) { + // TODO: 遍历 vertices[],按 stationId 查找下标 + return -1; +} + +/* ==================== 图遍历算法 ==================== */ + +void DFS(const StationGraph& G, int startIdx, bool visited[]) { + // TODO: 递归深度优先遍历 + // 1. 标记 visited[startIdx] = true,输出站点名 + // 2. 遍历该顶点的邻接表,对每个未访问的邻接点递归 DFS +} + +void BFS(const StationGraph& G, int startIdx) { + // TODO: 广度优先遍历,输出最少换乘次数的路径 + // 用 queue 辅助 +} + +/* ==================== 最短路径算法 ==================== */ + +void Dijkstra(const StationGraph& G, int srcIdx, + float dist[], int path[]) { + // TODO: Dijkstra 最短路径算法 + // 1. 初始化 dist[] = INF, visited[] = false + // 2. dist[src] = 0, path[src] = -1 + // 3. 循环 vertexCount 次: + // a. 选未访问中 dist 最小的顶点 u + // b. 标记 visited[u] = true + // c. 遍历 u 的邻接表,松弛操作 +} + +void PrintPath(const StationGraph& G, int path[], int destIdx) { + // TODO: 递归回溯打印路径 + // if (path[destIdx] != -1) PrintPath(G, path, path[destIdx]) + // cout << G.vertices[destIdx].stationName +} + +void RecommendTransfer(const StationGraph& G, + const string& startName, const string& endName) { + // TODO: + // 1. 按站名查找顶点下标 + // 2. 调用 Dijkstra + // 3. 调用 PrintPath 输出换乘方案 +} + +/* ==================== 最小生成树(可选加分) ==================== */ + +void PrimMST(const StationGraph& G) { + // TODO: Prim 算法实现最小生成树 +} + +/* ==================== 文件读写 ==================== */ + +int loadStationsFromFile(const string& filename) { + // TODO: 用 ifstream 读取 CSV + // 解析站点顶点和邻接边,调用 AddVertex / AddEdge 构建图 + // 边的格式: "相邻站点(车次号:边权)",多条边用 ; 分隔 + return 0; +} + +/* ==================== 乘客接口 ==================== */ + +void passengerFindTransfer() { + // TODO: + // 1. 输入起点站名和终点站名 + // 2. 调用 RecommendTransfer +} diff --git a/src/graph.h b/src/graph.h new file mode 100644 index 0000000..faade6e --- /dev/null +++ b/src/graph.h @@ -0,0 +1,66 @@ +/** + * graph.h - 线路网络图模块(邻接表存储) + * 负责人:组员C + * + * 核心数据结构:图 StationGraph(邻接表) + * 算法:DFS 深度优先遍历、BFS 广度优先遍历、Dijkstra 最短路径 + * C++ 版本 + */ + +#ifndef GRAPH_H +#define GRAPH_H + +#include "common.h" + +/* ==================== 图结构 ==================== */ + +struct StationGraph { + VertexNode vertices[MAX_STATIONS]; // 顶点数组 + int vertexCount = 0; // 当前顶点数 + int edgeCount = 0; // 边数 +}; + +/* ==================== 图基础操作 ==================== */ + +void InitGraph(StationGraph& G); +int AddVertex(StationGraph& G, int stationId, const string& stationName); +bool AddEdge(StationGraph& G, int fromId, int toId, float weight); +int FindVertex(const StationGraph& G, int stationId); + +/* ==================== 图遍历算法 ==================== */ + +// 深度优先遍历(DFS)—— 递归实现 +void DFS(const StationGraph& G, int startIdx, bool visited[]); + +// 广度优先遍历(BFS)—— 队列实现,找最少换乘次数路径 +void BFS(const StationGraph& G, int startIdx); + +/* ==================== 最短路径算法 ==================== */ + +// Dijkstra 最短路径 —— 最优换乘推荐 +// dist[]: 从 src 到各站点的最短距离 +// path[]: 前驱节点数组,用于回溯路径 +void Dijkstra(const StationGraph& G, int srcIdx, + float dist[], int path[]); + +// 打印路径(回溯 path[] 数组) +void PrintPath(const StationGraph& G, int path[], int destIdx); + +// 推荐换乘方案(用户接口:输入起点/终点站名,输出最优方案) +void RecommendTransfer(const StationGraph& G, + const string& startName, const string& endName); + +/* ==================== 最小生成树(可选加分) ==================== */ + +// Prim 算法 —— 最小总里程连通所有站点 +void PrimMST(const StationGraph& G); + +/* ==================== 文件读写 ==================== */ + +int loadStationsFromFile(const string& filename); + +/* ==================== 乘客接口 ==================== */ + +void passengerFindTransfer(); + +#endif /* GRAPH_H */ diff --git a/src/hash.cpp b/src/hash.cpp new file mode 100644 index 0000000..193b787 --- /dev/null +++ b/src/hash.cpp @@ -0,0 +1,82 @@ +/** + * hash.cpp - 订单哈希查找与销售统计模块实现 + * 负责人:组员C + * + * TODO: 组员C 请在此文件中实现所有声明的函数 + * C++ 版本 + */ + +#include "hash.h" +#include "train.h" // 需要 trainList 做统计关联 + +/* ==================== 全局变量 ==================== */ + +HashNode* orderHashTable[HASH_SIZE] = {nullptr}; + +/* ==================== 哈希表操作 ==================== */ + +int Hash(const string& idCard) { + // TODO: 哈希函数 + // 方案1:取身份证后6位 % HASH_SIZE + // 方案2:逐字符累加 ASCII 值 % HASH_SIZE + return 0; +} + +bool HashInsert(HashNode* hashTable[], const OrderInfo& order) { + // TODO: 链地址法插入 + // 1. 计算 hash = Hash(order.idCard) + // 2. 创建新 HashNode,头插法插入 hashTable[hash] + return false; +} + +const HashNode* HashSearch(HashNode* const hashTable[], const string& idCard) { + // TODO: 哈希查找 + // 1. 计算 hash = Hash(idCard) + // 2. 遍历 hashTable[hash] 链表,匹配 idCard + return nullptr; +} + +const OrderInfo* HashSearchByOrderId(HashNode* const hashTable[], + const string& orderId) { + // TODO: 按订单号查找(需遍历所有桶) + return nullptr; +} + +bool HashDelete(HashNode* hashTable[], const string& orderId) { + // TODO: 哈希删除 + // 遍历链表找到目标节点,从链表中移除并 delete + return false; +} + +void InitHashTable(HashNode* hashTable[]) { + // TODO: 所有桶置为 nullptr +} + +void DestroyHashTable(HashNode* hashTable[]) { + // TODO: 遍历每个桶,释放链表所有节点 +} + +/* ==================== 统计功能 ==================== */ + +int CountSalesByTrain(const string& trainNo) { + // TODO: 统计某车次已支付订单数 + // 遍历所有桶的链表,匹配 trainNo 且 status == PAID + return 0; +} + +int CountTrafficByRoute(const string& startStation, const string& endStation) { + // TODO: 统计某线路客流量 + // 需要关联车次表(trainList)判断始发-终点站 + return 0; +} + +float CountRevenueByPeriod(const string& startTime, const string& endTime) { + // TODO: 统计某时间段总收入 + // 匹配 orderTime 在区间内的已支付订单,累加对应车次的票价 + return 0.0f; +} + +void HotTrainRanking(TrainInfo ranking[], int topN) { + // TODO: 热门车次排行 + // 统计每个车次销售量 → 排序 → 取前 topN +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..682610b --- /dev/null +++ b/src/hash.h @@ -0,0 +1,54 @@ +/** + * hash.h - 订单哈希查找与销售统计模块 + * 负责人:组员C + * + * 核心数据结构:哈希表(链地址法解决冲突) + * 功能:按身份证号快速查找订单、销售统计 + * C++ 版本 + */ + +#ifndef HASH_H +#define HASH_H + +#include "common.h" + +/* ==================== 哈希表结构 ==================== */ + +constexpr int HASH_SIZE = 101; // 哈希表大小(素数,减少冲突) + +// 哈希表节点(链表) +struct HashNode { + OrderInfo order; // 订单数据 + HashNode* next = nullptr; // 链表下一节点 +}; + +/* ==================== 哈希表操作 ==================== */ + +// 哈希函数(字符累加法 + 除留余数) +int Hash(const string& idCard); + +// 插入订单到哈希表 +bool HashInsert(HashNode* hashTable[], const OrderInfo& order); + +// 按身份证号查找订单 +const HashNode* HashSearch(HashNode* const hashTable[], const string& idCard); + +// 按订单号查找(顺序扫描哈希表) +const OrderInfo* HashSearchByOrderId(HashNode* const hashTable[], + const string& orderId); + +// 删除哈希表中的订单 +bool HashDelete(HashNode* hashTable[], const string& orderId); + +// 初始化 / 销毁哈希表 +void InitHashTable(HashNode* hashTable[]); +void DestroyHashTable(HashNode* hashTable[]); + +/* ==================== 统计功能 ==================== */ + +int CountSalesByTrain(const string& trainNo); +int CountTrafficByRoute(const string& startStation, const string& endStation); +float CountRevenueByPeriod(const string& startTime, const string& endTime); +void HotTrainRanking(TrainInfo ranking[], int topN); + +#endif /* HASH_H */ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d629127 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,146 @@ +/** + * main.cpp - 火车票务管理系统主程序 + * 负责菜单调度、登录验证、模块整合 + * C++ 版本 + */ + +#include "common.h" +#include "train.h" +#include "order.h" +#include "graph.h" +#include "hash.h" +#include "stack.h" + +/* ==================== 全局数据 ==================== */ + +TrainList trainList; // 车次顺序表 +OrderInfo orderArray[MAX_ORDERS]; // 订单数组 +int orderCount = 0; // 订单总数 +StationGraph stationGraph; // 站点图 +WaitQueue* waitQueues[MAX_TRAINS] = {nullptr}; // 候补队列指针数组 +OpStack undoStack; // 撤销栈 + +/* ==================== 文件路径 ==================== */ + +const string TRAINS_CSV = "../data/trains.csv"; +const string ORDERS_CSV = "../data/orders.csv"; +const string STATIONS_CSV = "../data/stations.csv"; + +/* ==================== 菜单函数声明 ==================== */ + +void showMainMenu(); +void showAdminMenu(); +void showPassengerMenu(); + +/* ==================== 主函数 ==================== */ + +int main() { + cout << "========================================" << endl; + cout << " 火车票务管理系统 v1.0 (C++)" << endl; + cout << "========================================" << endl << endl; + + // 加载数据 + cout << "正在加载数据..." << endl; + loadTrainsFromFile(TRAINS_CSV); + loadOrdersFromFile(ORDERS_CSV); + loadStationsFromFile(STATIONS_CSV); + cout << "数据加载完成!" << endl << endl; + + // 登录选择 + int role; + while (true) { + cout << "请选择登录角色:" << endl; + cout << " 1. 管理员" << endl; + cout << " 2. 普通乘客" << endl; + cout << " 0. 退出系统" << endl; + cout << "请输入选项:"; + cin >> role; + cin.ignore(); // 吃掉换行符 + + if (role == 0) { + cout << endl << "正在保存数据..." << endl; + saveTrainsToFile(TRAINS_CSV); + saveOrdersToFile(ORDERS_CSV); + cout << "数据已保存,感谢使用!" << endl; + break; + } else if (role == 1) { + showAdminMenu(); + } else if (role == 2) { + showPassengerMenu(); + } else { + cout << "输入无效,请重新选择!" << endl << endl; + } + } + + return 0; +} + +/* ==================== 管理员菜单 ==================== */ + +void showAdminMenu() { + int choice; + + while (true) { + cout << endl; + cout << "========== 管理员菜单 ==========" << endl; + cout << " 1. 新增车次" << endl; + cout << " 2. 删除/停运车次" << endl; + cout << " 3. 修改车次信息" << endl; + cout << " 4. 查看车次列表" << endl; + cout << " 5. 查询车次(按车次号)" << endl; + cout << " 6. 排序车次(按票价/余票/时间)" << endl; + cout << " 7. 查看销售统计" << endl; + cout << " 0. 返回上级菜单" << endl; + cout << "请输入选项:"; + cin >> choice; + cin.ignore(); + + switch (choice) { + case 0: return; + case 1: adminAddTrain(); break; + case 2: adminDeleteTrain(); break; + case 3: adminModifyTrain(); break; + case 4: adminListTrains(); break; + case 5: adminSearchTrain(); break; + case 6: adminSortTrains(); break; + case 7: adminShowStatistics(); break; + default: + cout << "输入无效,请重新选择!" << endl; + } + } +} + +/* ==================== 乘客菜单 ==================== */ + +void showPassengerMenu() { + int choice; + + while (true) { + cout << endl; + cout << "========== 乘客菜单 ==========" << endl; + cout << " 1. 查询车次" << endl; + cout << " 2. 购票" << endl; + cout << " 3. 退票" << endl; + cout << " 4. 改签" << endl; + cout << " 5. 查看我的订单" << endl; + cout << " 6. 查询换乘方案" << endl; + cout << " 7. 撤销上一步操作" << endl; + cout << " 0. 返回上级菜单" << endl; + cout << "请输入选项:"; + cin >> choice; + cin.ignore(); + + switch (choice) { + case 0: return; + case 1: passengerSearchTrain(); break; + case 2: passengerBuyTicket(); break; + case 3: passengerRefundTicket(); break; + case 4: passengerChangeTicket(); break; + case 5: passengerViewOrders(); break; + case 6: passengerFindTransfer(); break; + case 7: passengerUndo(); break; + default: + cout << "输入无效,请重新选择!" << endl; + } + } +} diff --git a/src/order.cpp b/src/order.cpp new file mode 100644 index 0000000..14ce939 --- /dev/null +++ b/src/order.cpp @@ -0,0 +1,125 @@ +/** + * order.cpp - 购票/退票/改签模块实现 + * 负责人:组员B + * + * TODO: 组员B 请在此文件中实现所有声明的函数 + * C++ 版本 + */ + +#include "order.h" +#include "train.h" // 需要操作车次余票 +#include "stack.h" // 需要记录操作日志到撤销栈 + +/* ==================== 全局变量 ==================== */ + +OrderInfo orderArray[MAX_ORDERS]; +int orderCount = 0; +WaitQueue* waitQueues[MAX_TRAINS] = {nullptr}; + +/* ==================== 队列操作 ==================== */ + +void InitQueue(WaitQueue& Q) { + // TODO: 初始化 front = rear = count = 0 +} + +bool EnQueue(WaitQueue& Q, const OrderInfo& e) { + // TODO: 候补入队(循环队列) + // 检查队满 → 返回 false + // 将 e 存入 data[rear],rear = (rear+1) % MAX_QUEUE_SIZE,count++ + return false; +} + +bool DeQueue(WaitQueue& Q, OrderInfo& e) { + // TODO: 候补转正出票(循环队列) + // 检查队空 → 返回 false + // 取出 data[front],front = (front+1) % MAX_QUEUE_SIZE,count-- + return false; +} + +bool QueueEmpty(const WaitQueue& Q) { + // TODO: count == 0 + return true; +} + +bool QueueFull(const WaitQueue& Q) { + // TODO: count == MAX_QUEUE_SIZE + return false; +} + +/* ==================== 订单操作 ==================== */ + +string GenerateOrderId() { + // TODO: 生成 O00001 格式的自增订单号 + // 用静态变量记录已生成的序号 + return "O00000"; +} + +int BuyTicket(const OrderInfo& order) { + // TODO: 购票逻辑 + // 1. 查出对应车次(trainList 中查找) + // 2. 判断 remainSeats > 0: + // a. 有余票 → remainSeats--,生成订单,存入 orderArray, + // 将操作 Push 到 undoStack + // b. 无余票 → 询问是否进入候补队列 → EnQueue + return -1; +} + +int RefundTicket(const string& orderId) { + // TODO: 退票逻辑 + // 1. 找到订单,status 改为 REFUNDED + // 2. 对应车次 remainSeats++ + // 3. 检查该车次候补队列是否非空: + // a. DeQueue → 自动为候补乘客出票,remainSeats-- + // 4. 将操作 Push 到 undoStack + return -1; +} + +int ChangeTicket(const string& orderId, const string& newTrainNo) { + // TODO: 改签逻辑 + // 1. 原订单退票(释放原车次余票) + // 2. 在新车次购票(扣减新车次余票) + // 3. 更新订单的 trainNo 和 seatNo + // 4. Push 到 undoStack + return -1; +} + +int SearchOrdersByIdCard(const string& idCard, + OrderInfo results[], int maxResults) { + // TODO: 遍历 orderArray,按 idCard 匹配 + return 0; +} + +const OrderInfo* SearchOrderById(const string& orderId) { + // TODO: 遍历 orderArray,按 orderId 匹配,返回指针 + return nullptr; +} + +/* ==================== 文件读写 ==================== */ + +int loadOrdersFromFile(const string& filename) { + // TODO: 用 ifstream 读取 CSV,填充 orderArray + return 0; +} + +int saveOrdersToFile(const string& filename) { + // TODO: 用 ofstream 写回 CSV + return 0; +} + +/* ==================== 乘客接口 ==================== */ + +void passengerBuyTicket() { + // TODO: 交互:选择车次 → 输入姓名/身份证 → 生成订单 +} + +void passengerRefundTicket() { + // TODO: 交互:输入订单号 → 执行退票 +} + +void passengerChangeTicket() { + // TODO: 交互:输入订单号 + 新车次号 → 执行改签 +} + +void passengerViewOrders() { + // TODO: 交互:输入身份证号 → 显示该乘客所有订单 +} diff --git a/src/order.h b/src/order.h new file mode 100644 index 0000000..ffc17a8 --- /dev/null +++ b/src/order.h @@ -0,0 +1,64 @@ +/** + * order.h - 购票/退票/改签模块(订单管理) + * 负责人:组员B + * + * 核心数据结构:候补队列 WaitQueue(队列) + * C++ 版本 + */ + +#ifndef ORDER_H +#define ORDER_H + +#include "common.h" + +/* ==================== 候补队列结构 ==================== */ + +struct WaitQueue { + OrderInfo data[MAX_QUEUE_SIZE]; + int front = 0; // 队首下标 + int rear = 0; // 队尾下标 + int count = 0; // 当前队列长度 +}; + +/* ==================== 队列操作 ==================== */ + +void InitQueue(WaitQueue& Q); +bool EnQueue(WaitQueue& Q, const OrderInfo& e); +bool DeQueue(WaitQueue& Q, OrderInfo& e); +bool QueueEmpty(const WaitQueue& Q); +bool QueueFull(const WaitQueue& Q); + +/* ==================== 订单操作 ==================== */ + +// 生成订单号(自增格式化) +string GenerateOrderId(); + +// 购票 +int BuyTicket(const OrderInfo& order); + +// 退票(若有候补自动转正) +int RefundTicket(const string& orderId); + +// 改签 +int ChangeTicket(const string& orderId, const string& newTrainNo); + +// 按身份证号查询订单,返回匹配数量 +int SearchOrdersByIdCard(const string& idCard, + OrderInfo results[], int maxResults); + +// 按订单号查询 +const OrderInfo* SearchOrderById(const string& orderId); + +/* ==================== 文件读写 ==================== */ + +int loadOrdersFromFile(const string& filename); +int saveOrdersToFile(const string& filename); + +/* ==================== 乘客接口 ==================== */ + +void passengerBuyTicket(); +void passengerRefundTicket(); +void passengerChangeTicket(); +void passengerViewOrders(); + +#endif /* ORDER_H */ diff --git a/src/stack.cpp b/src/stack.cpp new file mode 100644 index 0000000..2f7e599 --- /dev/null +++ b/src/stack.cpp @@ -0,0 +1,64 @@ +/** + * stack.cpp - 操作撤销栈模块实现 + * 负责人:组员B + * + * TODO: 组员B 请在此文件中实现所有声明的函数 + * C++ 版本 + */ + +#include "stack.h" +#include "order.h" // 需要调用 RefundTicket / BuyTicket 执行逆操作 + +/* ==================== 全局变量 ==================== */ + +OpStack undoStack; + +/* ==================== 栈操作 ==================== */ + +void InitStack(OpStack& S) { + // TODO: top = -1 +} + +bool Push(OpStack& S, const OperationLog& op) { + // TODO: 检查栈满 → false + // top++,data[top] = op + return false; +} + +bool Pop(OpStack& S, OperationLog& op) { + // TODO: 检查栈空 → false + // op = data[top],top-- + return false; +} + +bool StackEmpty(const OpStack& S) { + // TODO: top == -1 + return true; +} + +bool StackFull(const OpStack& S) { + // TODO: top == MAX_STACK_SIZE - 1 + return false; +} + +/* ==================== 撤销功能 ==================== */ + +int ExecuteUndo(OpStack& S) { + // TODO: + // 1. Pop 出一个 OperationLog + // 2. 根据其 type 执行逆操作: + // BUY → 调用 RefundTicket + // REFUND → 调用 BuyTicket(恢复原订单) + // CHANGE → 改回原车次 + // 3. 注意:撤销操作本身不再次入栈 + return -1; +} + +/* ==================== 乘客接口 ==================== */ + +void passengerUndo() { + // TODO: + // 1. 判断 undoStack 是否为空 + // 2. 显示最近操作信息,询问是否确认撤销 + // 3. 执行 ExecuteUndo +} diff --git a/src/stack.h b/src/stack.h new file mode 100644 index 0000000..3eb3e5e --- /dev/null +++ b/src/stack.h @@ -0,0 +1,40 @@ +/** + * stack.h - 操作撤销栈模块 + * 负责人:组员B + * + * 核心数据结构:顺序栈 OpStack + * 功能:记录最近操作,支持撤销购票/退票/改签 + * C++ 版本 + */ + +#ifndef STACK_H +#define STACK_H + +#include "common.h" + +/* ==================== 栈结构 ==================== */ + +struct OpStack { + OperationLog data[MAX_STACK_SIZE]; + int top = -1; // 栈顶下标,-1 表示栈空 +}; + +/* ==================== 栈操作 ==================== */ + +void InitStack(OpStack& S); +bool Push(OpStack& S, const OperationLog& op); +bool Pop(OpStack& S, OperationLog& op); +bool StackEmpty(const OpStack& S); +bool StackFull(const OpStack& S); + +/* ==================== 撤销功能 ==================== */ + +// 执行撤销(调用对应的逆操作) +// 购票→退票,退票→购票,改签→改回原车次 +int ExecuteUndo(OpStack& S); + +/* ==================== 乘客接口 ==================== */ + +void passengerUndo(); + +#endif /* STACK_H */ diff --git a/src/train.cpp b/src/train.cpp new file mode 100644 index 0000000..3772a96 --- /dev/null +++ b/src/train.cpp @@ -0,0 +1,129 @@ +/** + * train.cpp - 车次信息管理模块实现(顺序表) + * 负责人:组员A + * + * TODO: 组员A 请在此文件中实现所有声明的函数 + * C++ 版本 + */ + +#include "train.h" + +/* ==================== 全局变量 ==================== */ + +TrainList trainList; + +/* ==================== 基础操作 ==================== */ + +bool ListInsert(TrainList& L, int i, const TrainInfo& e) { + // TODO: 在位置 i 插入车次 + // 检查合法性(i 范围、表是否满、车次号是否重复) + // 将位置 i 及之后元素后移一位 + // 插入新元素,length++ + return false; +} + +bool ListDelete(TrainList& L, int i, TrainInfo& e) { + // TODO: 删除位置 i 的车次,结果存入 e + // 将位置 i+1 及之后元素前移一位 + // length-- + return false; +} + +bool ListUpdate(TrainList& L, int i, const TrainInfo& e) { + // TODO: 更新位置 i 的车次信息 + return false; +} + +/* ==================== 查找算法 ==================== */ + +int SeqSearch(const TrainList& L, const string& trainNo) { + // TODO: 顺序查找,逐个比较 trainNo + // 找到返回下标,未找到返回 -1 + return -1; +} + +int BinSearch(const TrainList& L, const string& trainNo) { + // TODO: 二分查找(前提:顺序表按 trainNo 有序) + // 使用 InsertSort 排序后再查找 + return -1; +} + +int SearchByStation(const TrainList& L, const string& keyword, + TrainInfo results[], int maxResults) { + // TODO: 按站名模糊查找(在 startStation / endStation 中匹配) + return 0; +} + +int AdvancedSearch(const TrainList& L, const string& startStation, + const string& endStation, float minPrice, + float maxPrice, TrainInfo results[], int maxResults) { + // TODO: 多条件组合查询 + return 0; +} + +/* ==================== 排序算法 ==================== */ + +void InsertSort(TrainList& L) { + // TODO: 直接插入排序(按车次号升序) + // 将 trainNo 字符串比较,从小到大排列 +} + +void QuickSort(TrainList& L, int low, int high) { + // TODO: 快速排序(按票价排序) +} + +void HeapSort(TrainList& L) { + // TODO: 堆排序(按余票数排序) + // 构建大顶堆 / 小顶堆 +} + +/* ==================== 文件读写 ==================== */ + +int loadTrainsFromFile(const string& filename) { + // TODO: 用 ifstream 读取 CSV 文件 + // 跳过标题行,逐行解析字段填充 TrainInfo + // 存入 trainList + return 0; +} + +int saveTrainsToFile(const string& filename) { + // TODO: 用 ofstream 写回 CSV 文件 + // 先写标题行,再逐行输出 trainList 数据 + return 0; +} + +/* ==================== 管理员接口 ==================== */ + +void adminAddTrain() { + // TODO: 交互式录入新 TrainInfo,调用 ListInsert +} + +void adminDeleteTrain() { + // TODO: 输入车次号,SeqSearch 找到后调用 ListDelete +} + +void adminModifyTrain() { + // TODO: 查找后修改字段,调用 ListUpdate +} + +void adminListTrains() { + // TODO: 遍历 trainList 输出所有车次 +} + +void adminSearchTrain() { + // TODO: 提示选择查找方式(顺序/二分/站名模糊) +} + +void adminSortTrains() { + // TODO: 提示选择排序方式(票价快排 / 余票堆排) +} + +void adminShowStatistics() { + // TODO: 调用 hash 模块的统计函数展示 +} + +/* ==================== 乘客接口 ==================== */ + +void passengerSearchTrain() { + // TODO: 乘客查车次(支持按站名、时间、票价区间筛选) +} diff --git a/src/train.h b/src/train.h new file mode 100644 index 0000000..a1fddf7 --- /dev/null +++ b/src/train.h @@ -0,0 +1,80 @@ +/** + * train.h - 车次信息管理模块(顺序表) + * 负责人:组员A + * + * 核心数据结构:顺序表 TrainList + * 算法:顺序查找、二分查找、直接插入排序、快速排序、堆排序 + * C++ 版本 + */ + +#ifndef TRAIN_H +#define TRAIN_H + +#include "common.h" + +/* ==================== 顺序表结构 ==================== */ + +struct TrainList { + TrainInfo data[MAX_TRAINS]; + int length = 0; +}; + +/* ==================== 基础操作 ==================== */ + +// 增:在位置 i 插入车次 +bool ListInsert(TrainList& L, int i, const TrainInfo& e); + +// 删:删除位置 i 的车次,将删除的元素存入 e +bool ListDelete(TrainList& L, int i, TrainInfo& e); + +// 改:更新车次信息 +bool ListUpdate(TrainList& L, int i, const TrainInfo& e); + +/* ==================== 查找算法 ==================== */ + +// 顺序查找(按车次号),返回下标,未找到返回 -1 +int SeqSearch(const TrainList& L, const string& trainNo); + +// 二分查找(要求顺序表按 trainNo 有序),返回下标,未找到返回 -1 +int BinSearch(const TrainList& L, const string& trainNo); + +// 按始发站/终点站模糊查找,返回匹配数量 +int SearchByStation(const TrainList& L, const string& keyword, + TrainInfo results[], int maxResults); + +// 多条件组合查询 +int AdvancedSearch(const TrainList& L, const string& startStation, + const string& endStation, float minPrice, + float maxPrice, TrainInfo results[], int maxResults); + +/* ==================== 排序算法 ==================== */ + +// 直接插入排序(按车次号排序) +void InsertSort(TrainList& L); + +// 快速排序(按票价排序) +void QuickSort(TrainList& L, int low, int high); + +// 堆排序(按余票数排序) +void HeapSort(TrainList& L); + +/* ==================== 文件读写 ==================== */ + +int loadTrainsFromFile(const string& filename); +int saveTrainsToFile(const string& filename); + +/* ==================== 管理员接口 ==================== */ + +void adminAddTrain(); +void adminDeleteTrain(); +void adminModifyTrain(); +void adminListTrains(); +void adminSearchTrain(); +void adminSortTrains(); +void adminShowStatistics(); + +/* ==================== 乘客接口 ==================== */ + +void passengerSearchTrain(); + +#endif /* TRAIN_H */