add: 添加实训 '数据结构课程设计'

This commit is contained in:
2025-12-22 17:09:05 +08:00
parent 778956cec3
commit dff46e6f21
14 changed files with 720 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
main.cpp
bin
obj

View File

@@ -0,0 +1,52 @@
# Makefile for Bus Route Management System
CC = gcc
CFLAGS = -Wall -Wextra -g -I./include
LDFLAGS = -lm
# 目录
SRC_DIR = src
INC_DIR = include
OBJ_DIR = obj
BIN_DIR = bin
DATA_DIR = data
# 源文件和目标文件
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
TARGET = $(BIN_DIR)/bus_system
# 默认目标
all: directories $(TARGET)
# 创建必要的目录
directories:
@mkdir -p $(OBJ_DIR)
@mkdir -p $(BIN_DIR)
@mkdir -p $(DATA_DIR)
# 链接目标文件生成可执行文件
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS)
@cp -r $(DATA_DIR) $(BIN_DIR)
@echo "build successful: $(TARGET)"
# 编译源文件为目标文件
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理编译文件
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
@echo "clean over"
# 运行程序
run: $(TARGET)
@cp -r $(DATA_DIR) $(BIN_DIR)
@cd $(BIN_DIR) && ./bus_system
# 安装数据文件(如果需要)
install-data:
@echo "请确保 data/ 目录包含 stations.csv 和 routes.csv"
.PHONY: all clean run directories install-data

View File

@@ -0,0 +1,33 @@
## 课题二十六: 校园巴士实时查询系统
### 一、设计目的
1. 掌握数据结构知识强化C语言编程实现与算法设计的结合
2. 提升对数据结构的逻辑设计与存储结构的理解。
### 二、设计任务与要求
**设计一个校园巴士线路查询系统,模拟巴士运行状态,提供实时到站预测和最优乘车方案。**
- 功能要求
1. 支持定义多条巴士线路,每条线路有固定站点序列,每个站点包含名称、位置坐标、停靠时间,线路信息可从配置文件加载;
2. 为每辆巴士维护当前位置、行驶方向、速度,模拟巴士在站点停靠(固定时间),实时更新所有巴士位置(每虚拟分钟更新一次);
3. 输入站点名,显示即将到达的巴士信息(线路号、预计到达时间),显示当前正在该站点停靠的巴士,显示该站点下一班各线路巴士的预计等待时间;
4. 输入起点和终点站点,推荐换乘方案,显示总行程时间、换乘次数、步行距离(简化),提供最快方案和最简方案(最少换乘)。
- 技术点要求
1. 使用邻接表存储巴士站点网络。
2. 实现Dijkstra算法计算最优路径。
3. 使用文本文件读写数据。
4. 程序结构清晰,功能模块划分合理。
### 三、设计步骤
1. 根据课题,查阅相关资料,确定结构体设计。
2. 明确相关功能,对功能部分进行设计及代码编写。
3. 对所写代码进行测试。
4. 撰写课程设计报告:字数约5000字不包括程序清单
5. 答辩。

View File

@@ -0,0 +1,13 @@
id,routeName,stationCount
0,1ºÅÏß,4
0,1,2,3
5.0,6.0,7.0
1,2ºÅÏß,5
0,4,5,7,9
8.0,6.0,10.0,5.0
2,3ºÅÏß,4
1,6,4,2
4.0,5.0,6.0
3,4ºÅÏß,3
8,1,3
7.0,12.0
1 id,routeName,stationCount
2 0,1ºÅÏß,4
3 0,1,2,3
4 5.0,6.0,7.0
5 1,2ºÅÏß,5
6 0,4,5,7,9
7 8.0,6.0,10.0,5.0
8 2,3ºÅÏß,4
9 1,6,4,2
10 4.0,5.0,6.0
11 3,4ºÅÏß,3
12 8,1,3
13 7.0,12.0

View File

