一、题目
假设一学生信息文件中每个学生信息包括:学号(8位字符)、姓名(10个字符)、成绩(整型)及所用时间(整型),要求从该文件中读取每个学生的信息存入一双向循环链表中,然后按快速排序算法对其排序;排序的规则是:按成绩从高到低,成绩相同时所用时间少者在先,多者在后。要求输出排序后的结果。
二、关键知识点:
1.构造双向循环链表及其操作函数:
typedef struct //存放学生信息
{
char No[9];
char Name[11];
int Score;
int time;
}Student;
typedef struct DLNode //双向循环链表结点
{
int i; //用于记录顺序,便于排序
Student stu;
DLNode*prior;
DLNode*next;
}DLNode,*DLlist;
void InitList(DLlist& L) //建立链表头
{
L = new DLNode;
L->next = L;
L->prior = L;
L->i = 0;
}
void DestroyList(DLlist& L) //销毁链表
{
delete L;
}
void ShowStuMsg(DLlist L) //打印链表数据
{
DLNode* p = L->next;
while(p!=L)
{
std::cout << p->stu.No << " " << p->stu.Name << " " << p->stu.Score << " " << p->stu.time << std::endl;
p = p->next;
}
}
void InsertDLlist(DLlist& L, Student stu) //在链表末尾插入一个结点
{
DLNode *p;
p = new DLNode;
p->next = L;
p->prior = L->prior;
L->prior->next = p;
L->prior = p;
strcpy(p->stu.No, stu.No);
strcpy(p->stu.Name, stu.Name);
p->stu.Score = stu.Score;
p->stu.time = stu.time;
p->i =( p->prior->i)+ 1;
}
2.从文件中读取信息:
使用文件的输入输出流,需要加上头文件fstream,以及名称空间std。读取文件信息用ifstream,注意使用的时候要当作类来用,先定义一个对象,给一个文件名字作为构造函数参数,然后类似cin的方式使用。循环结束的条件用eof(),当读取到文件末尾时返回1。
ifstream fin(filename);//打开文件
Student stu;
while (!fin.eof()) //读取文件存入链表直到文件结尾
{
fin >> stu.No >> stu.Name >> stu.Score >> stu.time;
InsertDLlist(L, stu);
}
fin.close();
3.快速排序算法:
首先构造一个交换结点数据的函数swap(),指针位置不变。因为直接换结点比较麻烦,所以选择交换stu的数据。
因为要从大到小排列,所以将中间值设为右边第一个值,其余操作也是与平常的相比镜像转一下,最后注意一种情况,当返回的指针指向链表末尾的结点时,要跳过,不然就会与链表的首结点排序导致出现错误。
void swap(DLNode *s, DLNode *t)//将s与t所指向的结点的数据互换。
{
Student p =s->stu; //保存s所指向的结点的数据
strcpy(s->stu.No, t->stu.No); //把t结点的数据覆盖到s上
strcpy(s->stu.Name, t->stu.Name);
s->stu.Score = t->stu.Score;
s->stu.time = t->stu.time;
strcpy(t->stu.No, p.No); //把s的数据覆盖到t上
strcpy(t->stu.Name, p.Name);
t->stu.Score = p.Score;
t->stu.time = p.time;
}
DLNode* Partition(DLNode* L,DLNode* s, DLNode* t) //将s开始t结束的序列排列成左边比中间值大,右边比中间值小的形式
{
DLNode p = *t; //将t的数据作为两边比较的中间值
DLNode *q = t; //保存t指针的位置信息
while(s->i < t->i)
{
while (s->i < t->i && s->stu.Score >= p.stu.Score) //指针往后移动,直到遇到比中间值小的数据停下
s = s->next;
while(s->i < t->i && t->stu.Score<=p.stu.Score) //指针往前移动,直到遇到比中间值大的数据停下
t = t->prior;
if (s->i != t->i) //s,t指针未相遇时将它们指向的结点互换
swap(s, t);
}
swap(q, t); //s与t相遇,将中间值换到t的位置
return t; //返回中间值的位置
}
void QuickSort(DLNode* L,DLNode* s, DLNode* t)
{
if (s->i < t->i)
{
DLNode* par = Partition(L,s,t); //排列,并返回将序列分为了两部分的中间结点指针
if(par->next!=L)
QuickSort(L,par->next, t); //将右边继续排列
if(par->prior !=L)
QuickSort(L,s,par->prior); //将左边继续排列
}
}
三、完整的代码实现:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<fstream>
#include"DLinklist.h" //存放双向循环链表操作函数
using namespace std;
int main()
{
DLlist L;
InitList(L); //创建双向循环链表
char filename[] = "StudentScore.txt";
ifstream fin(filename);//打开文件
Student stu;
while (!fin.eof()) //读取文件存入链表直到文件结尾
{
fin >> stu.No >> stu.Name >> stu.Score >> stu.time;
InsertDLlist(L, stu);
}
fin.close();
cout << "读取的学生信息:\n";
ShowStuMsg(L);
QuickSort(L,L->next, L->prior);
DLNode* p = L->next;
int flag = 1;
while (flag == 1) //将成绩相同的学生按考试时间排序
{
flag = 0; //用来标记是否有排序操作,如果排序完成了就跳出循环
while (p->next != L) //遍历链表
{
if (p->stu.Score == p->next->stu.Score) //与后一个学生成绩比较,如果相等就比较时间,如果时间长就交换一下
if (p->stu.time > p->next->stu.time)
{
flag = 1; //
swap(p, p->next);
}
p = p->next;
}
p = L->next;
}
cout << "排序后的结果:\n";
ShowStuMsg(L);
return 0;
}
运行结果: