数据结构_航空客运订票系统(C实现)
文章目录
总述
在这里插入代码片 1. 问题描述:(题目复述) 题目复述如下:使用缩进使要求直观. (一)试设计一个航空客运定票系统。基本要求如下: 1. 设计数据结构 a) 每条航线所涉及的信息有: i. 终点站名、 ii. 航班号、 iii. 飞机号、 iv. 飞机周日(星期几)、 v. 乘员定额、 vi. 余票量、 b) 订定票的客户名单(包括 i. 姓名、 ii. 订票量、(显然取决于客户的需求) iii. 舱位等级1,2或3) c) 等候替补的客户名单(包括 iv. 姓名、 v. 所需数量)。 2. 数据结构的基础上设计算法,使系统能实现的操作和功能如下: 1) 查询航线:根据客户提出的终点站名输出如下信息: i. 航班号、 ii. 飞机号、 iii. 星期几飞行, iv. 最近一天航班的日期和 v. 余票额; 2) 承办订票业务:根据客户提出的要求(航班号、订票数额)查询该航班票额情况, i. 若有余票,则为客户办理订票手续,输出座位号; ii. 若已满员或余票少余订票额,则需重新询问客户要求。 a) 若需要,可登记排队候补; 3) 承办退票业务:根据客户提出的情况(日期、航班号),为客户办理退票手续, i. 然后查询该航班是否有人排队候补,首先询问排在第一的客户,若所退票额能满足他的要求,则为他办理订票手续, ii. 否则依次询问其它排队候补的客户。 2. 设计: (1) 数据结构设计和核心算法设计描述; 主要描述程序采用的逻辑结构(线性表、队列、堆栈、树、图)和存储结构(顺序存储和链式存储),以及核心算法设计(比如Kruskal算法、Prim算法、Dijkstra、Floyd算法) 1.本程序主要采用了 单链表+链队列的逻辑结构;且都是基于链式存储的存储结构; /* 订定票的客户链表结点结构 */ typedef struct LNode_Cer { char name[MAXNAME];/*客户名字(订票的时候输入)名字的排序建议用插入排序以保持有序*/ int num_tickets;/*订票量*/ int rank;/*仓位等级class是保留字,用rank*/ LNode_Cer* link;/*指针域*/ }LNode_Cer;/*link node of Certain */ /*航线链表的结点结构*/ typedef struct LFlightNode { char name_des[MAX];/*终点站名*/ char flight_num[MAX];/*航班号:航班号某时某刻从某地飞往另一地的 航班航程的编号*/ char plane_num[MAX];/*飞机号:飞机号相当于飞机的身份证*/ int plane_weekday;/*星期几的航班*/ int plane_crew;/*乘员定额(最大乘客量)*/ int remaining_tickets;/*剩余票量*/ /*三种等级仓位的价格int price[3]*/ /*此外,可考虑还能够查出该航线由哪些乘客以及候补乘客 LNode_Cer* customerName; LNode_Cer* candidateName;*/ LFlightNode* next; }LFlightNode; /*等候替补客户链队列节点*//*与顺序队列相比,就是队列结构里把数组用链表(头指针)替代(链表外置),虚拟指针用真正的指针替代*/ typedef struct LQueueNode_Can { /*数据域(不同场景下条目可多可少,类型可以多样)*/ char name[MAXNAME];/*候补客户名字*/ int num_tickets;/*该客户需要的票数*/ char flightNo[MAX];/*候补乘客想要的航班*/ /*附加的指针域*/ LQueueNode_Can* link; }LQueueNode_Can;/*can指candidate的缩写*/ /*等待候补客户链队列整体结构*/ typedef struct LQueue_Can { LQueueNode_Can* front; LQueueNode_Can* rear; }LQueue_Can; 2.涉及的算法有:基于链表的主关键字排序;链队列的以及链表的增加记录+删除记录+检索记录. (2) 主控及功能模块层次结构; (4) 功能模块之间的调用与被调用关系等。 4. 使用说明和作业小结: (1) 使用说明主要描述如何使用你的程序以及使用时的主要事项; 1.约定:本程序里的名字是指是个人 名字+身份ID号综合体 的简称(比如 chaoxin_1375,是具有唯一性的主关键词 2.本程序所指的座舱等级(1,2,3)是在飞机中额外提供的服务的品质,与坐位坐垫无关.(当然与实际生活中的标准不相同) 3.本程序生成座位号只生成第一张票的座位号(如果该乘客订了多张票,那么就默认是连续的号.(与实际生活的座位号有别) (2) 在小结中说明程序的改进思想、经验和体会; 1.由于要求实现的功能较多,在安排主函数时应注意将各模块的功能分配到各个函数群中,是主函数更简洁,逻辑更清晰. 一上来就急于实现数据结构的基本操作使我感到吃力而且低效 2. 建立全局变量(在不至于混淆的情况下可以减少参数的传递); 在本实验中,纠正了我对全局变量传参的错误认识. 3.对带头结点的链表进行排序操作比较统一,得益于表头结点的使用;此外在使用同一算法对不带头结点的链表进行关键字排序时,用化归思想,补偿一个临时头结点给不带头结点的链表,待排序结束后,修正表头指针,并释放后期加设的头结点实现排序.;链队列比循环队列要更加灵活,虽然定义结构体稍加复杂,单实际基本操作却很方便. 4.编写本程序时,由于涉及的功能比较多,尤其是对异常处理的编写占了较多篇幅,刚开始写的时候比较担心自己能否组织好各个模块,花了较多时间在调试上,比较考验耐心.同时,我也收获了不少,尤其是对vs这个IDE的使用更加熟练,调试较长代码的经验使我受益匪浅. 5.本程序参照生活中的场景作了简化处理,留心生活中的各种应用,与写好程序不无关系.
3. 测试
测试范例,测试结果,测试结果的分析与讨论,测试过程中遇到的主要问题及所采用的解决措施。
///以下实例测试包括测试错误处理的可用性,但其中多出使用getch()来读出用户的功能选项,(输入没有显示在屏幕上,而是直接确认选项,比如: “输入y/Y确定继续”“按键1,2,3,4,5直接进入功能”)
代码
粗糙版
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #include <ctype.h> #include <conio.h> /*getch()所在头文件是conio.h。而不是stdio.h*/ #define MAXLEN 100 #define MAX 60 #define MAXNAME 10 /* run this #define N 5/*航班号数量*/ /* 订定票的乘客链表结点结构 */ typedef struct LNode_Cer { char name[MAXNAME];/*乘客名字(订票的时候输入)名字的排序可以用插入的方式以保持有序*/ int num_tickets;/*订票量*/ int rank;/*仓位 等级class是保留字,用rank*/ LNode_Cer* link;/*指针域*/ }LNode_Cer;/*link node of Certain */ /*航线链表的结点结构*/ typedef struct LFlightNode { char name_des[MAX];/*终点站名*/ char flight_num[MAX];/*航班号:航班号某时某刻从某地飞往另一地的 航班航程的编号*/ char plane_num[MAX];/*飞机号:飞机号相当于飞机的身份证*/ int plane_weekday;/*星期几的航班*/ int plane_crew;/*乘员定额(最大乘客量)*/ int remaining_tickets;/*剩余票量*/ /*三种等级仓位的价格int price[3]*/ /*此外,可考虑还能够查出该航线由哪些乘客以及候补乘客 LNode_Cer* customerName; LNode_Cer* candidateName;*/ LFlightNode* next; }LFlightNode; /*等候替补乘客链队列节点*//*与顺序队列相比,就是队列结构里把数组用链表(头指针)替代(链表外置),虚拟指针用真正的指针替代*/ typedef struct LQueueNode_Can { /*数据域(不同场景下条目可多可少,类型可以多样)*/ char name[MAXNAME];/*候补乘客名字*/ int num_tickets;/*该乘客需要的票数*/ /**/ char flightNo[MAX];/*候补乘客想要的航班*/ /*附加的指针域*/ LQueueNode_Can* link; }LQueueNode_Can;/*candidate*/ /*等待候补乘客链队列整体结构*/ typedef struct LQueue_Can { // LQueueNode_Can LQueue[MAXLEN]; LQueueNode_Can* front; LQueueNode_Can* rear; }LQueue_Can; /*建立全局变量(在不至于混淆的情况下可以减少参数的传递)*/ /*任何函数都可访问到全局变量(不需要通过传参即可之间从内部访问)*/ LFlightNode* Head = NULL;/*第一个航班结点,初始化为NULL*/ LFlightNode* p2;/*航班辅助指针(指向表尾结点)(如果不设立该全局变量,要使用表尾时也可以同从头遍历找到表尾*/ LNode_Cer* LHead = 0;/*乘客链表表头表头*/ LNode_Cer* pLnow = 0;/*乘客链表表尾指针*/ LQueue_Can LQ = { 0,0 };/*//这里的LQ不设置为指针,而是一个 链队列 类型的 变量, 会分配一个LQueue_Can类型变量大小的内存给LQ.(内部含有两个指针的变量类型),不方便直接初始赋值*/ /*编写自定义基本操作函数时*/ /*假设函数的功能都能够实现,内容的具体实现待逻辑框架设计好后再作填充编写(函数原型的调整) (如果某个预想函数不易一步实现,则届时可以拆分实现(复合实现)还原到研究问题的本质上*/ /*f初始化链表(可以让它构建一个头结点并返回节点的指针创建链表(可以考虑将他写为复合函数(包含插入函数))*/ /*初始化订定票乘客链表(建一个头结点,返回头结点指针)*/ //LNode_Cer* InitLNode_Cer()/*//无参有返回值带出结果的函数版本(也可以使有参(间接传参保存结果)的版本)*/ //{ // LNode_Cer* LNode;/*只分配了一个指针的空间*/ // LNode = (LNode_Cer*)malloc(sizeof(LNode_Cer));/*申请一个节点的空间*/ // if (!LNode) // { // printf("malloc failed!\n"); // exit(1); // } // LNode->name[0] = 0; // LNode->num_tickets = 0; // LNode->rank = 0; // LNode->link = NULL; // return LNode; //} /*初始化链队列(申请一个表头结点,并且将处理结果保存再LQ中) void InitLQueue_Can(LQueue_Can* LQ ) { LQ->front = LQ->rear = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); if (!LQ->front) { exit(1); } LQ->front->link = 0; } */ /*备用的基本操作函数算法*/ /*插入新结点到队列尾*/ //void EnQueue(/**/LQueue_Can* LQ, char* name, int num_tickets) //{ // /*LQ->rear->link = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can));//直接挂钩新结点 // scanf("%s", LQ->rear->name); // LQ->rear = LQ->rear->link;*/ // /*如果引入辅助指针s,可以使得表达式更简洁(尽管步骤肯多点.*/ // LQueueNode_Can* s = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); // if (!s) exit(1); // strcpy(s->name, name); // s->num_tickets = num_tickets; // s->link = 0; // // LQ->rear->link = s; // LQ->rear = s; //} /*初始化队列节点 LQueueNode_Can* InitLQueueNode_Can() { LQueueNode_Can* LQNode; LQNode = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); LQNode->name[0] = '\0'; LQNode->num_tickets = 0; LQNode->link = NULL; return LQNode; }*/ /*插入新元素到链表尾*/ //void InsertToLinkTail(LNode_Cer* pHead) //{ // LNode_Cer* LNode = (LNode_Cer*)malloc(sizeof(LNode_Cer)); // if (!LNode) // { // printf("malloc failed!\n"); // exit(1); // } // /*新结点内容写入*/ // scanf("%s", LNode->name); // scanf("%d", &LNode->num_tickets); // scanf("%d", &LNode->rank); // /*找到链表尾部*/ // while (pHead->link) // { // pHead = pHead->link; // }/*指向当前表尾元素*/ // /*插入新节点*/ // pHead->link = LNode; //} /*检查输入的航班号是否早已被占用,返会 是/否*/ int is_aready_have(LFlightNode* Head, char* flight_num) { LFlightNode* p1 = Head; while (p1) { if (!strcmp(p1->flight_num, flight_num)) { return 1; } p1 = p1->next; } return 0; } /*f录入航班号(在停止之前一直录入) 需要注意的的是,航班号必须唯一,若发现重复录入,应给予提示:*/ void input_flights()/*line_add*/ { LFlightNode* p1; LFlightNode* ptemp;/*保存新申请的结点内存的指针*/ //p1 = Head; p2 = Head; char temp_flight_name[MAX];/*暂存输入的航班号名*/ char c;/*接收是否继续录入的指令*/ while (1) { printf("继续执行录入操作?(按y/Y以外的键退出.)\n"); c = getch(); if ((c != 'y' && c != 'Y')) { break; } /*创建并写入头结点*/ if (Head == NULL) { p2 = (LFlightNode*)malloc(sizeof(LFlightNode)); p2->next = NULL; Head = p2;/*获得表头结点(这一步只需执行一次)*/ } else { p2 = p2->next = (LFlightNode*)malloc(sizeof(LFlightNode));/*shen'qing申请+插入到表尾+更新表尾结点指针*/ //p2 = p2->next;/*gengxin更新表尾指针*/ p2->next = NULL; /*这样直接赋值引发问题就是,p2被初始化为与Head一样地指向头结点,而头结点 的指针域指向NULL,p2->next作为右值时就是NULL;p2->next作为左值时, 是为了修改表头结点的指针域; */ /*采用辅助指针ptemp的版本*/ //ptemp = (LFlightNode*)malloc(sizeof(LFlightNode)); //if (!ptemp) exit(1); //ptemp->next = NULL;/*初始化新结点的指针域*/ //p2->next = ptemp;/*接入新结点*/ ///*再更新表尾指针的对象*/ //p2 = ptemp; } /*向新结点写入数据*/ printf("\n输入终点站名:"); scanf("%s", p2->name_des); /*测试链表连通性,暂时屏蔽一些内容的填充:*/ printf("\n请输入唯一的航班号:"); while (1) { /*scanf("%s", p2->flight_num);如果直接写入链表,那is_aready_have()就总返回已占用,所有要用临时变量暂存输入.*/ scanf("%s", temp_flight_name); if (is_aready_have(Head, temp_flight_name)) { printf("\n该航班号已被占用,请使用新的编号:"); } else { strcpy(p2->flight_num, temp_flight_name); break; } } printf("\n输入飞机号:"); scanf("%s", p2->plane_num); printf("\n输入出发出发日期(周几):"); scanf("%d", &p2->plane_weekday);/*取成员->优先于取地址&*/ printf("\n输入乘员定额:"); scanf("%d", &p2->plane_crew); p2->remaining_tickets = p2->plane_crew;/*初始化余票额*/ // scanf("%d", &p2->remaining_tickets);/*剩余票量由乘员定额-已被订购数决定*/ }//while }//input_flights()//目前读入功能正常 /**/ /*根据用户输入的终点站名,输出:航班号+飞机号+星期几飞行+最近一天的航班日期(额外开个函数,内部调用)+余票额*/ void print_flight_search()//line_search() 根据终点站名查找相关元素的详情. { void print_closest_flight(int departure); char des_name[MAX];//临时接收一个名字字符串. LFlightNode* p1 = Head;/*p1用于遍历链表结点,一般被初始化为Head,表头开始遍历*/ if (Head == NULL) { printf("\n\t 尚未录入任何航班数据,无法提供有效查询!"); //getch();/*等待用户确认提示后按任意键返回;(也可直接返回)(提示信息已打印) return; }/*若已经录入一定信息*/ printf("\n\t 请输入要查询的终点站名:"); scanf("%s", des_name); printf("\n\t 查询结果:\n"); int is_has = 0;/*标识是否找到符合条件的航班*/ while (p1) { if (!strcmp(p1->name_des, des_name)) { is_has++; printf("终点站名:%s\n航班号:%s\n飞机型号:%s\n星期:%d\n余票量:%d\n", p1->name_des, p1->flight_num, p1->plane_num, p1->plane_weekday, p1->remaining_tickets); } p1 = p1->next; }//while if (!is_has) printf("\n\tsorry,没有该航班"); else { printf("\n\t输入要出发的日期(星期几),查询最近一天且未过期的航班:"); int departure; scanf("%d", &departure); print_closest_flight(departure); } return; } /*配合print_search_ret()根据输入的终点打印最近一天的航班日期(当天即之后) 然而最小值并不一定只有一个!(二次遍历找到全部最小值*/ void print_closest_flight(int departure) { LFlightNode* p1 = Head; LFlightNode* nearest_flight = p1; int delta = 0; int delta_min = 7; while (p1) { if (p1->plane_weekday >= departure)/*筛选本周未过期的航班*/ { /* delta = p1->plane_weekday - departure > 0 ? p1->plane_weekday - departure :departure - p1->plane_weekday ; delta = p1->plane_weekday - departure; if (delta < 0) { delta = -1 * delta; } */ /*找到最近的将来航班p.*/ delta = p1->plane_weekday - departure;//必>=0 if (delta < delta_min) { delta_min = delta; nearest_flight = p1;/*保存当前为止的最近航班指针*/ } }//if /* else {//可忽略. printf("\n此航班已过期,应该输入今天及之后的出发日期"); }*/ p1 = p1->next; }//while /*输出所有满足条件的最近hangban(航班)*/ p1 = Head;/*复位*/ while (p1) { if (p1->plane_weekday == nearest_flight->plane_weekday) { printf("航班:%s\n日期:%d\n", p1->flight_num, p1->plane_weekday); } p1 = p1->next; } } /*航班查看(所有航班)函数 */ int Line_See_all() { /*声明函数*/ int Empty_Flight(); LFlightNode* p1; //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ;/*清屏*/ p1 = Head; /*打印前检查是否已录入航班信息被录入:*/ if (Empty_Flight()) return 0; printf("\n\n\t 航班信息:\n"); while (p1 != NULL) { { printf("终点站名:%s\n航班号:%s\n飞机型号:%s\n星期:%d\n余票量:%d\n\n", p1->name_des, p1->flight_num, p1->plane_num, p1->plane_weekday, p1->remaining_tickets);/*如果将整型数以%s输出会出错*/ } p1 = p1->next; } printf("\n\t 按任意键确认返回!\n"); getch(); } /*判断航班是否为空函数(菜单调转) */ int Empty_Flight() { if (Head == NULL) { //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ; printf("\n\t sorry,尚不存在航班,按任意键返回!"); getch(); return 1; } else return 0; } /*操作异常/非法提示*/ void Error_warning() { printf("\n\t遇到非法或异常操作!\n"); } /*承办业务系(订票)函数 首先查找是否有对应航班; 若有对应航班,检查是否有余票,若有,询问要订购几张票*/ /*对已定票的乘客名单按名字进行排序*/ void Lsort_names(/*LNode_Cer* LHead*/)//经调试,可正常工作.(调试排序算法,可以在两个循环处+边界收缩处各打一个断点,用一组降序数来试) { LNode_Cer* pp, * p, * q, * last, * HHead;/*外加头结点*/ /*创建头结点:*/ HHead = (LNode_Cer*)malloc(sizeof(LNode_Cer)); HHead->link = LHead; last = HHead; /*通过遍历,使得last被初始化为表尾指针*/ while (last->link) { last = last->link; } /*外层循环*/ while (last != HHead->link)/*控制排序趟数,last走到表头前,继续比较*/ { pp = HHead; p = pp->link; /*内层循环(last是右元素的右边界)*/ while (p != last)/*直到:q == last进入循环并完成比较)(即右边界参与了比较),随后p,q都再向后移动一个元素,这时要离开了,判断条件为p == last*/ { q = p->link; if (strcmp(p->name, q->name) > 0) { pp->link = q;/*1接3*/ p->link = q->link;/*2接4*/ q->link = p;/*3接2*/ /*在必要时修正被悄然前调的last*/ if (last == q) { last = p; }//if }//if pp = (strcmp(p->name, q->name) < 0) ? p : q; p = pp->link; }//while(内存) last = pp;/*last向前回溯*/ }//while(外层) LHead = HHead->link; free(HHead);/*释放掉补偿的头结点HHead*/ return; } /*询问是否参加候补+登记候补*/ void inquiry_enroll_candidate(int num_book, char* flight) { { /*加入候补队列*/ /*在本函数之外就将准备好一个初始化完毕的队列,以便随时插入.*/ printf("输入您的名字:"); if (LQ.front == 0 && LQ.rear == 0)/*链队列的队头与队尾指针同时为NULL,为空队充分但不必要条件(与具体的实现有关)*/ {/*处理队头*/ /*在入队列的过程中,为队头front赋值只执行一次即可.*/ LQ.front = LQ.rear = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); /*专门填充队头元素*/ scanf("%s", LQ.rear->name); LQ.rear->num_tickets = num_book; LQ.rear->link = 0; } else/*处理队身*/ { // EnQueue()入队列(尾) /*不用辅助指针s*/ LQ.rear->link = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can));/*申请新结点的同时+入队列(写入到当前节点的指针域)*/ LQ.rear = LQ.rear->link;/*加入新结点空间后,更新表尾指针*/ /*及时初始化节点的指针域*/ LQ.rear->link = 0; /*向新结点写入信息*/ scanf("%s", LQ.rear->name); LQ.rear->num_tickets = num_book; strcpy(LQ.rear->flightNo, flight);/*该语句中入股flight是乱七八糟的东西可以导致尾节点指针域混乱(由NULL变成其他的东西)*/ // printf("\nobserve"); /*使用辅助指针s可以时表达式更短.*/ //s = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); //if (!s) exit(1); // /*向新结点写入信息*/ // scanf("%s", s->name); //s->num_tickets = num_book; // strcpy(s->flightNo, flight);/*原本想订的航班号:*/ // /*新结点入队列*/ //LQ.rear->link = s; //LQ.rear = s;/*更新队尾指针*/ }//else/*处理队身*/ }//if(若参与候补,否则pass该乘客,询问下一位乘客) //printf("\nobserve"); return; }//inquiry_enroll_candidate /*判断用户输入的 航班号+订票数量 检查对应航班是否有余票,同时办理订票+询问候补需要*/ void booking() { /*声明函数:*/ int find_flight_route(char* flight, LFlightNode * *Psave /*若找到对应航班,则将结果写在变量Psave中*//*LFlightNode* Head Head是全局变量 无需传参即可使用*/); char c;/*接收用户选择*/ char flight[MAX]; int num_book = 0; LFlightNode* Psave = 0;/*通过函数find_flight_route(), 将找到的航班结点的指针带回(修改Psave)*/ LQueueNode_Can* s = 0; if (Empty_Flight()) { return; } printf("\n当前可以订票"); while (1) { printf("\n输入行班号:"); scanf("%s", flight); if (!find_flight_route(flight, &Psave)) { printf("sorry,没有该航班!"); } else if (Psave->remaining_tickets)/*找到该航班,检查改航班是否满员*/ { printf("尚有余票,输入您想要定的票的数量:\n"); scanf("%d", &num_book); if (num_book < Psave->remaining_tickets)/*余票chongzu充足*/ { /*满足该乘客需求,登记购票*/ printf("余票数量充足\n输入您的名字:"); /*创建并向链表头写入数据*/ if (!LHead) { pLnow = (LNode_Cer*)malloc(sizeof(LNode_Cer)); LHead = pLnow;/*保存头结点指针;pLnow 是辅助指针(而且是全局变量(可不必全局)*/ pLnow->link = 0;/*初始化新结点的指针域*/ } else/*链表身体*/ { /*直接从当前节点接收新节点内存地址.(直接挂接到表尾.)*/ pLnow->link = (LNode_Cer*)malloc(sizeof(LNode_Cer)); pLnow = pLnow->link;/*更新pLnow*/ pLnow->link = 0;/*初始化新结点的指针域*/ /*引入辅助指针+初始化新结点的指针域:*/ } /*向新节点内写入内容*/ pLnow->num_tickets = num_book; scanf("%s", pLnow->name); printf("购买几等票:"); scanf("%d", &pLnow->rank); Psave->remaining_tickets -= num_book; /*输出座位号:*/ printf("您的座位号:%d\n\n", Psave->plane_crew - Psave->remaining_tickets); }//if else/*余票不足*/ { char c;/*接收用户选择*/ char isCandidate; printf("余票不足,是否加入候补?(按y/Y)\n"); isCandidate = getch(); if (isCandidate == 'y' || isCandidate == 'Y') inquiry_enroll_candidate(num_book, flight); }//else/*余票不足*/ printf("\n是否继续订票?\n输入y继续"); c = getch(); if (c != 'y') { break; }//跳出(连续订票循环) }//if(若有余票) }//while(连续订票循环) /*订顶票乘客非空,则为乘客链表排序*/ if (LHead) //Lsort_names(LHead); /*需要纠正的观念是,如果全局变量当初本地变量传入参数的话,可能使得修改白费(优先识别为传值传参), 实在要传参,用间接传参&Head,函数声明可为Lsort_names(LNode_cer**) 函数原型可为(LNode_cer** ppnode/LHead)*/ { Lsort_names(); } }//booking() /*按航班号查找航班,若找到,返回1,并且将航班结点指针记录到Psave中; 为了带回多个/多种 结果,使用指针间接传参*/ int find_flight_route( char* flight, LFlightNode** Psave/*若找到对应航班,则将结果写在变量Psave中*/ /*LFlightNode* Head Head是全局变量 无需传参即可使用*/) { LFlightNode* p1 = Head; while (p1) { if (!strcmp(p1->flight_num, flight)) { (*Psave) = p1;/*保存找到的航班号的指针,以便修访问该结点*/ return 1;/*表示已找到*/ } p1 = p1->next; } return 0; } /*承办退票业务系函数*/ ///*判断是否有对应航班*/ //int is_find_flight(char* refund_flightNo) //{ // LFlightNode* p1 = Head; // while (p1) // { // if (strcmp(refund_flightNo, p1->flight_num)) // { // return 1; // } // p1 = p1->next; // } // return 0; //} /*根据传入的某个节点的指针,删除名单链表节点操作*/ void DeleteLNode_cer(LNode_Cer* p)/**/ { LNode_Cer* pre = LHead; /*特殊处理头结点的删除*/ if (p == LHead) { LHead = LHead->link; free(pre); return; } /*寻找pre的正确位置*/ while (pre) { if (pre->link == p)/*先找到要被删除的节点的 前驱节点pre*/ { break; } pre = pre->link;/*没找到的话继续往后找*/ } pre->link = p->link; free(p); } /*删除队头*/ void DeLQueueNode(LQueueNode_Can** pre) { LQueueNode_Can* p1 = 0; /*LQueueNode_Can* pre = LQ.front;*//*用于辅助删除队列元素(由于链队列使用的空间是即使申请/释放的, 不用考虑空间闲置的浪费问题(无需循环结构)*/ /*在候补队列中删除该名字*/ p1 = *pre; *pre = (*pre)->link;/*队头前进.*/ free(p1);/*释放旧队头节点*/ return; } /*遍历询问候补乘客,直到遇到被退票(的航班)能够满足需求/都不满足需求为止,候补成功接受则要在候补队列中删除改名字*/ void inquiry_can_satisfy(LFlightNode* Psave) { char c; printf("\n询问候补乘客,直到遇到被退票(的航班)能够满足需求/都不满足需求为止"); LQueueNode_Can* p1 = 0; LQueueNode_Can* pre = LQ.front;/*用于辅助删除队列元素(由于链队列使用的空间是即使申请/释放的, 不用考虑空间闲置的浪费问题(无需循环结构)*/ int num_book = 0; char flight[MAX]; while (pre) { printf("\n航班%s是否能够满足您的目的地?(若是,输入y,并为您办理订票):", Psave->flight_num); c = getch(); if (c == 'y') { booking(); /*在候补队列中删除该名字*/ DeLQueueNode(&LQ.front);/*注意参数写成&pre无法正确更改队头指针,当然可以尝试:DeLQueueNode(&pre);LQ.front=pre;*/ printf("\n补办完成"); return; } else { /*再次询问是否重新加入候补?*/ char c;/*接收用户选择*/; char isCandidate; printf("\n余票不足,是否重新加入候补?(按y/Y)\n"); isCandidate = getch(); if (isCandidate == 'y' || isCandidate == 'Y') { printf("\n输入您想候补的航班号:"); scanf("%s", flight); printf("\n输入您想要订的票数:"); scanf("%d", &num_book); inquiry_enroll_candidate(num_book, flight); printf("\n候补完毕!"); } /*否则删除该结点*/ DeLQueueNode(&pre); } //else(仍不满足需求). }//while(pre)遍历询问候补乘客 printf("\n未满足任何以为候补顾客的需求."); } /*根据用户提交的 航班号+日期,将对应票回收+1,删除已订票乘客名单里的名字;*/ void refund() { /*char* refund_flightNo[MAX];糊涂了*/ char refund_flightNo[MAX]; char name[MAX]; int refund_tickets_num = 0;/*保存被退的票的数量*/ LNode_Cer* p1 = LHead;/*在名单中找到乘客名字*/ LFlightNode* Psave; /*检查是否已录入航班信息,否则无法退票*/ if (Empty_Flight()) return; while (1)/*直到输入正确的可退票行班,离开循环.*/ { printf("\n\t可进行退票手续\n请输入您要退订的航班:"); scanf("%s", refund_flightNo); if (find_flight_route(refund_flightNo, &Psave))/*搜索是否有对应航班号*/ { /*约定:这里的名字是指是个人 名字+身份ID号综合体 的简称(比如 chaoxin_1375,是具有唯一性的主关键词*/ printf("\n输入您的名字:"); scanf("%s", name); printf("\n正在查找您的名字...\n"); if (!p1) { printf("\nsorry,没有该名字,请重新确认.\n"); return;/*二级菜单*/ } while (p1) { if (!strcmp(p1->name, name)) { refund_tickets_num = p1->num_tickets; /*执行删除操作(删除p1)*/ printf("执行删除操作...\n"); DeleteLNode_cer(p1); break; } p1 = p1->link; }//while(p1) printf("名字_ID 删除完毕\n"); printf("\n回收机票..."); /*回收机票*/ Psave->remaining_tickets += refund_tickets_num; break;//离开外层循环 }//if else { printf("\nsorry,没有找到该航班!请重新输入:"); } }//while(1) printf("\n询问候补乘客的需求:\n"); inquiry_can_satisfy(Psave); }//refund() /*主要复合函数*/ /*航班管理函数*/ void flightNo_manage() { char c; //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ;/*清楚屏幕内容准备当前菜单的打印*/ while (1) { printf("\n\t\t 航班管理菜单:\n"); printf("\n______________________________________________________________\n"); printf("\t 1. 添加新的航班\n"); printf("\t 2. 查询航班 \n"); printf("\t 3. 查看航班 \n"); printf("\t 4. 返回主菜单 \n"); printf("\n\n______________________________________________________________\n"); printf("\t 请选择您想要的服务(回车确定):"); scanf("%c", &c); switch (c) { case '1': input_flights(); break;/*添加新的航班*/ case '2': print_flight_search(); break;/*查询航班*/ case '3': Line_See_all(); break;/*查看航班(全部)*/ case '4': return; default: Error_warning(); }//switch() }//while } /*查看当前乘客订单信息*/ void customer_check() { char c; LNode_Cer* p1 = LHead; LQueueNode_Can* pQ = LQ.front, * pQrear = LQ.rear; printf("\n当前乘客订单信息"); printf("\n\t1.订定票乘客信息");/*xin'xi*/ printf("\n\t2.候补乘客信息"); printf("\n\t选择您要的服务:"); c = getch(); switch (c) { case '1': printf("\n订定票乘客信息\n按 名字_ID 主关键字升序排列:\n"); while (p1) { printf("\n\n乘客姓名:%s\n订票量:%d\n舱位等级:%d", p1->name, p1->num_tickets, p1->rank); p1 = p1->link; } break;/*如果不break,将总是会执行case'2'*/ case '2': printf("\n候补乘客信息:"); if (!pQ /*|| LQ.rear->link == pQ*/) { printf("\n当前无候补乘客."); } while (pQ) { printf("\n乘客姓名:%s\n订票量:%d\n", pQ->name, pQ->num_tickets); pQ = pQ->link; } break; default: break; } } /*主函数*/ int main() { char c;/*接收一个字符,进入选中的菜单项*/ do { //system("cls");//此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) printf("\n\t\t 航空客运订票主系统菜单\n"); printf("\t1.航班管理子菜单\n"); printf("\t2.订票办理子菜单\n"); printf("\t3.退票办理子菜单\n"); printf("\t4.乘客信息查看子菜单\n"); printf("\t5.退出\n"); /* printf("\n\t请选择您想要的服务并回车确定:"); scanf("%c", &c);*/ printf("\n\t\t请选择您想要的服务:"); c = getch(); /*用switch执行调用相应函数执行相应功能*/ switch (c) { /*在4个复合函数中选择*/ case '1': flightNo_manage(); break;/*航 班 管 理 (增) 菜 单*/ case '2': booking(); break;/* 订 票 办 理 (增) 菜 单*/ case '3': refund(); break;/*退 票 办 理 (增/删/查) 菜 单*/ case '4':customer_check(); break;/*/'kʌstəmər/*/ case '5': exit(0);/*退出系统*/ default:; break; } //switch } while (c != '5'); return 0; }
修理版
/* 1.设计数据结构 a) 每条航线所涉及的信息有:(填充5条航线,可以有相同的终点站但飞行日期等参数可以不同) i. 终点站名、 ii. 航班号、 iii.飞机号、 iv. 飞机周日(星期几)、 v. 乘员定额、 vi. 余票量、 b) 订定票的乘客名单(已订票乘客的线性表应按乘客姓名有序)(包括 i. 姓名、 ii. 订票量、(显然取决于乘客的需求) iii.舱位等级1,2或3) c) 等候替补的乘客名单(包括 iv. 姓名、 v. 所需数量)。 2. 数据结构的基础上设计算法,使系统能实现的操作和功能如下: 1) 查询航线:根据乘客提出的 终点站名 输出如下信息:(读入用户输入,在各条航线里的对应成员相比较,若匹配则所有输出匹配航线的各项信息.) i. 航班号、 ii. 飞机号、 iii.星期几飞行, iv. 最近一天航班的日期和(将的到的所有匹配项,且日期大等于今日的航班做一个排序,并将最小日期输出) v. 余票额; 2) 承办订票业务:根据乘客提出的要求(航班号、订票数额)查询该航班票额情况, i. 若有余票,则为乘客办理订票手续,输出座位号; ii. 若已满员或余票少余订票额,则需重新询问乘客要求。 a) 若需要,可登记排队候补; 3) 承办退票业务:根据乘客提出的情况(日期、航班号),为乘客办理退票手续, i. 然后查询该航班是否有人排队候补,首先询问排在第一的乘客,若所退票额能满足他的要求,则为他办理订票手续, ii. 否则依次询问其它排队候补的乘客。 */ /*外部问题:如何设计界功能菜单,选中并进入功能菜单? 使用printf()+switch()语句实现 scanf("%d", &a); switch (a) { case 1: ; }*/ /*约定,search()是根据关键字查询与该关键字相关的元素的各项详情(返回详情). 是根据关键字查询有元素具有该关键字(返回是/否)*/ #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #include <ctype.h> #include <conio.h> /*getch()所在头文件是conio.h。而不是stdio.h*/ #define MAXLEN 100 #define MAX 60 #define MAXNAME 10 /* run this #define N 5/*航班号数量*/ /* 订定票的乘客链表结点结构 */ typedef struct LNode_Cer { char name[MAXNAME]; /*乘客名字(订票的时候输入)名字的排序可以用插入的方式以保持有序*/ int num_tickets; /*订票量*/ int rank; /*仓位 等级class是保留字,用rank*/ LNode_Cer *link; /*指针域*/ } LNode_Cer; /*link node of Certain */ /*航线链表的结点结构*/ typedef struct LFlightNode { char name_des[MAX]; /*终点站名*/ char flight_num[MAX]; /*航班号:航班号某时某刻从某地飞往另一地的 航班航程的编号*/ char plane_num[MAX]; /*飞机号:飞机号相当于飞机的身份证*/ int plane_weekday; /*星期几的航班*/ int plane_crew; /*乘员定额(最大乘客量)*/ int remaining_tickets; /*剩余票量*/ /*三种等级仓位的价格int price[3]*/ /*此外,可考虑还能够查出该航线由哪些乘客以及候补乘客 LNode_Cer* customerName; LNode_Cer* candidateName;*/ LFlightNode *next; } LFlightNode; /*等候替补乘客链队列节点*/ /*与顺序队列相比,就是队列结构里把数组用链表(头指针)替代(链表外置),虚拟指针用真正的指针替代*/ typedef struct LQueueNode_Can { /*数据域(不同场景下条目可多可少,类型可以多样)*/ char name[MAXNAME]; /*候补乘客名字*/ int num_tickets; /*该乘客需要的票数*/ /**/ char flightNo[MAX]; /*候补乘客想要的航班*/ /*附加的指针域*/ LQueueNode_Can *link; } LQueueNode_Can; /*candidate*/ /*等待候补乘客链队列整体结构*/ typedef struct LQueue_Can { // LQueueNode_Can LQueue[MAXLEN]; LQueueNode_Can *front; LQueueNode_Can *rear; } LQueue_Can; /*建立全局变量(在不至于混淆的情况下可以减少参数的传递)*/ /*任何函数都可访问到全局变量(不需要通过传参即可之间从内部访问)*/ LFlightNode *Head = NULL; /*第一个航班结点,初始化为NULL*/ LFlightNode *p2; /*航班辅助指针(指向表尾结点)(如果不设立该全局变量,要使用表尾时也可以同从头遍历找到表尾*/ LNode_Cer *LHead = 0; /*乘客链表表头表头*/ LNode_Cer *pLnow = 0; /*乘客链表表尾指针*/ LQueue_Can LQ = {0, 0}; /*//这里的LQ不设置为指针,而是一个 链队列 类型的 变量, 会分配一个LQueue_Can类型变量大小的内存给LQ.(内部含有两个指针的变量类型),不方便直接初始赋值*/ /*检查输入的航班号是否早已被占用,返会 是/否*/ int is_already_have(LFlightNode *Head, char *flight_num) { LFlightNode *p1 = Head; while (p1) { if (!strcmp(p1->flight_num, flight_num)) { return 1; } p1 = p1->next; } return 0; } /*f录入航班号(在停止之前一直录入) 需要注意的的是,航班号必须唯一,若发现重复录入,应给予提示:*/ void input_flights() /*line_add*/ { LFlightNode *p1; LFlightNode *ptemp; /*保存新申请的结点内存的指针*/ //p1 = Head; p2 = Head; char temp_flight_name[MAX]; /*暂存输入的航班号名*/ char c; /*接收是否继续录入的指令*/ while (1) { printf("继续执行录入操作?(按y/Y以外的键退出.)\n"); c = getch(); if ((c != 'y' && c != 'Y')) { break; } /*创建并写入头结点*/ if (Head == NULL) { p2 = (LFlightNode *)malloc(sizeof(LFlightNode)); p2->next = NULL; Head = p2; /*获得表头结点(这一步只需执行一次)*/ } else { p2 = p2->next = (LFlightNode *)malloc(sizeof(LFlightNode)); /*shen'qing申请+插入到表尾+更新表尾结点指针*/ //p2 = p2->next;/*gengxin更新表尾指针*/ p2->next = NULL; /*这样直接赋值引发问题就是,p2被初始化为与Head一样地指向头结点,而头结点 的指针域指向NULL,p2->next作为右值时就是NULL;p2->next作为左值时, 是为了修改表头结点的指针域; */ /*采用辅助指针ptemp的版本*/ //ptemp = (LFlightNode*)malloc(sizeof(LFlightNode)); //if (!ptemp) exit(1); //ptemp->next = NULL;/*初始化新结点的指针域*/ //p2->next = ptemp;/*接入新结点*/ ///*再更新表尾指针的对象*/ //p2 = ptemp; } /*向新结点写入数据*/ printf("\n输入终点站名:"); scanf("%s", p2->name_des); /*测试链表连通性,暂时屏蔽一些内容的填充:*/ printf("\n请输入唯一的航班号:"); while (1) { /*scanf("%s", p2->flight_num);如果直接写入链表,那is_already_have()就总返回已占用,所有要用临时变量暂存输入.*/ scanf("%s", temp_flight_name); if (is_already_have(Head, temp_flight_name)) { printf("\n该航班号已被占用,请使用新的编号:"); } else { strcpy(p2->flight_num, temp_flight_name); break; } } printf("\n输入飞机号:"); scanf("%s", p2->plane_num); printf("\n输入出发出发日期(周几):"); scanf("%d", &p2->plane_weekday); /*取成员->优先于取地址&*/ printf("\n输入乘员定额:"); scanf("%d", &p2->plane_crew); p2->remaining_tickets = p2->plane_crew; /*初始化余票额*/ // scanf("%d", &p2->remaining_tickets);/*剩余票量由乘员定额-已被订购数决定*/ } //while } //input_flights()//目前读入功能正常 /**/ /*根据用户输入的终点站名,输出:航班号+飞机号+星期几飞行+最近一天的航班日期(额外开个函数,内部调用)+余票额*/ void print_flight_search() //line_search() 根据终点站名查找相关元素的详情. { void print_closest_flight(int departure); char des_name[MAX]; //临时接收一个名字字符串. LFlightNode *p1 = Head; /*p1用于遍历链表结点,一般被初始化为Head,表头开始遍历*/ if (Head == NULL) { printf("\n\t 尚未录入任何航班数据,无法提供有效查询!"); //getch();/*等待用户确认提示后按任意键返回;(也可直接返回)(提示信息已打印) return; } /*若已经录入一定信息*/ printf("\n\t 请输入要查询的终点站名:"); scanf("%s", des_name); printf("\n\t 查询结果:\n"); int is_has = 0; /*标识是否找到符合条件的航班*/ while (p1) { if (!strcmp(p1->name_des, des_name)) { is_has++; printf("终点站名:%s\n航班号:%s\n飞机型号:%s\n星期:%d\n余票量:%d\n", p1->name_des, p1->flight_num, p1->plane_num, p1->plane_weekday, p1->remaining_tickets); } p1 = p1->next; } //while if (!is_has) printf("\n\tsorry,没有该航班"); else { printf("\n\t输入要出发的日期(星期几),查询最近一天且未过期的航班:"); int departure; scanf("%d", &departure); print_closest_flight(departure); } return; } /*配合print_search_ret()根据输入的终点打印最近一天的航班日期(当天即之后) 然而最小值并不一定只有一个!(二次遍历找到全部最小值*/ void print_closest_flight(int departure) { LFlightNode *p1 = Head; LFlightNode *nearest_flight = p1; int delta = 0; int delta_min = 7; while (p1) { if (p1->plane_weekday >= departure) /*筛选本周未过期的航班*/ { /* delta = p1->plane_weekday - departure > 0 ? p1->plane_weekday - departure :departure - p1->plane_weekday ; delta = p1->plane_weekday - departure; if (delta < 0) { delta = -1 * delta; } */ /*找到最近的将来航班p.*/ delta = p1->plane_weekday - departure; //必>=0 if (delta < delta_min) { delta_min = delta; nearest_flight = p1; /*保存当前为止的最近航班指针*/ } } //if /* else {//可忽略. printf("\n此航班已过期,应该输入今天及之后的出发日期"); }*/ p1 = p1->next; } //while /*输出所有满足条件的最近hangban(航班)*/ p1 = Head; /*复位*/ while (p1) { if (p1->plane_weekday == nearest_flight->plane_weekday) { printf("航班:%s\n日期:%d\n", p1->flight_num, p1->plane_weekday); } p1 = p1->next; } } /*航班查看(所有航班)函数 */ int Line_See_all() { /*声明函数*/ int Empty_Flight(); LFlightNode *p1; //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ;/*清屏*/ p1 = Head; /*打印前检查是否已录入航班信息被录入:*/ if (Empty_Flight()) return 0; printf("\n\n\t 航班信息:\n"); while (p1 != NULL) { { printf("终点站名:%s\n航班号:%s\n飞机型号:%s\n星期:%d\n余票量:%d\n\n", p1->name_des, p1->flight_num, p1->plane_num, p1->plane_weekday, p1->remaining_tickets); /*如果将整型数以%s输出会出错*/ } p1 = p1->next; } printf("\n\t 按任意键确认返回!\n"); getch(); } /*判断航班是否为空函数(菜单调转) */ int Empty_Flight() { if (Head == NULL) { //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ; printf("\n\t sorry,尚不存在航班,按任意键返回!"); getch(); return 1; } else return 0; } /*操作异常/非法提示*/ void Error_warning() { printf("\n\t遇到非法或异常操作!\n"); } /*承办业务系(订票)函数 首先查找是否有对应航班; 若有对应航班,检查是否有余票,若有,询问要订购几张票*/ /*对已定票的乘客名单按名字进行排序*/ void Lsort_names(/*LNode_Cer* LHead*/) //经调试,可正常工作.(调试排序算法,可以在两个循环处+边界收缩处各打一个断点,用一组降序数来试) { LNode_Cer *pp, *p, *q, *last, *HHead; /*外加头结点*/ /*创建头结点:*/ HHead = (LNode_Cer *)malloc(sizeof(LNode_Cer)); HHead->link = LHead; last = HHead; /*通过遍历,使得last被初始化为表尾指针*/ while (last->link) { last = last->link; } /*外层循环*/ while (last != HHead->link) /*控制排序趟数,last走到表头前,继续比较*/ { pp = HHead; p = pp->link; /*内层循环(last是右元素的右边界)*/ while (p != last) /*直到:q == last进入循环并完成比较)(即右边界参与了比较),随后p,q都再向后移动一个元素,这时要离开了,判断条件为p == last*/ { q = p->link; if (strcmp(p->name, q->name) > 0) { pp->link = q; /*1接3*/ p->link = q->link; /*2接4*/ q->link = p; /*3接2*/ /*在必要时修正被悄然前调的last*/ if (last == q) { last = p; } //if } //if pp = (strcmp(p->name, q->name) < 0) ? p : q; p = pp->link; } //while(内存) last = pp; /*last向前回溯*/ } //while(外层) LHead = HHead->link; free(HHead); /*释放掉补偿的头结点HHead*/ return; } /*询问是否参加候补+登记候补*/ void inquiry_enroll_candidate(int num_book, char *flight) { { /*加入候补队列*/ /*在本函数之外就将准备好一个初始化完毕的队列,以便随时插入.*/ printf("输入您的名字:"); if (LQ.front == 0 && LQ.rear == 0) /*链队列的队头与队尾指针同时为NULL,为空队充分但不必要条件(与具体的实现有关)*/ { /*处理队头*/ /*在入队列的过程中,为队头front赋值只执行一次即可.*/ LQ.front = LQ.rear = (LQueueNode_Can *)malloc(sizeof(LQueueNode_Can)); /*专门填充队头元素*/ scanf("%s", LQ.rear->name); LQ.rear->num_tickets = num_book; LQ.rear->link = 0; } else /*处理队身*/ { // EnQueue()入队列(尾) /*不用辅助指针s*/ LQ.rear->link = (LQueueNode_Can *)malloc(sizeof(LQueueNode_Can)); /*申请新结点的同时+入队列(写入到当前节点的指针域)*/ LQ.rear = LQ.rear->link; /*加入新结点空间后,更新表尾指针*/ /*及时初始化节点的指针域*/ LQ.rear->link = 0; /*向新结点写入信息*/ scanf("%s", LQ.rear->name); LQ.rear->num_tickets = num_book; strcpy(LQ.rear->flightNo, flight); /*该语句中入股flight是乱七八糟的东西可以导致尾节点指针域混乱(由NULL变成其他的东西)*/ // printf("\nobserve"); /*使用辅助指针s可以使表达式更短.*/ //s = (LQueueNode_Can*)malloc(sizeof(LQueueNode_Can)); //if (!s) exit(1); // /*向新结点写入信息*/ // scanf("%s", s->name); //s->num_tickets = num_book; // strcpy(s->flightNo, flight);/*原本想订的航班号:*/ // /*新结点入队列*/ //LQ.rear->link = s; //LQ.rear = s;/*更新队尾指针*/ } //else/*处理队身*/ } //if(若参与候补,否则pass该乘客,询问下一位乘客) //printf("\nobserve"); return; } //inquiry_enroll_candidate /*判断用户输入的 航班号+订票数量 检查对应航班是否有余票,同时办理订票+询问候补需要*/ void booking() { /*声明函数:*/ int find_flight_route(char *flight, LFlightNode **Psave /*若找到对应航班,则将结果写在变量Psave中*/ /*LFlightNode* Head Head是全局变量 无需传参即可使用*/); char c; /*接收用户选择*/ char flight[MAX]; int num_book = 0; LFlightNode *Psave = 0; /*通过函数find_flight_route(), 将找到的航班结点的指针带回(修改Psave)*/ LQueueNode_Can *s = 0; if (Empty_Flight()) { return; } printf("\n当前可以订票"); while (1) { printf("\n输入行班号:"); scanf("%s", flight); if (!find_flight_route(flight, &Psave)) { printf("sorry,没有该航班!"); } else if (Psave->remaining_tickets) /*找到该航班,检查改航班是否满员*/ { printf("尚有余票,输入您想要定的票的数量:\n"); scanf("%d", &num_book); if (num_book < Psave->remaining_tickets) /*余票chongzu充足*/ { /*满足该乘客需求,登记购票*/ printf("余票数量充足\n输入您的名字:"); /*创建并向链表头写入数据*/ if (!LHead) { pLnow = (LNode_Cer *)malloc(sizeof(LNode_Cer)); LHead = pLnow; /*保存头结点指针;pLnow 是辅助指针(而且是全局变量(可不必全局)*/ pLnow->link = 0; /*初始化新结点的指针域*/ } else /*链表身体*/ { /*直接从当前节点接收新节点内存地址.(直接挂接到表尾.)*/ pLnow->link = (LNode_Cer *)malloc(sizeof(LNode_Cer)); pLnow = pLnow->link; /*更新pLnow*/ pLnow->link = 0; /*初始化新结点的指针域*/ /*引入辅助指针+初始化新结点的指针域:*/ } /*向新节点内写入内容*/ pLnow->num_tickets = num_book; scanf("%s", pLnow->name); printf("购买几等票:"); scanf("%d", &pLnow->rank); Psave->remaining_tickets -= num_book; /*输出座位号:*/ printf("您的座位号:%d\n\n", Psave->plane_crew - Psave->remaining_tickets); } //if else /*余票不足*/ { char c; /*接收用户选择*/ char isCandidate; printf("余票不足,是否加入候补?(按y/Y)\n"); isCandidate = getch(); if (isCandidate == 'y' || isCandidate == 'Y') inquiry_enroll_candidate(num_book, flight); } //else/*余票不足*/ printf("\n是否继续订票?\n输入y继续"); c = getch(); if (c != 'y') { break; } //跳出(连续订票循环) } //if(若有余票) } //while(连续订票循环) /*订顶票乘客非空,则为乘客链表排序*/ if (LHead) //Lsort_names(LHead); /*需要纠正的观念是,如果全局变量当初本地变量传入参数的话,可能使得修改白费(优先识别为传值传参), 实在要传参,用间接传参&Head,函数声明可为Lsort_names(LNode_cer**) 函数原型可为(LNode_cer** ppnode/LHead)*/ { Lsort_names(); } } //booking() /*按航班号查找航班,若找到,返回1,并且将航班结点指针记录到Psave中; 为了带回多个/多种 结果,使用指针间接传参*/ int find_flight_route( char *flight, LFlightNode **Psave /*若找到对应航班,则将结果写在变量Psave中*/ /*LFlightNode* Head Head是全局变量 无需传参即可使用*/) { LFlightNode *p1 = Head; while (p1) { if (!strcmp(p1->flight_num, flight)) { (*Psave) = p1; /*保存找到的航班号的指针,以便修访问该结点*/ return 1; /*表示已找到*/ } p1 = p1->next; } return 0; } /*承办退票业务系函数*/ /*根据传入的某个节点的指针,删除名单链表节点操作*/ void DeleteLNode_cer(LNode_Cer *p) /**/ { LNode_Cer *pre = LHead; /*特殊处理头结点的删除*/ if (p == LHead) { LHead = LHead->link; free(pre); return; } /*寻找pre的正确位置*/ while (pre) { if (pre->link == p) /*先找到要被删除的节点的 前驱节点pre*/ { break; } pre = pre->link; /*没找到的话继续往后找*/ } pre->link = p->link; free(p); } /*删除队头*/ void DeLQueueNode(LQueueNode_Can **pre) { LQueueNode_Can *p1 = 0; /*LQueueNode_Can* pre = LQ.front;*/ /*用于辅助删除队列元素(由于链队列使用的空间是即使申请/释放的, 不用考虑空间闲置的浪费问题(无需循环结构)*/ /*在候补队列中删除该名字*/ p1 = *pre; *pre = (*pre)->link; /*队头前进.*/ free(p1); /*释放旧队头节点*/ return; } /*遍历询问候补乘客,直到遇到被退票(的航班)能够满足需求/都不满足需求为止,候补成功接受则要在候补队列中删除改名字*/ void inquiry_can_satisfy(LFlightNode *Psave) { char c; printf("\n询问候补乘客,直到遇到被退票(的航班)能够满足需求/都不满足需求为止"); LQueueNode_Can *p1 = 0; LQueueNode_Can *pre = LQ.front; /*用于辅助删除队列元素(由于链队列使用的空间是即使申请/释放的, 不用考虑空间闲置的浪费问题(无需循环结构)*/ int num_book = 0; char flight[MAX]; while (pre) { printf("\n航班%s是否能够满足您的目的地?(若是,输入y,并为您办理订票):", Psave->flight_num); c = getch(); if (c == 'y') { booking(); /*在候补队列中删除该名字*/ DeLQueueNode(&LQ.front); /*注意参数写成&pre无法正确更改队头指针,当然可以尝试:DeLQueueNode(&pre);LQ.front=pre;*/ printf("\n补办完成"); return; } else { /*再次询问是否重新加入候补?*/ char c; /*接收用户选择*/ ; char isCandidate; printf("\n余票不足,是否重新加入候补?(按y/Y)\n"); isCandidate = getch(); if (isCandidate == 'y' || isCandidate == 'Y') { printf("\n输入您想候补的航班号:"); scanf("%s", flight); printf("\n输入您想要订的票数:"); scanf("%d", &num_book); inquiry_enroll_candidate(num_book, flight); printf("\n候补完毕!"); } /*否则删除该结点*/ DeLQueueNode(&pre); } //else(仍不满足需求). } //while(pre)遍历询问候补乘客 printf("\n未满足任何以为候补顾客的需求."); } /*根据用户提交的 航班号+日期,将对应票回收+1,删除已订票乘客名单里的名字;*/ void refund() { /*char* refund_flightNo[MAX];糊涂了*/ char refund_flightNo[MAX]; char name[MAX]; int refund_tickets_num = 0; /*保存被退的票的数量*/ LNode_Cer *p1 = LHead; /*在名单中找到乘客名字*/ LFlightNode *Psave; /*检查是否已录入航班信息,否则无法退票*/ if (Empty_Flight()) return; while (1) /*直到输入正确的可退票行班,离开循环.*/ { printf("\n\t可进行退票手续\n请输入您要退订的航班:"); scanf("%s", refund_flightNo); if (find_flight_route(refund_flightNo, &Psave)) /*搜索是否有对应航班号*/ { /*约定:这里的名字是指是个人 名字+身份ID号综合体 的简称(比如 chaoxin_1375,是具有唯一性的主关键词*/ printf("\n输入您的名字:"); scanf("%s", name); printf("\n正在查找您的名字...\n"); if (!p1) { printf("\nsorry,没有该名字,请重新确认.\n"); return; /*二级菜单*/ } while (p1) { if (!strcmp(p1->name, name)) { refund_tickets_num = p1->num_tickets; /*执行删除操作(删除p1)*/ printf("执行删除操作...\n"); DeleteLNode_cer(p1); break; } p1 = p1->link; } //while(p1) printf("名字_ID 删除完毕\n"); printf("\n回收机票..."); /*回收机票*/ Psave->remaining_tickets += refund_tickets_num; break; //离开外层循环 } //if else { printf("\nsorry,没有找到该航班!请重新输入:"); } } //while(1) printf("\n询问候补乘客的需求:\n"); inquiry_can_satisfy(Psave); } //refund() /*主要复合函数*/ /*航班管理函数*/ void flightNo_manage() { char c; //system("cls");此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) ;/*清楚屏幕内容准备当前菜单的打印*/ while (1) { printf("\n\t\t 航班管理菜单:\n"); printf("\n______________________________________________________________\n"); printf("\t 1. 添加新的航班\n"); printf("\t 2. 查询航班 \n"); printf("\t 3. 查看航班 \n"); printf("\t 4. 返回主菜单 \n"); printf("\n\n______________________________________________________________\n"); printf("\t 请选择您想要的服务(回车确定):"); scanf("%c", &c); switch (c) { case '1': input_flights(); break; /*添加新的航班*/ case '2': print_flight_search(); break; /*查询航班*/ case '3': Line_See_all(); break; /*查看航班(全部)*/ case '4': return; default: Error_warning(); } //switch() } //while } /*查看当前乘客订单信息*/ void customer_check() { char c; LNode_Cer *p1 = LHead; LQueueNode_Can *pQ = LQ.front, *pQrear = LQ.rear; printf("\n当前乘客订单信息"); printf("\n\t1.订定票乘客信息"); /*xin'xi*/ printf("\n\t2.候补乘客信息"); printf("\n\t选择您要的服务:"); c = getch(); switch (c) { case '1': printf("\n订定票乘客信息\n按 名字_ID 主关键字升序排列:\n"); while (p1) { printf("\n\n乘客姓名:%s\n订票量:%d\n舱位等级:%d", p1->name, p1->num_tickets, p1->rank); p1 = p1->link; } break; /*如果不break,将总是会执行case'2'*/ case '2': printf("\n候补乘客信息:"); if (!pQ /*|| LQ.rear->link == pQ*/) { printf("\n当前无候补乘客."); } while (pQ) { printf("\n乘客姓名:%s\n订票量:%d\n", pQ->name, pQ->num_tickets); pQ = pQ->link; } break; default: break; } } /*主函数*/ int main() { char c; /*接收一个字符,进入选中的菜单项*/ do { //system("cls");//此处cls函数导致屏幕打印混乱.(vs才可下正常运行.) printf("\n\t\t 航空客运订票主系统菜单\n"); printf("\t1.航班管理子菜单\n"); printf("\t2.订票办理子菜单\n"); printf("\t3.退票办理子菜单\n"); printf("\t4.乘客信息查看子菜单\n"); printf("\t5.退出\n"); /* printf("\n\t请选择您想要的服务并回车确定:"); scanf("%c", &c);*/ printf("\n\t\t请选择您想要的服务:"); c = getch(); /*用switch执行调用相应函数执行相应功能*/ switch (c) { /*在4个复合函数中选择*/ case '1': flightNo_manage(); break; /*航 班 管 理 (增) 菜 单*/ case '2': booking(); break; /* 订 票 办 理 (增) 菜 单*/ case '3': refund(); break; /*退 票 办 理 (增/删/查) 菜 单*/ case '4': customer_check(); break; /*/'kʌstəmər/*/ case '5': exit(0); /*退出系统*/ default:; break; } //switch } while (c != '5'); return 0; }
还可以修正的点:
余票不足时仍然提示还剩下几张(而不是直接拒绝该用户的订票操作)
(考虑到用户可能会先让一部分人买到票)
对于购票者的id 不单单是说约定一个可以不重复的主键(命名规则),而且还要辅以必要的检查违约功能
(就比如说,社会约定大家不犯罪,但是还是有个别人会犯罪,这就需要提供强制性检查并处分(拒接非法行为的执行),(就像航班号的重名检查并给出具体反馈)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2023-07-31 c++函数返回值类型是否为引用的比较
2022-07-31 java_查询从控制台输入的字符的ASCII码值(每行一个,-1结束)