@@ -0,0 +1,11 @@
id,name,latitude,longitude,stopTime
0,火车站,39.9042,116.4074,60
1,市中心,39.9100,116.4100,45
2,大学城,39.9200,116.4200,45
3,科技园,39.9300,116.4300,45
4,商业街,39.9150,116.4250,60
5,体育馆,39.9250,116.4350,45
6,公园,39.9180,116.4180,30
7,机场,39.9400,116.4400,90
8,医院,39.9050,116.4150,50
9,图书馆,39.9280,116.4280,40
1 id name latitude longitude stopTime
2 0 火车站 39.9042 116.4074 60
3 1 市中心 39.9100 116.4100 45
4 2 大学城 39.9200 116.4200 45
5 3 科技园 39.9300 116.4300 45
6 4 商业街 39.9150 116.4250 60
7 5 体育馆 39.9250 116.4350 45
8 6 公园 39.9180 116.4180 30
9 7 机场 39.9400 116.4400 90
10 8 医院 39.9050 116.4150 50
11 9 图书馆 39.9280 116.4280 40

View File

@@ -0,0 +1,49 @@
// include/graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include <stdbool.h>
#include "station.h"
#include "route.h"
// 邻接表节点
typedef struct AdjNode {
int stationId;
int routeId;
double travelTime; // 行驶时间(分钟)
struct AdjNode* next;
} AdjNode;
// 邻接表
typedef struct AdjList {
AdjNode* head;
} AdjList;
// 图结构
typedef struct Graph {
AdjList adjLists[MAX_STATIONS];
int vertexCount;
} Graph;
// 路径节点
typedef struct PathNode {
int stationId;
int routeId;
double time;
struct PathNode* next;
} PathNode;
// 图操作函数
Graph* createGraph(int vertexCount);
void destroyGraph(Graph* graph);
void addEdge(Graph* graph, int from, int to, int routeId, double travelTime);
void buildGraphFromRoutes(Graph* graph, const BusRoute routes[], int routeCount,
const Station stations[], int stationCount);
// 路径查询函数
PathNode* dijkstra(Graph* graph, int start, int end, double* totalTime);
void freePath(PathNode* path);
void displayPath(PathNode* path, const Station stations[], int stationCount,
const BusRoute routes[], int routeCount);
#endif

View File

@@ -0,0 +1,22 @@
// include/route.h
#ifndef ROUTE_H
#define ROUTE_H
#include "station.h"
#define MAX_STATIONS_PER_ROUTE 50
typedef struct BusRoute {
int id;
char routeName[50];
int stationCount;
int stations[MAX_STATIONS_PER_ROUTE]; // 站点ID序列
double segmentTimes[MAX_STATIONS_PER_ROUTE]; // 每段行驶时间(分钟)
} BusRoute;
// 线路管理函数
int loadRoutes(const char* filename, BusRoute routes[], int maxRoutes);
void displayRoute(const BusRoute* route, const Station stations[], int stationCount);
void displayAllRoutes(const BusRoute routes[], int routeCount, const Station stations[], int stationCount);
#endif

View File

@@ -0,0 +1,23 @@
// include/station.h
#ifndef STATION_H
#define STATION_H
#define MAX_NAME 50
#define MAX_STATIONS 100
typedef struct Station {
int id;
char name[MAX_NAME];
double latitude;
double longitude;
int stopTime; // 停靠时间(秒)
} Station;
// 站点管理函数
int loadStations(const char* filename, Station stations[], int maxStations);
int findStationByName(const Station stations[], int count, const char* name);
int findStationById(const Station stations[], int count, int id);
void displayStation(const Station* station);
void displayAllStations(const Station stations[], int count);
#endif

View File

