C++中CList实操作---C++双向链表来实现学生管理系统【Final_version*(੭*ˊᵕˋ)੭*ଘ】

C++中CList实操作---C++双向链表实现

  • 特色:
  1.        重点学习了MFC自带库 CList类
  2.     重新学习了三种排序算法:【表内】冒泡、选择、【表外】选择
  3.        链表模板,即承载数据类型是结构体
  4.        更加完善的人性化思考---比如重复输入、重复删除、按照内容查询等
  • 问题:【已解决】
  1.        表外打印函数的重新编制、形式参数的引用
  2.        字符串无法显示的问题---空格是一个办法
  3.        链表数据添加---Check时特别注意调转脑筋

   

  1 #include "stdafx.h"
  2 #include<conio.h>
  3 #include<string.h>
  4 #include "Student.h"
  5 typedef struct SUser{//typedef定义之后DATA这个别名才有意义,否者直接结构体 名
  6     int m_Num;         //学号数据
  7     char m_Name[20];   //姓名数据
  8     float m_Math;      //成绩数据
  9 }DATA;//结构体的别名  每个结构体含有28个字节
 10 CList<DATA> m_list;
 11 
 12 CStudent::CStudent()
 13 {
 14 }
 15 
 16 
 17 CStudent::~CStudent()
 18 {
 19 }
 20 
 21 
 22 // 开始启动
 23 void CStudent::Start()
 24 {
 25     system("color 1F");
 26     Welcome_string();
 27     system("pause");
 28     system("cls");
 29     Load();
 30     while (Menu()){
 31 
 32     }
 33 }
 34 
 35 
 36 // 菜单选项
 37 int CStudent::Menu()
 38 {
 39      
 40     puts("                1】浏览所有信息");//puts专门输出字符
 41     puts("                2】添加信息");
 42     puts("                3】删除信息");
 43     puts("                4】修改信息");
 44     puts("                5】插入信息");
 45     puts("                6】查找信息");
 46     puts("                7】颜色设置");
 47     puts("                0】退出");
 48     printf("              请选择: ");
 49     int i = 0;
 50     scanf_s("%d", &i);
 51     switch (i){
 52     case 1:
 53         DisplayMenu();
 54         break;
 55     case 2:
 56         while (AddData()){};
 57         break;
 58     case 3:
 59         while (DeleteData()){};
 60         break;
 61     case 4:
 62         Modify();
 63         break;
 64     case 5:
 65         InsertData();        
 66         break;
 67     case 6:
 68         Find();
 69         break;
 70     case 7:
 71         Color_Set();
 72         break;
 73     }
 74     return i;
 75 }
 76 
 77 
 78 // 学习CList类添加数据
 79 int CStudent::AddData()
 80 {//整体流程: 1、查重检测 2、保存链表 3、打印显示 4、循环输入
 81     DATA d;
 82     /*do{
 83         cout << "请输入学号: ";
 84         cin >> d.m_Num;
 85         if (Check(d.m_Num))
 86             cout << "学号已存在,请重新上输入: ";
 87         else
 88             break;
 89     } while (true);*/
 90     while (true){
 91         cout << "请输入学号: ";
 92         cin >> d.m_Num;
 93         if (Check(d.m_Num))
 94             cout << "学号已存在,请重新上输入: ";
 95         else
 96             break;
 97     } 
 98     cout << "请输入姓名和成绩: ";
 99     cin >> d.m_Name >> d.m_Math;
100     m_list.AddTail(d);
101     //3、保存链表
102     Save();
103     //4、打印显示
104     Print();
105     cout << "是否继续添加: ";
106     //此处没有这一句,while直接就接受不到信号 跳出了
107     fflush(stdin);//清空输入缓冲区,通常是为了确保不影响后面的数据读取
108     char c = getchar();
109     //双引号里面的是字符串 而单引号里面的代表字符 ASCII
110     return c == 'y' || c == 'Y';//是否结束while循环
111                                 //如果不打y 或 Y字符就离开这个循环
112     
113     
114 }
115 
116 
117 // 保存链表数据到本地
118 void CStudent::Save()//注意:保存是发生在adddata之后,不会出现没有数据情况下的保存
119 {   
120     //1、写入打开 2、写入文件 3、关闭文件
121     //写入文件:将链表数据依次写入文件中
122     POSITION save_list;
123     FILE *p; int i = 0;
124     p = fopen("学生成绩管理系统.txt", "w");
125     if (!p){
126         puts("\n保存链表时出错!\n");
127         _getch();//临时停顿函数
128         return;
129     }
130     save_list = m_list.GetHeadPosition();
131     while (save_list){//不为0时才存储,否则全部存储就浪费
132             fwrite(&m_list.GetAt(save_list), 1, sizeof(DATA), p);//将什么,以什么样的方式写入哪里的文件
133             //取一个地址存下去,存到p中
134             //每次写一块,总共写多少块
135             /*fprintf(p, "%d %s %f", &g_user[i]);*/
136         m_list.GetNext(save_list);
137     }
138     fclose(p);
139 }
140 
141 
142 // 打印显示链表数据
143 void CStudent::Print()
144 {
145     cout << endl;
146     cout << "=====================" << endl;
147     cout << "学号" << "\t" << "姓名" << "\t" << "成绩" << endl;
148     int i = 0, num = 0;
149     POSITION print_list;
150     print_list = m_list.GetHeadPosition();
151     while (print_list){//0.1f只取一位
152         if (m_list.GetAt(print_list).m_Num > 0){//将-1屏蔽 不打印 =0 停止退出循环
153             cout << m_list.GetAt(print_list).m_Num << "\t" << m_list.GetAt(print_list).m_Name << "\t" << m_list.GetAt(print_list).m_Math << endl;
154         }
155         m_list.GetNext(print_list);//这里很重要,必须是先+后赋值
156 
157     }
158     
159     cout << "当前共用:" << m_list.GetCount() << "条记录" << endl;
160     cout << "=====================" << endl << endl;
161     
162 }
163 
164 // 新打印函数
165 void CStudent::PrintP(POSITION * p)
166 {
167     int i = 0;
168     cout << "=====================" << endl;
169     cout << "学号\t姓名\t成绩" << endl;
170     while (p[i]){
171         DATA t = m_list.GetAt(p[i]);
172         cout << t.m_Num << '\t' << t.m_Name << '\t' << t.m_Math << endl;
173         ++i;
174     }
175     cout << "当前共用:" << m_list.GetCount() << "条记录" << endl;
176     cout << "=====================" << endl << endl;
177 }
178 
179 
180 // 欢迎字符串界面
181 void CStudent::Welcome_string()
182 {
183     cout << "  \t\t\t┏┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┓  " << endl;
184     cout << "  \t\t\t┃  **************************************************************  ┃  " << endl;
185     cout << "  \t\t\t┃***┏┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┓***┃  " << endl;
186     cout << "  \t\t\t┃***┃********************************************************┃***┃    " << endl;
187     cout << "  \t\t\t┃***┃┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┃***┃    " << endl;
188     cout << "  \t\t\t┃***┃                                                        ┃***┃    " << endl;
189     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
190     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
191     cout << "  \t\t\t┃***┃***              《欢迎使用学生管理系统》            ***┃***┃    " << endl;
192     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
193     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
194     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
195     cout << "  \t\t\t┃***┃***                     轻心御风                     ***┃***┃    " << endl;
196     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
197     cout << "  \t\t\t┃***┃***                2017.08.12|3:00pm                ***┃***┃    " << endl;
198     cout << "  \t\t\t┃***┃***                                                  ***┃***┃    " << endl;
199     cout << "  \t\t\t┃***┃                                                        ┃***┃    " << endl;
200     cout << "  \t\t\t┃***┃┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┃***┃    " << endl;
201     cout << "  \t\t\t┃***┃********************************************************┃***┃    " << endl;
202     cout << "  \t\t\t┃***┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛***┃  " << endl;
203     cout << "  \t\t\t┃  **************************************************************  ┃  " << endl;
204     cout << "  \t\t\t┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛  " << endl;
205 }
206 
207 
208 // 载入本地保存的数据
209 void CStudent::Load()
210 {
211     DATA load_data;
212     FILE *fp = fopen("学生成绩管理系统.txt", "r");
213     //防止文件不存在,出现bug
214     if (!fp){
215         puts("文件不存在!");
216         cout << endl;
217         return;
218     }
219     while (fread(&load_data, 1, sizeof(DATA), fp)==sizeof(load_data)){
220         //fread(&load_data最终数据会读入结构体中作为载体
221         m_list.AddTail(load_data);//然后将结构体中的数据依次放入栈中的链表取
222     }
223     fclose(fp);//fopen and fclose are twins in pairs
224     
225 }
226 
227 
228 // 检测添加链表数据是否有重复
229 bool CStudent::Check(int nNum)
230 {
231     POSITION pos;
232     pos = m_list.GetHeadPosition();
233     while (pos){
234         if (m_list.GetAt(pos).m_Num == nNum)
235             return true;//确有重复
236         m_list.GetNext(pos);//指针指向下一个
237     }
238     return false;
239 }
240 
241 
242 // 删除链表中数据
243 int CStudent::DeleteData()
244 {   //1、找到学号 2、删除 3、保存 4、打印 5、是否持续删除
245     Print();
246     int i=0,flag=0;
247     cout << "请输入需要删除的学号: ";
248     cin >> i;
249     POSITION pos=m_list.GetHeadPosition();
250     while (pos){
251         if (m_list.GetAt(pos).m_Num == i){
252             m_list.RemoveAt(pos);
253             flag = 1;
254             Save();
255             Print();
256             //return;//删除之后的节点就不能用了,在m_list.GetNext(pos)会崩溃
257             break;//可以用return 和 break
258         }        
259             m_list.GetNext(pos);
260     }
261     if (flag == 0){
262         cout << endl<< "*********************" << endl;
263         cout << "不存在将要删除的学号"  << endl;
264         cout << "*********************" << endl << endl;
265     }
266     
267     fflush(stdin);//清除字符 包括回车键    
268     cout << "是否继续删除: ";    
269     char c = getchar();//如果没有上面一句,c得到的信号就是回车键\n
270     //持续性删除
271     return c == 'y' ||c == 'Y';//如果打入y 或 Y继续删除while(真),其他键为假
272 }
273 
274 
275 // 显示浏览和排序
276 int CStudent::DisplayMenu()
277 {
278     Print();
279     cout << endl<< "==============="<<endl;
280     puts("1】按学号排序");//puts专门输出字符
281     puts("2】按名字排序");
282     puts("3】按成绩排序");
283     puts("0】返回主菜单");
284     cout << "==============="<<endl<<endl;
285     cout<<"请输入选项: ";
286     int i = 0;
287     cin >> i;
288     switch (i){//按0已经考虑到直接返回主菜单,防止堆栈压满情况; case 0
289     case 1:
290         Gather_Sort(i);//排序选择方法2 ---汇总的方法
291         break;
292     case 2:
293         Gather_Sort(i);
294         break;
295     case 3:
296         Gather_Sort(i);
297         break;
298     }
299     return i;
300 }
301 
302 
303 // 修改链表数据
304 void CStudent::Modify()
305 {
306     int i=0,j=0;
307     Print();
308     cout << endl<< "请输入需要修改记录的学号:";
309     cin >> i;
310     POSITION pos = m_list.GetHeadPosition();
311     while (pos){
312         if (m_list.GetAt(pos).m_Num == i){
313             cout << "请选择需要修改内容:" << endl;
314             cout << "============" << endl;
315             cout << "1> 修改学号" << endl;
316             cout << "2> 修改姓名" << endl;
317             cout << "3> 修改成绩" << endl;
318             cout << "============" << endl;
319             cin >> j;
320             switch (j){
321             case 1:
322                 cout << "请输入修改的学号: ";
323                 cin >> m_list.GetAt(pos).m_Num;
324                 break;
325             case 2:
326                 cout << "请输入修改的姓名: ";
327                 cin >> m_list.GetAt(pos).m_Name;
328                 break;
329             case 3:
330                 cout << "请输入修改的成绩: ";
331                 cin >> m_list.GetAt(pos).m_Math;
332                 break;
333             }
334         }
335         m_list.GetNext(pos);
336     }
337     Save();
338     Print();
339 }
340 
341 
342 // 查找链表数据
343 void CStudent::Find()
344 {
345     cout << "请输出需要查找的内容: ";
346     int i; char nam[20];
347     int flag = 0;
348     char k[20];
349     cin >> nam;
350     sscanf(nam, "%d", &i);
351     sscanf(nam, "%s", &k);    
352     //转换字节数组到float数据  
353     char *buffer = new char[30];
354     POSITION pos = m_list.GetHeadPosition();
355     while (pos){
356         if ((m_list.GetAt(pos).m_Math - (int)m_list.GetAt(pos).m_Math) > 0){
357             sprintf(buffer, "%0.1f", m_list.GetAt(pos).m_Math);//存在小数点
358         }
359         else{
360             sprintf(buffer, "%d", (int)m_list.GetAt(pos).m_Math);//不存在小数点
361         }
362         
363         if (m_list.GetAt(pos).m_Num == i || strcmp(m_list.GetAt(pos).m_Name, k) == 0 || strcmp(buffer,nam)==0){
364             cout << "========================" << endl;
365             cout << "学号\t姓名\t成绩" << endl;
366             cout << m_list.GetAt(pos).m_Num << '\t' << m_list.GetAt(pos).m_Name << '\t' << m_list.GetAt(pos).m_Math << endl;
367             cout << "========================" << endl;
368             flag = 1;
369         }
370 
371         m_list.GetNext(pos);
372     }
373     if (flag == 0){
374         cout <<endl<< ">>>>>>>>>>>><<<<<<<<<<<<" << endl;
375         cout << "很抱歉,未找到该条记录!" << endl;
376         cout << ">>>>>>>>>>>><<<<<<<<<<<<" << endl << endl;
377     }
378 }
379 
380 
381 // 界面颜色设置
382 void CStudent::Color_Set()
383 {
384     char bg[3];
385     char fg[3];
386     char yanse[9] = "color ";
387     puts("0 = 黑色 1 = 蓝色   2 = 绿色   3 = 湖蓝色   4 = 红色   5 = 紫色   6 = 黄色   7 = 白色 ");
388     puts("8 = 灰色 9 = 淡蓝色 A = 淡绿色 B = 淡浅绿色 C = 淡红色 D = 淡紫色 E = 淡黄色 F = 亮白色");
389     printf("\n请按照您的喜好和心情b( ̄▽ ̄)d选择背景颜色: ");
390     //===================================================
391     scanf_s("%s", bg, 2);//大小控制为1,读取到为空,插入,显示为0
392     //scanf("%s", bg); 也行
393     puts("0 = 黑色 1 = 蓝色   2 = 绿色   3 = 湖蓝色   4 = 红色   5 = 紫色   6 = 黄色   7 = 白色 ");
394     puts("8 = 灰色 9 = 淡蓝色 A = 淡绿色 B = 淡浅绿色 C = 淡红色 D = 淡紫色 E = 淡黄色 F = 亮白色");
395     printf("\n请按照您的喜好和心情b( ̄▽ ̄)d选择前景颜色: ");
396     scanf_s("%s", fg, 2);
397     //scanf("%s", fg);
398     /*printf("color 1A");
399     printf("\n%s", strcat(strcat(yanse, bg), fg));*/
400     system(strcat(strcat(yanse, bg), fg));//
401     return ;
402 }
403 
404 
405 // 插入链表数据
406 int CStudent::InsertData()
407 {
408     DATA d;
409     int i = 0, j = 0;
410     Print();
411     cout << "请问需要在哪个学号附近进行插入操作?:";    
412     cin >> i;
413     POSITION pos = m_list.GetHeadPosition();
414     while (pos){
415         if (m_list.GetAt(pos).m_Num == i){
416             cout << endl<<"==================" << endl;
417             cout << "1> 选择学号前插入" << endl;
418             cout << "2> 选择学号后插入" << endl;
419             cout << "=================" << endl;
420             cout << "请选择在该学号前或后进行插入: ";
421             cin >> j;
422             switch (j){
423             case 1:
424                 cout << "请输入学号: ";
425                 cin >> d.m_Num;
426                 cout << "请输入姓名:  ";
427                 cin >> d.m_Name;
428                 cout << "请输入成绩:  ";
429                 cin >> d.m_Math;
430                 m_list.InsertBefore(pos, d);
431                 break;
432             case 2:
433                 cout << "请输入学号: ";
434                 cin >> d.m_Num;
435                 cout << "请输入姓名:  ";
436                 cin >> d.m_Name;
437                 cout << "请输入成绩:  ";
438                 cin >> d.m_Math;
439                 m_list.InsertAfter(pos, d);
440                 break;
441             }
442         }
443         m_list.GetNext(pos);
444     }
445     Save();
446     Print();    
447     return 0;
448 }
449 
450 
451 // 综合排序策略
452 int CStudent::Gather_Sort(int i)
453 {
454     
455     switch (i){
456     case 1:
457         Sort(i);
458         break;
459     case 2:
460         Sort(i);
461         break;
462     case 3:
463         Sort(i);
464         break;
465     }
466     return 0;
467 }
468 
469 
470 // 排序算法
471 int CStudent::Sort(int i)
472 {
473     int ii = 0, j = 0, m = 0;
474     int n = m_list.GetCount();
475     POSITION *p = new POSITION [n+1];//申请堆上的指针 栈内不可以用变量定义数组
476                                     //[n+1]数组第一个就是地址 多+1代表:结尾指针
477     //int *p = new int [3];//
478     
479     POSITION pos = m_list.GetHeadPosition();
480     while (p[ii++]=pos)//将栈内链表地址传给p来记录  表外操作
481         m_list.GetNext(pos);
482     //ii = 0;
483     //while (p[ii]){//测试查看p中保存的链表地址
484     //    cout << "p[ii]: "<<p[ii] << endl;
485     //    ii++;
486     //    //1共8次记录,第九次保存的是空,退出
487     //    //保存的都是链表保存结构体的地址
488     //}
489     ii = 0;
490     while (ii < n - 1){//外循环
491         m = ii;//当前指针
492         j = ii + 1;//下一指针
493         while (j < n){//内循环  p[j]类型是POSITION
494             //一定是<n 找到的是全部数据的最小值
495             Sort_Choose2(i, p, m, j);//通过地址查看相应链表数据并进行比较
496             ++j;
497         }
498         if (m != ii){//如果表外指针已经更换了  j和m是临时变量,起调度作用
499             //表外的指针在p[]中的顺序也需要进行更换
500             POSITION t = p[m];//数组内的地址更换,后续会依次打印即可
501             p[m] = p[ii];
502             p[ii] = t;
503 
504         }
505         ii++;
506     }
507     PrintP(p);
508     Save();
509     delete []p;//释放 不删除堆空间否则内存泄露   重点    
510     system("pause");
511     return 0;
512 }
513 //选择排序                表内排序
514 //POSITION pos = m_list.GetHeadPosition();
515 //POSITION m, q;
516 //while (pos){
517 //    m = q = pos;
518 //    m_list.GetNext(q);
519 //    while (q){//选择排序做法  表内排序
520 //        Sort_Choose(i, m, q);            
521 //        m_list.GetNext(q);
522 //    }
523 //    if (m != pos){
524 //        DATA d = m_list.GetAt(m);
525 //        m_list.GetAt(m) = m_list.GetAt(pos);
526 //        m_list.GetAt(pos) = d;
527 //    }
528 //    m_list.GetNext(pos);
529 //}
530 //Print();
531 
532 //========================================================
533 //冒泡🐱o(=•ェ•=)m 排序  表内排序
534 //POSITION pos = m_list.GetHeadPosition();
535 //POSITION m, q;//找到中间变量进行更替
536 //while (pos){
537 //    m = q = pos;//m是固定下外循环  q是相对变动的
538 //    m_list.GetNext(q);//q=m->pNext;
539 //    while (q){//q是内循环
540 //        if (m_list.GetAt(m).m_Num > m_list.GetAt(q).m_Num){
541 //            //if 前>后
542 //            DATA d = m_list.GetAt(m);
543 //            m_list.GetAt(m) = m_list.GetAt(q);
544 //            m_list.GetAt(q) = d;
545 //        }
546 //        m_list.GetNext(q);
547 //    }
548 //
549 //    m_list.GetNext(pos);
550 //}
551 
552 // 排序选择
553 int CStudent::Sort_Choose(int i, POSITION &m, POSITION &q)
554 {
555     switch (i){
556     case 1:
557         if (m_list.GetAt(m).m_Num > m_list.GetAt(q).m_Num)
558             m = q;//交换地址
559         break;
560     case 2:
561         if (strcmp(m_list.GetAt(m).m_Name, m_list.GetAt(q).m_Name) > 0)
562             m = q;//交换地址
563         break;
564     case 3:
565         if (m_list.GetAt(m).m_Math > m_list.GetAt(q).m_Math)
566             m = q;//交换地址
567         break;
568     }
569     return i;
570 }
571 
572 // 排序选择2
573 int CStudent::Sort_Choose2(int i,POSITION *p,int &m,int &j)
574 {
575     switch (i){
576     case 1:
577         if (m_list.GetAt(p[m]).m_Num > m_list.GetAt(p[j]).m_Num)
578             m = j;//更换地址
579         break;
580     case 2:
581         if (strcmp(m_list.GetAt(p[m]).m_Name , m_list.GetAt(p[j]).m_Name)>0)
582             m = j;//更换地址
583         break;
584     case 3:
585         if (m_list.GetAt(p[m]).m_Math < m_list.GetAt(p[j]).m_Math)
586             m = j;//更换地址
587         break;
588     }
589     return i;
590 }
Student.cpp
 1 #pragma once
 2 #include<afxtempl.h>//含有CListl类
 3 #include<iostream>
 4 using namespace std;
 5 
 6 class CStudent//学生类
 7 {
 8 private:    
 9     // 学习CList类添加数据
10     int AddData();
11     // 保存链表数据到本地
12     void Save();
13     // 打印显示链表数据
14     void Print();
15     // 载入本地保存的数据
16     void Load();
17     // 删除链表中数据
18     int DeleteData();
19     // 显示浏览和排序
20     int DisplayMenu();
21     // 修改链表数据
22     void Modify();
23     // 查找链表数据
24     void Find();
25     // 界面颜色设置
26     void Color_Set();
27     // 插入链表数据
28     int InsertData();
29     // 综合排序策略
30     int Gather_Sort(int i);
31     // 排序算法
32     int Sort(int i);
33     // 排序选择
34     int Sort_Choose(int i, POSITION &m, POSITION &q);
35     // 排序选择2
36     int Sort_Choose2(int i, POSITION *p, int &m, int &j);
37     // 新打印函数
38     void PrintP(POSITION * p);
39     // 检测添加链表数据是否有重复
40     bool Check(int nNum);
41     // 欢迎字符串界面
42     void Welcome_string();
43 public:
44     CStudent();
45     ~CStudent();
46     // 菜单选项
47     int Menu();    
48     // 开始启动
49     void Start();
50 };
Student
 1 // 双向链表---学生成绩管理系统.cpp : 定义控制台应用程序的入口点。
 2 //
 3 
 4 #include "stdafx.h"
 5 #include"Student.h"
 6 
 7 int main()
 8 {
 9     CStudent list;    
10     list.Start();
11     return 0;
12 }
双向链表---学生成绩管理系统.cpp

  • 补充表外排序原理:

 

  

 

posted @ 2017-08-17 08:46  心沉大海-汇聚成一  阅读(750)  评论(0)    收藏  举报