C语言实现飞机订票系统
问题描述与题目要求
问题描述: 假定某民航有M个航次的班机,每个航次都只到达一个地方。试为该机场售票处设计一个自动订票和退票系统,要求系统具有以下功能:
(1) 订票:若该航次余票大于等于乘客订票数,则在该航次的乘客表中,插入订票乘客的信息项,并修改该航次有关数据,否则给出相应信息。
(2) 退票:若该航次当前退票数小于等于乘客原订票数,则在相应的乘客表中找到该乘客项,修改该航次及乘客表中有关数据;当某乘客由于退票使订票数为零时,则从乘客表中撤消该数据项。
要求:
(1)描述对航次表和乘客表选用的数据结构。
(2)编程实现飞机票订票和退票系统。
模型假设
1.假设所有输入均为整数且在int类型的表示范围内
2.假设航次是从1到n的连续整数
3.假设每个乘客 ID 均唯一
数据结构的选用
联想到图中的邻接链表,采用相似的数据结构描述该问题
航次表: 用一个数组flight_info_list存储每个航次的乘客表,该数组下标即为航班航次,对应元素即为该航次相关信息(乘客表,航班编号及航班余票数)
乘客表: 用双向链表存储每个航次的乘客表passenger_info_list,每个结点存储乘客的 ID,订票数以及指向前、后结点的指针
编程实现(C语言实现)
/* * @Description: 模拟航班的订票系统 * 模型假设: * 1. 飞机最大载客量为300人 * 2. 共10个航次 * 用双向链表存储乘客信息 * 用array存储航班信息 * @Author: Fishermanykx * @Date: 2019-09-29 10:32:56 * @LastEditors: Fishermanykx * @LastEditTime: 2019-09-30 12:29:16 */ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #define MAX_CAPACITY 300 // 假定飞机最大载客量为300 #define TOTAL_AIRLINE 10 // 假定不同航线最大数目为10 #define BOOK_TICKET 1 #define REFUND -1 #define EXIT_SYSTEM 0 #define PRINT_INFO 11 #define ROOT 123456 // 某航次航班 struct SingleFlight { int flight_id; // 航班编号,从1开始,到TOTAL_AIRLINE为止 int remain_tickets; // 该航班余票数 struct SinglePassenger* passenger_info_list; // 该航班乘客表 }; // 某航次航班的某个乘客的信息 struct PassengerInfo { int passenger_id; // 乘客id int ticket_number; // 该乘客购买票数 }; // 乘客表中的一个结点 struct SinglePassenger { struct PassengerInfo passenger_info; // 乘客信息 struct SinglePassenger* prev_passenger; // 指向前一个乘客的指针 struct SinglePassenger* next_passenger; // 指向后一个乘客的指针 }; typedef struct SingleFlight SingleFlight; typedef struct SinglePassenger SinglePassenger; // 订票操作 SingleFlight* BookTicket(SingleFlight flight_info_list[]); SinglePassenger* GetNewPassenger(const int new_passenger_id, const int book_ticket_number); SinglePassenger* AddNewPassenger(SinglePassenger* head, const int new_passenger_id, const int book_ticket_number); // 退票操作 SingleFlight* Refund(SingleFlight flight_info_list[]); SinglePassenger* RemovePassenger(SinglePassenger* head, const int passenger_id); // 判断操作 bool IsPassengerExist(SinglePassenger* head, const int passenger_id); // 打印操作 void PrintCurrentAirlineInfo(SingleFlight flight_info_list[]); void PrintPassengerList(SinglePassenger* head, SingleFlight* flight_info_list, int airline_id); int main(void) { int order, exit_loop = 1; // 初始化航班信息 SingleFlight* flight_info_list; flight_info_list = (SingleFlight*)malloc(TOTAL_AIRLINE * sizeof(SingleFlight)); for (int i = 0; i < TOTAL_AIRLINE; ++i) { flight_info_list[i].flight_id = i + 1; flight_info_list[i].remain_tickets = MAX_CAPACITY; flight_info_list[i].passenger_info_list = NULL; } /* 登录界面 */ printf("您好,欢迎使用此系统!\n\n"); printf("使用说明:\n"); printf("1. 本程序所有输入均为整数\n"); printf("2. 可供选择的航次编号为1-%d, 每架次最大载客量为%d\n", TOTAL_AIRLINE, MAX_CAPACITY); printf( "3. 若订票,请输入1;若退票,请输入-1;若退出系统,请输入0; " "若要以root用户登录,请输入root密码\n"); printf("使用说明到此结束,祝您使用愉快!\n"); // 判断是否以root登录 int log_in_as_root, root_key; bool is_root = false; printf("-------------------------------------------------------------\n\n"); printf("是否以root用户登录?若是,请输入1,否则请输入0:"); scanf("%d", &log_in_as_root); if (log_in_as_root) printf("请输入root密码(按0退出root登录程序):"); while (log_in_as_root) { scanf("%d", &root_key); if (!root_key) { break; } else if (root_key != ROOT) { printf("输入密码错误!请重新输入或按0退出root登录程序:"); } else { is_root = true; break; } } // 欢迎界面 if (is_root) printf("欢迎,root用户!输入11可查看当前航次表\n"); else printf("欢迎,普通用户!\n"); printf("-------------------------------------------------------------\n"); // 主循环 while (true) { if (is_root) printf("请输入1, 0, -1或11中的一个数字: "); else printf("请输入1, 0, -1中的一个数字: "); scanf("%d", &order); switch (order) { case BOOK_TICKET: flight_info_list = BookTicket(flight_info_list); break; case REFUND: flight_info_list = Refund(flight_info_list); break; case EXIT_SYSTEM: exit_loop = 0; break; case PRINT_INFO: printf( "-------------------------------------------------------------\n"); PrintCurrentAirlineInfo(flight_info_list); break; default: printf("非法输入!\n"); break; } if (!exit_loop) break; } return 0; } /** * @description: 一次订票操作的模拟 * @param {type} * flight_info_list {SingleFlight *}: 航班信息表(航次表) * @return: */ SingleFlight* BookTicket(SingleFlight flight_info_list[]) { /* 获取乘客预定航次 */ int target_airline; printf("可供选择的航次对应的编号为: 1 - %d\n", TOTAL_AIRLINE); printf("请输入您想预定的航次(输入0时退出订票程序): "); // 判断输入合法性 while (true) { scanf("%d", &target_airline); if (target_airline < 0 || target_airline > TOTAL_AIRLINE) { printf("您要预定的航次不存在!\n"); printf("请重新输入一个正确的航次,或按0退出订票程序:"); } else if (target_airline == 0) { printf("-------------------------------------------------------------\n"); return flight_info_list; } else break; } /* 获取乘客id */ int passenger_id; int modify_tickets; printf("若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: "); // 判断输入合法性 while (true) { scanf("%d", &modify_tickets); if (modify_tickets != 1 && modify_tickets != 0) { printf("您输入的是非法命令,请重新输入0(原先未订票)或1(原先已经订票):"); } else break; } printf("请输入您的ID: "); // 若原先未订票 while (!modify_tickets) { scanf("%d", &passenger_id); if (IsPassengerExist( flight_info_list[target_airline - 1].passenger_info_list, passenger_id)) { printf("该ID已存在,请输入一个新的ID: "); } else break; } // 若原先已经订票 if (modify_tickets) { scanf("%d", &passenger_id); if (!IsPassengerExist( flight_info_list[target_airline - 1].passenger_info_list, passenger_id)) { printf("您原先并未预订该航次的票!\n"); printf("-------------------------------------------------------------\n"); return flight_info_list; } } /* 获取乘客预定票数 */ // 获取当前航次余票数 int remain_tickets; remain_tickets = flight_info_list[target_airline - 1].remain_tickets; printf("当前航次余票数为: %d\n", remain_tickets); // 若该乘客想修改票数,显示此乘客此前预订的票数 if (modify_tickets) { SinglePassenger* head = flight_info_list[target_airline - 1].passenger_info_list; while (head->passenger_info.passenger_id != passenger_id) { head = head->next_passenger; } printf("您此前预订的票数为%d张\n", head->passenger_info.ticket_number); } // 获取乘客想预定的票数 int target_ticket_num; printf("请输入您想预定(或增订)的票数: "); // 判断输入合法性 while (true) { scanf("%d", &target_ticket_num); if (target_ticket_num > remain_tickets) { printf("您想预定的票数为%d, 但当前航次余票数仅为%d, 余票不足!\n", target_ticket_num, remain_tickets); printf("请输入您想预定的票数,或按0退出订票程序: "); } else if (target_ticket_num == 0) { printf("-------------------------------------------------------------\n"); return flight_info_list; } else { break; } } /* 修改航次余票数 */ flight_info_list[target_airline - 1].remain_tickets -= target_ticket_num; /* 修改乘客表中对应的项 */ // 判断该乘客原先是否存在 if (modify_tickets) { // 若存在,找到该乘客并修改他的订票项 SinglePassenger* tmp = flight_info_list[target_airline - 1].passenger_info_list; while (tmp->passenger_info.passenger_id != passenger_id) { tmp = tmp->next_passenger; } tmp->passenger_info.ticket_number += target_ticket_num; printf("增订成功!您现在共预订%d张航次%d的票\n", tmp->passenger_info.ticket_number, target_airline); } else { // 若不存在,则在该航次的乘客列表中增加该乘客及其对应信息 flight_info_list[target_airline - 1].passenger_info_list = AddNewPassenger( flight_info_list[target_airline - 1].passenger_info_list, passenger_id, target_ticket_num); printf("预订成功!您现在共预订%d张航次%d的票\n", target_ticket_num, target_airline); } printf("-------------------------------------------------------------\n"); return flight_info_list; } /** * @description: 查找乘客表(双向链表)中某乘客是否存在 * @param {type} * head {SinglePassenger*}: 双向链表头结点 * passenger_id {const int}: 待查找的键值 * @return: 若存在,返回true;否则返回false */ bool IsPassengerExist(SinglePassenger* head, const int passenger_id) { SinglePassenger* tmp = head; bool exist = false; if (!head) { return false; } while (tmp) { if (tmp->passenger_info.passenger_id == passenger_id) { exist = true; break; } tmp = tmp->next_passenger; } return exist; } /** * @description: 初始化一个新结点 * @param {type} * new_passenger_id {const int}: 新增加的乘客的id * book_ticket_number {const int}: 新增加乘客的订票数 * @return: 初始化后的结点(前驱,后继均为空指针) */ SinglePassenger* GetNewPassenger(const int new_passenger_id, const int book_ticket_number) { SinglePassenger* new_passenger = (SinglePassenger*)malloc(sizeof(SinglePassenger)); new_passenger->passenger_info.passenger_id = new_passenger_id; new_passenger->passenger_info.ticket_number = book_ticket_number; new_passenger->next_passenger = NULL; new_passenger->prev_passenger = NULL; return new_passenger; } SinglePassenger* AddNewPassenger(SinglePassenger* head, const int new_passenger_id, const int book_ticket_number) { SinglePassenger* new_passenger = GetNewPassenger(new_passenger_id, book_ticket_number); if (!head) { head = new_passenger; } else { // 直接从头部插入 new_passenger->next_passenger = head->next_passenger; if (head->next_passenger) { head->next_passenger->prev_passenger = new_passenger; } new_passenger->prev_passenger = head; head->next_passenger = new_passenger; } return head; } /** * @description: 一次退票操作的模拟 * @param {type} * flight_info_list {SingleFlight *}: 航次表 * @return: 修改后的航次表 */ SingleFlight* Refund(SingleFlight flight_info_list[]) { /* 获取乘客预定航次 */ int target_airline; printf("可供选择的航次对应的编号为: 1 - %d\n", TOTAL_AIRLINE); printf("请输入您想退订的航次(输入0时退出订票程序): "); // 判断输入合法性 while (true) { scanf("%d", &target_airline); if (target_airline < 0 || target_airline > TOTAL_AIRLINE) { printf("您要退订的航次不存在!\n"); printf("请重新输入一个正确的航次,或按0退出退票程序:"); } else if (target_airline == 0) { printf("-------------------------------------------------------------\n"); return flight_info_list; } else break; } /* 获取乘客ID并判断其合法性 */ int passenger_id; printf("请输入您的ID: "); scanf("%d", &passenger_id); SinglePassenger* head = flight_info_list[target_airline - 1].passenger_info_list; if (!IsPassengerExist(head, passenger_id)) { printf("您并未预订此次航班!\n"); printf("-------------------------------------------------------------\n"); return flight_info_list; } /* 获取乘客退票数 */ // 打印此乘客的预订票数 SinglePassenger* tmp = head; while (tmp->passenger_info.passenger_id != passenger_id) { tmp = tmp->next_passenger; } printf("您当前预订的票数为: %d张\n", tmp->passenger_info.ticket_number); // 读入退票数 int refund_ticket_num; printf("请输入您的退票数(输入0退出退票程序): "); scanf("%d", &refund_ticket_num); // 输入合法性检查 int cur_ticket = tmp->passenger_info.ticket_number; // 当前该乘客预订的票数 while (cur_ticket < refund_ticket_num) { if (!refund_ticket_num) { printf("-------------------------------------------------------------\n"); return flight_info_list; } printf("您输入的退票数大于您当前预订的票数!"); printf("请重新输入退票数(输入0退出退票程序): "); scanf("%d", &refund_ticket_num); } /* 退票 */ // 更新航次表 flight_info_list[target_airline - 1].remain_tickets += refund_ticket_num; // 更新乘客表 if (cur_ticket > refund_ticket_num) { tmp->passenger_info.ticket_number -= refund_ticket_num; printf("您已成功退票,现在您%d航次的余票为%d张\n", target_airline, tmp->passenger_info.ticket_number); } else { flight_info_list[target_airline - 1].passenger_info_list = RemovePassenger(head, passenger_id); printf("您已成功退票,现在您%d航次的余票为%d张\n", target_airline, 0); } printf("-------------------------------------------------------------\n"); return flight_info_list; } /** * @description: 从乘客表中删除某个结点 * @param {type} * head {SinglePassenger *}: 乘客表 * passenger_id {const int}: 待删除乘客的id * @return: 修改后的航次表 */ SinglePassenger* RemovePassenger(SinglePassenger* head, const int passenger_id) { SinglePassenger* tmp = head; while (tmp->passenger_info.passenger_id != passenger_id) { tmp = tmp->next_passenger; } // 若为头结点 if (!tmp->prev_passenger) { head = head->next_passenger; } // 若为尾结点 else if (!tmp->next_passenger) { tmp->prev_passenger->next_passenger = NULL; } // 若为中间某个结点 else { tmp->prev_passenger->next_passenger = tmp->next_passenger; tmp->next_passenger->prev_passenger = tmp->prev_passenger; } return head; } /** * @description: 输出当前航次表 * @param {type} * flight_info_list {SingleFlight *}: 航班信息表(航次表) * @return: void */ void PrintCurrentAirlineInfo(SingleFlight flight_info_list[]) { for (int current_airline_index = 1; current_airline_index <= TOTAL_AIRLINE; ++current_airline_index) { int remain_ticket_num = flight_info_list[current_airline_index - 1].remain_tickets; SinglePassenger* head = flight_info_list[current_airline_index - 1].passenger_info_list; // 输出 PrintPassengerList(head, flight_info_list, current_airline_index); printf("-------------------------------------------------------------\n"); } } /** * @description: 打印某航次的乘客表 * @param {type} * head {SinglePassenger*}: 乘客表的头结点 * flight_info_list {SingleFlight *}: 航次表 * @return: */ void PrintPassengerList(SinglePassenger* head, SingleFlight* flight_info_list, int airline_id) { if (!head) { printf("%d航次无乘客订票!\n", airline_id); } else { printf("%d航次余票数为: %d, 其中:\n", airline_id, flight_info_list[airline_id - 1].remain_tickets); } while (head) { printf("ID为%d的乘客订票数为%d张\n", head->passenger_info.passenger_id, head->passenger_info.ticket_number); head = head->next_passenger; } printf("\n"); }
实现亮点
1.每一步操作均有对非法输入的处理,最大限度上确保了程序运行的稳定性
2.区分root用户和普通用户,且只有root用户能够查看所有人的订票情况,从而保护了客户的隐私
3.使用双向链表存储乘客信息,一方面便于存取乘客的信息(定长数组分配的是栈内存,而栈内存小于堆内存,所以用链表进行存储更不容易造成内存溢出),另一方面降低了编程难度(既不需要实现对数组进行动态内存分配的一系列操作,又最大限度地降低了删除结点操作的复杂度)
实现缺点
1.只考虑了数字输入的情况,没有考虑字符及其他数据类型的输入
2.用链表存储导致不能随机访问,使查找操作复杂度始终为O(n) O(n)O(n)
运行结果
您好,欢迎使用此系统! 使用说明: 1. 本程序所有输入均为整数 2. 可供选择的航次编号为1-10, 每架次最大载客量为300 3. 若订票,请输入1;若退票,请输入-1;若退出系统,请输入0; 若要以root用户登录,请输入root密码 使用说明到此结束,祝您使用愉快! ------------------------------------------------------------- 是否以root用户登录?若是,请输入1,否则请输入0:1 请输入root密码(按0退出root登录程序):123456 欢迎,root用户!输入11可查看当前航次表 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 1 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 0 请输入您的ID: 1 当前航次余票数为: 300 请输入您想预定(或增订)的票数: 12 预订成功!您现在共预订12张航次1的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 1 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 0 请输入您的ID: 2 当前航次余票数为: 288 请输入您想预定(或增订)的票数: 21 预订成功!您现在共预订21张航次1的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 1 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 0 请输入您的ID: 3 当前航次余票数为: 267 请输入您想预定(或增订)的票数: 32 预订成功!您现在共预订32张航次1的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 2 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 0 请输入您的ID: 32 当前航次余票数为: 300 请输入您想预定(或增订)的票数: 2 预订成功!您现在共预订2张航次2的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 10 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 0 请输入您的ID: 212 当前航次余票数为: 300 请输入您想预定(或增订)的票数: 123 预订成功!您现在共预订123张航次10的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 1 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 1 请输入您的ID: 1 当前航次余票数为: 235 您此前预订的票数为12张 请输入您想预定(或增订)的票数: -1 增订成功!您现在共预订11张航次1的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: -1 可供选择的航次对应的编号为: 1 - 10 请输入您想退订的航次(输入0时退出订票程序): 1 请输入您的ID: 2 您当前预订的票数为: 21张 请输入您的退票数(输入0退出退票程序): 222 您输入的退票数大于您当前预订的票数!请重新输入退票数(输入0退出退票程序): 2 您已成功退票,现在您1航次的余票为19张 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 1 可供选择的航次对应的编号为: 1 - 10 请输入您想预定的航次(输入0时退出订票程序): 10 若您原先已经订票,且想增加您的订票数,请输入1,否则请输入0: 32 您输入的是非法命令,请重新输入0(原先未订票)或1(原先已经订票):0 请输入您的ID: 322 当前航次余票数为: 177 请输入您想预定(或增订)的票数: 12 预订成功!您现在共预订12张航次10的票 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: -1 可供选择的航次对应的编号为: 1 - 10 请输入您想退订的航次(输入0时退出订票程序): 10 请输入您的ID: 212 您当前预订的票数为: 123张 请输入您的退票数(输入0退出退票程序): 123 您已成功退票,现在您10航次的余票为0张 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 11 ------------------------------------------------------------- 1航次余票数为: 238, 其中: ID为1的乘客订票数为11张 ID为3的乘客订票数为32张 ID为2的乘客订票数为19张 ------------------------------------------------------------- 2航次余票数为: 298, 其中: ID为32的乘客订票数为2张 ------------------------------------------------------------- 3航次无乘客订票! ------------------------------------------------------------- 4航次无乘客订票! ------------------------------------------------------------- 5航次无乘客订票! ------------------------------------------------------------- 6航次无乘客订票! ------------------------------------------------------------- 7航次无乘客订票! ------------------------------------------------------------- 8航次无乘客订票! ------------------------------------------------------------- 9航次无乘客订票! ------------------------------------------------------------- 10航次余票数为: 288, 其中: ID为322的乘客订票数为12张 ------------------------------------------------------------- 请输入1, 0, -1或11中的一个数字: 0
注:普通用户不能执行查看所有乘客信息的操作,只能查看自己的购票信息
更多学习资料请关注专题《管理系统开发》。