@@ -0,0 +1,169 @@
// src/graph.c
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include "../include/graph.h"
// 创建图
Graph* createGraph(int vertexCount) {
Graph* graph = (Graph*)malloc(sizeof(Graph));
graph->vertexCount = vertexCount;
for (int i = 0; i < MAX_STATIONS; i++) {
graph->adjLists[i].head = NULL;
}
return graph;
}
// 销毁图
void destroyGraph(Graph* graph) {
if (!graph) return;
for (int i = 0; i < MAX_STATIONS; i++) {
AdjNode* curr = graph->adjLists[i].head;
while (curr) {
AdjNode* temp = curr;
curr = curr->next;
free(temp);
}
}
free(graph);
}
// 添加边
void addEdge(Graph* graph, int from, int to, int routeId, double travelTime) {
AdjNode* newNode = (AdjNode*)malloc(sizeof(AdjNode));
newNode->stationId = to;
newNode->routeId = routeId;
newNode->travelTime = travelTime;
newNode->next = graph->adjLists[from].head;
graph->adjLists[from].head = newNode;
}
// 从线路构建图(双向)
void buildGraphFromRoutes(Graph* graph, const BusRoute routes[], int routeCount,
const Station stations[], int stationCount) {
for (int r = 0; r < routeCount; r++) {
for (int i = 0; i < routes[r].stationCount - 1; i++) {
int from = routes[r].stations[i];
int to = routes[r].stations[i + 1];
double segmentTime = routes[r].segmentTimes[i];
// 获取站点停靠时间
int fromIdx = findStationById(stations, stationCount, from);
int toIdx = findStationById(stations, stationCount, to);
if (fromIdx == -1 || toIdx == -1) continue;
double fromStopTime = stations[fromIdx].stopTime / 60.0;
double toStopTime = stations[toIdx].stopTime / 60.0;
// 正向边: from -> to
addEdge(graph, from, to, r, segmentTime + fromStopTime);
// 反向边: to -> from支持返程
addEdge(graph, to, from, r, segmentTime + toStopTime);
}
}
printf("图构建完成(支持双向路线)\n");
}
// Dijkstra算法
PathNode* dijkstra(Graph* graph, int start, int end, double* totalTime) {
double dist[MAX_STATIONS];
int prev[MAX_STATIONS];
int prevRoute[MAX_STATIONS];
bool visited[MAX_STATIONS] = {false};
// 初始化
for (int i = 0; i < MAX_STATIONS; i++) {
dist[i] = DBL_MAX;
prev[i] = -1;
prevRoute[i] = -1;
}
dist[start] = 0;
// Dijkstra主循环
for (int count = 0; count < graph->vertexCount; count++) {
double minDist = DBL_MAX;
int u = -1;
// 找到未访问的最小距离节点
for (int i = 0; i < MAX_STATIONS; i++) {
if (!visited[i] && dist[i] < minDist) {
minDist = dist[i];
u = i;
}
}
if (u == -1 || u == end) break;
visited[u] = true;
// 更新相邻节点
AdjNode* node = graph->adjLists[u].head;
while (node != NULL) {
int v = node->stationId;
double weight = node->travelTime;
if (!visited[v] && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
prev[v] = u;
prevRoute[v] = node->routeId;
}
node = node->next;
}
}
// 检查是否找到路径
if (dist[end] == DBL_MAX) {
*totalTime = -1;
return NULL;
}
*totalTime = dist[end];
// 重建路径
PathNode* path = NULL;
for (int v = end; v != -1; v = prev[v]) {
PathNode* newNode = (PathNode*)malloc(sizeof(PathNode));
newNode->stationId = v;
newNode->routeId = prevRoute[v];
newNode->time = dist[v];
newNode->next = path;
path = newNode;
}
return path;
}
// 释放路径
void freePath(PathNode* path) {
while (path) {
PathNode* temp = path;
path = path->next;
free(temp);
}
}
// 显示路径
void displayPath(PathNode* path, const Station stations[], int stationCount,
const BusRoute routes[], int routeCount) {
(void)routeCount; // 标记为未使用,避免警告
if (!path) return;
PathNode* curr = path;
while (curr) {
int stationIdx = findStationById(stations, stationCount, curr->stationId);
if (stationIdx != -1) {
printf(" %s", stations[stationIdx].name);
if (curr->next && curr->next->routeId != -1) {
printf(" -[%s]-> ", routes[curr->next->routeId].routeName);
} else if (!curr->next) {
printf(" (到达)");
}
printf("\n");
}
curr = curr->next;
}
}

View File

