C++中CList实操作---C++双向链表来实现学生管理系统【Final_version*(੭*ˊᵕˋ)੭*ଘ】
C++中CList实操作---C++双向链表实现
- 特色:
- 重点学习了MFC自带库 CList类
- 重新学习了三种排序算法:【表内】冒泡、选择、【表外】选择
- 链表模板,即承载数据类型是结构体
- 更加完善的人性化思考---比如重复输入、重复删除、按照内容查询等
- 问题:【已解决】
- 表外打印函数的重新编制、形式参数的引用
- 字符串无法显示的问题---空格是一个办法
- 链表数据添加---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 }
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 };
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 }

- 补充表外排序原理:


浙公网安备 33010602011771号