@@ -0,0 +1,170 @@
// src/main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../include/station.h"
#include "../include/route.h"
#include "../include/graph.h"
#define MAX_ROUTES 20
// 全局变量
Station stations[MAX_STATIONS];
BusRoute routes[MAX_ROUTES];
int stationCount = 0;
int routeCount = 0;
Graph* graph = NULL;
// 函数声明
void printMenu();
void queryRoute();
void saveQueryResult(const char* filename, const char* startName, const char* endName,
PathNode* path, double totalTime);
// 打印菜单
void printMenu() {
printf("\n╔════════════════════════════════╗\n");
printf("║ 巴士路线管理系统 ║\n");
printf("╠════════════════════════════════╣\n");
printf("║ 1. 查询最短路径 ║\n");
printf("║ 2. 显示所有站点 ║\n");
printf("║ 3. 显示所有线路 ║\n");
printf("║ 4. 退出系统 ║\n");
printf("╚════════════════════════════════╝\n");
printf("请选择功能 (1-4): ");
}
// 查询路线
void queryRoute() {
char startName[MAX_NAME], endName[MAX_NAME];
printf("\n请输入起点站名称: ");
scanf("%s", startName);
printf("请输入终点站名称: ");
scanf("%s", endName);
int startId = findStationByName(stations, stationCount, startName);
int endId = findStationByName(stations, stationCount, endName);
if (startId == -1) {
printf("错误: 找不到站点 '%s'\n", startName);
return;
}
if (endId == -1) {
printf("错误: 找不到站点 '%s'\n", endName);
return;
}
double totalTime;
PathNode* path = dijkstra(graph, startId, endId, &totalTime);
if (!path || totalTime < 0) {
printf("\n没有找到从 %s 到 %s 的路线\n", startName, endName);
return;
}
printf("\n========== 最短路径查询结果 ==========\n");
printf("起点: %s\n", startName);
printf("终点: %s\n", endName);
printf("总时间: %.2f 分钟\n", totalTime);
printf("\n详细路线:\n");
displayPath(path, stations, stationCount, routes, routeCount);
printf("=====================================\n\n");
// 保存结果
saveQueryResult("data/query_results.txt", startName, endName, path, totalTime);
freePath(path);
}
// 保存查询结果
void saveQueryResult(const char* filename, const char* startName, const char* endName,
PathNode* path, double totalTime) {
FILE* fp = fopen(filename, "a");
if (!fp) {
printf("警告: 无法保存查询结果到文件\n");
return;
}
fprintf(fp, "================================\n");
fprintf(fp, "起点: %s\n", startName);
fprintf(fp, "终点: %s\n", endName);
fprintf(fp, "总时间: %.2f 分钟\n", totalTime);
fprintf(fp, "路径: ");
PathNode* curr = path;
while (curr) {
int stationIdx = findStationById(stations, stationCount, curr->stationId);
if (stationIdx != -1) {
fprintf(fp, "%s", stations[stationIdx].name);
if (curr->next) {
fprintf(fp, " -> ");
}
}
curr = curr->next;
}
fprintf(fp, "\n");
fclose(fp);
}
// 主函数
int main() {
printf("========================================\n");
printf(" 巴士路线管理系统 v1.0\n");
printf("========================================\n\n");
// 加载数据
printf("正在加载系统数据...\n");
stationCount = loadStations("data/stations.csv", stations, MAX_STATIONS);
if (stationCount == 0) {
printf("错误: 无法加载站点数据\n");
return 1;
}
routeCount = loadRoutes("data/routes.csv", routes, MAX_ROUTES);
if (routeCount == 0) {
printf("错误: 无法加载线路数据\n");
return 1;
}
// 创建图
graph = createGraph(stationCount);
buildGraphFromRoutes(graph, routes, routeCount, stations, stationCount);
printf("\n系统初始化完成!\n");
// 主循环
int choice;
while (1) {
printMenu();
if (scanf("%d", &choice) != 1) {
while (getchar() != '\n');
printf("无效输入,请输入数字1-4\n");
continue;
}
switch (choice) {
case 1:
queryRoute();
break;
case 2:
displayAllStations(stations, stationCount);
break;
case 3:
displayAllRoutes(routes, routeCount, stations, stationCount);
break;
case 4:
printf("\n感谢使用巴士路线管理系统!\n");
destroyGraph(graph);
return 0;
default:
printf("无效选择,请输入1-4\n");
}
}
return 0;
}

View File

@@ -0,0 +1,83 @@
// src/route.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../include/route.h"
// 从文件加载线路信息
int loadRoutes(const char* filename, BusRoute routes[], int maxRoutes) {
FILE* fp = fopen(filename, "r");
if (!fp) {
printf("错误: 无法打开文件 %s\n", filename);
return 0;
}
int count = 0;
char line[512];
// 跳过标题行
if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
return 0;
}
while (count < maxRoutes) {
// 读取基本信息: id,routeName,stationCount
if (fscanf(fp, "%d,%49[^,],%d\n",
&routes[count].id,
routes[count].routeName,
&routes[count].stationCount) != 3) {
break;
}
// 读取站点序列
if (fgets(line, sizeof(line), fp) == NULL) break;
char* token = strtok(line, ",\n");
int idx = 0;
while (token != NULL && idx < routes[count].stationCount) {
routes[count].stations[idx++] = atoi(token);
token = strtok(NULL, ",\n");
}
// 读取各段行驶时间
if (fgets(line, sizeof(line), fp) == NULL) break;
token = strtok(line, ",\n");
idx = 0;
while (token != NULL && idx < routes[count].stationCount - 1) {
routes[count].segmentTimes[idx++] = atof(token);
token = strtok(NULL, ",\n");
}
count++;
}
fclose(fp);
printf("成功加载 %d 条线路\n", count);
return count;
}
// 显示单条线路
void displayRoute(const BusRoute* route, const Station stations[], int stationCount) {
printf("%s: ", route->routeName);
for (int i = 0; i < route->stationCount; i++) {
int stationIdx = findStationById(stations, stationCount, route->stations[i]);
if (stationIdx != -1) {
printf("%s", stations[stationIdx].name);
if (i < route->stationCount - 1) {
printf(" -> ");
}
}
}
printf("\n");
}
// 显示所有线路
void displayAllRoutes(const BusRoute routes[], int routeCount,
const Station stations[], int stationCount) {
printf("\n========== 所有线路信息 ==========\n");
for (int i = 0; i < routeCount; i++) {
displayRoute(&routes[i], stations, stationCount);
}
printf("总计: %d 条线路\n", routeCount);
printf("==================================\n");
}

View File

@@ -0,0 +1,75 @@
// src/station.c
#include <stdio.h>
#include <string.h>
#include "../include/station.h"
// 从文件加载站点信息
int loadStations(const char* filename, Station stations[], int maxStations) {
FILE* fp = fopen(filename, "r");
if (!fp) {
printf("错误: 无法打开文件 %s\n", filename);
return 0;
}
int count = 0;
char line[256];
// 跳过标题行
if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
return 0;
}
while (count < maxStations &&
fscanf(fp, "%d,%49[^,],%lf,%lf,%d\n",
&stations[count].id,
stations[count].name,
&stations[count].latitude,
&stations[count].longitude,
&stations[count].stopTime) == 5) {
count++;
}
fclose(fp);
printf("成功加载 %d 个站点\n", count);
return count;
}
// 根据名称查找站点
int findStationByName(const Station stations[], int count, const char* name) {
for (int i = 0; i < count; i++) {
if (strcmp(stations[i].name, name) == 0) {
return stations[i].id;
}
}
return -1;
}
// 根据ID查找站点索引
int findStationById(const Station stations[], int count, int id) {
for (int i = 0; i < count; i++) {
if (stations[i].id == id) {
return i;
}
}
return -1;
}
// 显示单个站点信息
void displayStation(const Station* station) {
printf(" ID: %d\n", station->id);
printf(" 名称: %s\n", station->name);
printf(" 经纬度: (%.4f, %.4f)\n", station->latitude, station->longitude);
printf(" 停靠时间: %d秒\n", station->stopTime);
}
// 显示所有站点
void displayAllStations(const Station stations[], int count) {
printf("\n========== 所有站点信息 ==========\n");
for (int i = 0; i < count; i++) {
printf("%d. %s (停靠时间: %d秒)\n",
stations[i].id, stations[i].name, stations[i].stopTime);
}
printf("总计: %d 个站点\n", count);
printf("==================================\n");
}

15
PracticalTrain/README.md Normal file
View File

@@ -0,0 +1,15 @@
<!--
Copyright (c) 2025 zhilv
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
## 实训
### 文件介绍
```sh
.
└── DataStructure # 数据结构课程设计
```

View File

@@ -11,6 +11,7 @@
```sh ```sh
. .
├── DataStructure # 数据结构 ├── DataStructure # 数据结构
├── PracticalTrain # 实训
├── Server # Node.js 从基础到项目实践 ├── Server # Node.js 从基础到项目实践
├── Spider # Python 网络爬虫基础教程 ├── Spider # Python 网络爬虫基础教程
└── Web # Web 前端开发技术 └── Web # Web 前端开发技术
@@ -21,6 +22,7 @@
- [`Node.js` 从基础到项目实践](Server/README.md) - [`Node.js` 从基础到项目实践](Server/README.md)
- [`Python` 网络爬虫基础教程](Spider/README.md) - [`Python` 网络爬虫基础教程](Spider/README.md)
- [`Web` 前端开发技术](Web/README.md) - [`Web` 前端开发技术](Web/README.md)
- [实训](PracticalTrain/README.md)
## 📜 License ## 📜 License