两种多关键字排序策略比较
Posted zhangstudy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两种多关键字排序策略比较相关的知识,希望对你有一定的参考价值。
两种多关键字排序策略比较
一 目的
使用LSD和MSD法的稳定的内部排序法或者"分配""收集"的方法实现学生成绩按照多关键字进行排序,实现不同单科次序下的成绩排名,并对两种方法产生的结果进行比较。学生成绩有语文,数学,英语和总分,数据以从old.txt文件中读取和随机产生并保存到new.txt文件中两种方式产生,排序式要求以总分主次序从大到小进行排序,当总分相等时,对用户要求的次序顺序进行排序,最后将按照多关键字排序的结果保存到out.txt文件中。
二 需求分析
1、排序方式分析
(1)LSD法:选择"分配"和"收集"的基数排序算法,使用单链表存储分数,从最低的次序开始排,最后排总分,排序时,依次从数的个位、十位、百位进行分配和收集,最终得到从大到小的成绩顺序;
(2)MSD法:选择稳定的内部排序中的冒泡排序算法,使用单链表存储分数,从总分开始比较,比较相邻两个数之间的大小,前一个数小于后一个数则交换两数据元素的指针指向,如果相等,则进行后一个次序的比较,最终得到从大到小的成绩顺序。
2、输入数据
(1)old.txt里的学生信息;
(2)需要从old.txt文件中读取的数据组数n(50<=n<=1000);
(3)需要随机生成的数据组数m(0<=m=1000);
(4)用户输入的排序次序。
3、输出数据
(1)随机生成的数据信息写入new.txt文件中;
(2)使用冒泡排序产生的排序结果打印到屏幕和out.txt文件中;
(3)使用基数排序产生的排序结构打印到屏幕和out.txt文件中。
4、程序需要的功能
(1)菜单界面,显示提示信息,选择数据产生的方式以及需要多少组数据;
(2)选择功能,提示用户选择哪种排序方式,以及排序次序;
(3)读取文件数据,创建单链表;
(4)MSD冒泡排序,对成绩进行排序;
(5)LSD基数排序,对成绩进行排序;
(6)打印各操作的结果到屏幕上,并把结果保存到out.txt中。
三 概要设计
1、思路
(1)调用菜单,要求用户输入产生数据的方式;
(2)选择从old.txt文件中读取数据或者随机生成数据,并输入需要多少组数据;
(3)调用单链表创建函数,从old.txt文件或者new.txt文件中读取数据并创建链表;
(4)将创建的学生成绩打印到屏幕上;
(5)调用选择函数,要求用户选择排序方式;
(6)将排序结果打印到屏幕上;
(7)将各项数据写入out.txt文件中;
(8)各项选择使用switch语句控制,功能结束或者继续操作使用do-while语句控制。
2、各函数的功能和数据含义
(1)结构体typedef struct SNode SNode,*Student:
string name;//学生姓名
int ID;//学号
int key[4];//4类成绩
struct SNode *next;//结点的指针域
(2)main函数:
调用Menu()函数,输出提示菜单;
使用do-while语句控制程序是否继续运行;
使用文件流类定义fout,把显示在屏幕上的内容输出到out.txt文件中。
(3)void StudentGrade(int n); //生成学生成绩信息文件new.txt, n为需要随机生成的数据组数。
(4)Student CreatStudent_old(Student &L,int num);//读取old.txt,尾插法建立学生成绩单链表L,num为需要读取的数据组数。
(5)Student CreatStudent_new(Student &L,int num);//读取new.txt,尾插法建立学生成绩单链表L,num为需要读取的数据组数。
(6)Student BubbleSort(Student &L,int order[]);//MSD冒泡排序,L为学生成绩链表,order[]为用户输入的次关键字顺序。
(7)Student RadixSort(Student &L,int order[]);//LSD基数排序,L为学生成绩链表,order[]为次关键字顺序
(8)void Print(Student &L);//在屏幕上显示,L为生成绩单链表
(9)void choose(Student &L);//选择功能,L为学生成绩单链表
(10)void Menu(); //菜单
四 详细设计
1、流程图
2、伪代码
(1)main函数:
Begin
定义文件输出流对象fout;
定义文件输入流对象fin;
input a;
while a!=0;
do
调用函数Menu();
END
(2)Menu()函数
Begin
定义结构体类型L;
input a;
if a=1
then input n;
调用函数CreatStudent_old(L,n);
for i=1;i<=n;i++
do fin读取文本old.txt数据;
end;
if a=2
then input m;
调用函数StudentGrade(m);
do fout输出数据到new.txt文件;
调用函数CreatStudent_new(L,m);
for i=1;i<=n;i++
do fin读取文本new.txt数据;
end;
调用print(L);????//打印信息到屏幕
end;
调用函数choose(L)????//进行排序方式选择
END
(3)choose()函数
定义整型数组order[4];
for i=0;i<=3;i++
do input order[i];
input a;
if a=1
then 调用函数BubbleSort(L,order);????//调用冒泡排序
print(L);
end;
if a=2
then 调用RadixSort(L,order);????//调用基数排序
if d=1
then m=p->key[k]%10;
if d=2
then m= (p->key[k]%100)/10;
if d=3
then m=p->key[k]/100;
end;
e[m]->next=p;
e[m]=p;
print(L)
end;
打开输出文件,将文本信息输入文本文档;
关闭输出文件;
END
(4)BubbleSort()函数
Begin
while q
if p->key[k1]<q->key[k1]
then p与q交换;
if p->key[k1]=q->key[k1]
then if p->key[k2]<q->key[k2]
then p与q交换;
if p->key[k2]=q->key[k2]
then if p->key[k3]<q->key[k3]
then p与q交换;
if p->key[k3]=q->key[k3]
then if p->key[k4]<q->key[k4]
then p与q交换;
end
(5)RadixSort()函数
Begin
if d=1
then m=p->key[k]%10;
if d=2
then m= (p->key[k]%100)/10;
if d=3
then m=p->key[k]/100;
end;
e[m]->next=p;
e[m]=p;
for i=9;i>=0;i++
t->next=f[i];
t=e[i];
return;
END
五 调试分析
1、调试环境:
(1)系统:Windows10 ;
(2)编译器:QT Creator4.4.1。
2、问题
(1)界面不友好。
1)错误分析:注重程序的可视化以及可操作性,使用户能方便操作,读懂屏幕输出的内容。
2)问题解决:添加空格和分割线等来设计界面。
(2)输入除1和2以外的数,程序无法正常运行。
1)错误分析:switch语句中未考虑除1和2以外的情形。
2)问题解决:在switch语句中加default,提示用户输入其他字符是重新输入1或2。
六 测试结果
1、操作界面
运行程序,输入1,选择第一种数据产生方式;输入50,从old.txt文件中读取50组数据,输出所读取的数据打印在屏幕上。
输入1,选择第一种排序方式,输入次序3,2,1,0,得到得结果正确。
输入2,选择第二种排序方式,输入次序3,2,1,0,得到得结果正确。
输入0,结束此次排序。
输入2,选择第二钟数据产生方式,输入50,随机产生50组数据并保存到new.txt文件钟,并从中读取数据,打印在屏幕上。
输入1,选择第一钟排序方式,输入次序3,2,1,0,得到得结果正确。
输入2,选择第二种排序方式,输入次序3,2,1,0,得到得结果正确。
输入0,结束此次排序。
2、文本文件及保存结果
打开old.txt,查看数据。
打开new.txt,查看数据。
打开out.txt,查看文本结果。
3、测试说明
使用第一种产生数据方式,从old.txt文件中读取数据,屏幕上打印出得数据与old.txt文件中得数据一致,使用两种排序方式产生的排序结果相同,且正常保存到out.txt文件中;使用第二种产生数据方式,随机产生数据,并保存到new.txt文件中,从new.txt文件中读取数据,打印在屏幕上与new.txt文件数据一致,使用两种排序产生的排序结果相同,且正常保存到out.txt文件中。整个测试过程中程序正常运行,且结果正确。
七 用户使用说明
1、功能说明
该程序为两种多关键字排序策略的比较,通过使用LSD法的基数排序和MSD法的冒泡排序对学生成绩进行排序,学生成绩数据产生的方式有两种:第一种是从old.txt中读取,第二种是随机产生数据并保存到new.txt文件中,程序使用两种排序方式对这些数据从大到小进行排序,并将结果等记录保存到out.txt文件中。
2、操作步骤
(1)打开程序并运行。
(2)界面出现程序说明,以及数据产生选项。
(3)输入相应的数字进行选择。
(4)输入1,按回车键,选择从old.txt文件中读取数据,界面提示读取多少组数据。
(5)输入50,按回车,从old.txt中读取50组数据,并创建学生成绩打印在屏幕上。
(6)界面出现提示,选择排序方式。
(7)输入1,按回车,对该组成绩进行MSD法的冒泡排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3,2,1,0为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。
(8)此时屏幕上会提示用户,输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。
(9)输入1,按回车键,再次显示选择排序方式,输入2,使用LSD法的基数排序对该组数据进行排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3,2,1,0为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。
(10)屏幕再次显示输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。输入0,结束对该组数据的排序。屏幕显示输入0结束,输入其他值则可重新选择生成成绩再开始排序。
(11)输入1,按回车键,界面再次出现程序说明,以及数据产生选项。
(12)输入2,按回车键,选择随机产生数据并导入new.txt文件中,屏幕显示需要随机产生多少组数据,输入50,按回车键,界面显示出学生信息数据。
(13)输入1,按回车,对该组成绩进行MSD法的冒泡排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3,2,1,0为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。
(14)此时屏幕上会提示用户,输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。
(15)输入1,按回车键,再次显示选择排序方式,输入2,使用LSD法的基数排序对该组数据进行排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3,2,1,0为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。
(16)屏幕再次显示输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。输入0,结束对该组数据的排序。屏幕显示输入0结束,输入其他值则可重新选择生成成绩再开始排序。输入0,结束排序。
3、查看文本文本文件,打开文件new.txt和out.txt进行查看
(1)new.txt文件。
(2)out.txt文件。
八 源代码
#include<iostream>
#include<iomanip>
#include<ctime>
#include<fstream>
#include<string>
#define radix 10
using namespace std;
clock_t start,stop;
//学生的结构体
typedef struct SNode{
string name;
int ID; //学号
int key[4]={0}; //成绩
struct SNode *next; //指针域
}SNode,*Student;
?
void StudentGrade(int n); //生成学生成绩信息文件new.txt,n为文件读取数据的组数
?
Student CreatStudent_old(Student &L,int num); //读取old.txt,尾插法建立学生成绩单链表L,num为总人数
?
Student CreatStudent_new(Student &L,int num); //读取new.txt,尾插法建立学生成绩单链表L,num为总人数
?
Student BubbleSort(Student &L,int order[]); //MSD冒泡排序,L为学生成绩链表,order[]为用户输入的次关键字顺序
?
Student RadixSort(Student &L,int order[]); //LSD基数排序,L为学生成绩链表,order[]为次关键字顺序
?
void Print(Student &L); //在屏幕上显示
?
void choose(Student &L); //选择功能
?
void Menu(); //菜单
?
int main(){
ofstream fout;
fout.open("C:\\Out.txt",ios::app);
if(!fout){
cout<<"无法打开文件!!!"<<endl;
}
int a;
do{
Menu(); //调用菜单
cout<<" 输入0结束,输入其他则可重新生成成绩再进行排序";
cin>>a;
fout<<" 输入0结束,输入其他则可重新生成成绩再进行排序"<<a;
}while(a!=0) ; //a等于0停止循环
?
return 0;
}
?
//菜单
void Menu(){
ofstream fout;
fout.open("C:\\Out.txt",ios::app);
if(!fout){
cout<<"无法打开文件!!!"<<endl;
}
cout<<endl;
cout<<endl;
cout<<" --------------------------------------------------"<<endl;
cout<<" |------------------学生成绩排序------------------|"<<endl;
cout<<" | |"<<endl;
cout<<" | 本程序可以按关键字的优先关系排序对成绩排序。 |"<<endl;
cout<<" | |"<<endl;
cout<<" | 学生成绩有两种方式产生 |"<<endl;
cout<<" | 输入1:从old.txt文件中读取 |"<<endl;
cout<<" | 输入2:随机产生数据并导入new.txt文件中 |"<<endl;
cout<<" --------------------------------------------------"<<endl;
cout<<endl;
?
//输出到文件
fout<<endl;
fout<<endl;
fout<<" --------------------------------------------------"<<endl;
fout<<" ------------------学生成绩排序------------------"<<endl;
fout<<" "<<endl;
fout<<" 本程序可以按关键字的优先关系排序对成绩排序。 "<<endl;
fout<<" "<<endl;
fout<<" 学生成绩有两种方式产生 "<<endl;
fout<<" 输入1:从old.txt文件中读取 "<<endl;
fout<<" 输入2:随机产生数据并导入new.txt文件中 "<<endl;
fout<<" --------------------------------------------------"<<endl;
fout<<endl;
?
Student L;
int a;
cout<<" 选择产生成绩的方式:";
cin>>a;
cout<<endl;
fout<<" 选择产生成绩的方式:"<<a<<endl;
switch (a) {
case 1:
int n;
cout<<" 从old.txt文件中读取多少组数据:";
cin>>n;
fout<<" 从old.txt文件中读取多少组数据:"<<n<<endl;
CreatStudent_old(L,n); //调用函数创建学生成绩单链表
break;
case 2:
int m;
cout<<" 需要随机产生多少组数据:";
cin>>m;
fout<<" 需要随机产生多少组数据:"<<m<<endl;
StudentGrade(m);
CreatStudent_new(L,m); //调用函数创建学生成绩单链表
break;
}
?
Print(L); //调用打印函数将生成的学生成绩信息打印在屏幕上
?
int b;
do{
cout<<" --------------------------------------------------"<<endl;
cout<<" | 请选择: |"<<endl;
cout<<" | 1.MSD策略对数据进行冒泡法排序 |"<<endl;
cout<<" | 2.LSD策略对数据进行基数排序 |"<<endl;
cout<<" --------------------------------------------------"<<endl;
?
fout<<" --------------------------------------------------"<<endl;
fout<<" 请选择: "<<endl;
fout<<" 1.MSD策略对数据进行冒泡法排序 "<<endl;
fout<<" 2.LSD策略对数据进行基数排序 "<<endl;
fout<<" --------------------------------------------------"<<endl;
choose(L); //调用选择函数,选择排序方式和排序次序
cout<<" 输入0结束,输入其他数则继续进行此成绩的排序";
cin>>b;
cout<<endl;
fout<<" 输入0结束,输入其他数则继续进行此成绩的排序"<<b<<endl;
}while(b!=0);
}
?
//生成学生成绩信息文件new.txt,n为需要随机生成的数据组数
void StudentGrade(int n){
ofstream fout;
fout.open("C:\\new.txt",ios::out);
if(!fout){
cout<<"无法打开文件!!!"<<endl;
}
string name; //姓名
int id; //学号
int score[4]; //分数
//随机产生数据并写入new中
string studentname[10]={"a","b","c","d","e","f","g","h","i","j"};
srand(time(0));
for(int a=1;a<=n;a++){
name=studentname[rand()%10] + studentname[rand()%10] + studentname[rand()%10];//对字进行随机组合生成姓名
fout<<name<<" ";
id=a;
fout<<id<<" ";
for(int c=0;c<=3;c++){ //初始化成绩
score[c]=0;
}
for(int b=0;b<3;b++){
score[b]=rand()%101; //随机产生成绩
fout<<score[b]<<" ";
score[3]=score[3]+score[b]; //总分
}
fout<<score[3]<<endl;
}
fout.close();
}
?
//读取old.txt,尾插法建立学生成绩单链表L,num为人数
Student CreatStudent_old(Student &L,int num){
ifstream fin;
fin.open("C:\\old.txt",ios::in);
if(!fin){
cout<<"无法打开文件!!!"<<endl;
}
Student p,r;
L=new SNode;
L->next=NULL; //创建带头结点的链表L
r=L; //尾指针指向头结点
for(int i=1;i<=num;i++){
p=new SNode; //生成新结点
fin>>p->name; //读取姓名
fin>>p->ID; //读取学号
for(int j=0;j<=3;j++){
fin>>p->key[j];
}
p->next=NULL;
r->next=p; //将新结点p插入尾结点r之后
r=p; //r指向新的尾结点p
}
return L;
}
?
//读取new.txt里的数据,尾插法建立学生成绩单链表L,num为人数
Student CreatStudent_new(Student &L,int num){
ifstream fin;
fin.open("C:\\new.txt",ios::in);
if(!fin){
cout<<"无法打开文件!!!"<<endl;
}
Student p,r;
L=new SNode;
L->next=NULL; //创建带头结点的链表L
r=L; //尾指针指向头结点
for(int i=1;i<=num;i++){
p=new SNode; //生成新结点
fin>>p->name; //读取姓名
fin>>p->ID; //读取学号
for(int j=0;j<=3;j++){
fin>>p->key[j];
}
p->next=NULL;
r->next=p; //将新结点p插入尾结点r之后
r=p; //r指向新的尾结点p
}
return L;
}
?
//选择功能
void choose(Student &L){
ofstream fout;
fout.open("C:\\Out.txt",ios::app);
if(!fout){
cout<<"无法打开文件!!!"<<endl;
}
double time; //排序时间
cout<<" ";
fout<<" ";
int order[4];
int a;
cin>>a;
fout<<a<<endl;
cout<<" --------------------------------------------------"<<endl;
fout<<" --------------------------------------------------"<<endl;
switch (a) {
case 1:
cout<<" 输入次关键字比较顺序,0为语文,1为数学,2为英语,3为总分,总分默认第一个"<<endl;
cout<<endl;
fout<<" 输入次关键字比较顺序,0为语文,1为数学,2为英语,3为总分,总分默认第一个"<<endl;
fout<<endl;
for(int i=0;i<=3;i++){
cout<<" 第"<<i+1<<"个次序";
cin>>order[i];
cout<<endl;
fout<<" 第"<<i+1<<"个次序";
fout<<order[i]<<endl;
}
start=clock();
BubbleSort(L,order); //调用冒泡排序
stop=clock();
time=(double)(stop-start)/CLK_TCK;
Print(L); //打印结果
cout<<" 排序所用时间:"<<time<<endl;;
fout<<" 排序所用时间:"<<time<<endl;
break;
case 2:
cout<<" 输入次关键字比较顺序,0为语文,1为数学,2为英语,3为总分,总分默认第一个"<<endl;
cout<<endl;
fout<<" 输入次关键字比较顺序,0为语文,1为数学,2为英语,3为总分,总分默认第一个"<<endl;
fout<<endl;
for(int i=0;i<=3;i++){
cout<<" 第"<<i+1<<"个次序";
cin>>order[i];
cout<<endl;
fout<<" 第"<<i+1<<"个次序";
fout<<order[i]<<endl;
}
start=clock();
RadixSort(L,order); //调用基数排序
stop=clock();
time=(double)(stop-start)/CLK_TCK;
Print(L);
cout<<" 排序所用时间:"<<time<<endl;
fout<<" 排序所用时间:"<<time<<endl;
break;
default:
cout<<" 选择不存在,请输入1或者2"<<endl;
fout<<" 选择不存在,请输入1或者2"<<endl;
break;
}
}
?
//MSD冒泡排序,L为学生成绩链表,order[]为用户输入的次关键字顺序
Student BubbleSort(Student &L,int order[]){
int flag=1; //标记,判断该趟排序是否发生交换
Student p,q,t;
while(flag==1){
t=L;
p=L->next;
q=p->next;
flag=0;
while(q){
int k1=order[0];
if(p->key[k1]<q->key[k1]){ //交换数据域,指针域不变
flag=1; //发生交换,flag变成1
p->next=q->next;
t->next=q;
q->next=p;
q=p->next;
}
else if(p->key[k1]==q->key[k1]){ //总分相等,比较第二次序
int k2=order[1];
if(p->key[k2]<q->key[k2]){
flag=1;
p->next=q->next;
t->next=q;
q->next=p;
q=p->next;
}
else if(p->key[k2]==q->key[k2]){ //第二次序相等,比较第三次序
int k3=order[2];
if(p->key[k3]<q->key[k3]){
flag=1;
p->next=q->next;
t->next=q;
q->next=p;
q=p->next;
}
if(p->key[k3]==q->key[k3]){ //第三次序相等,比较第四次序
int k4=order[3];
if(p->key[k4]<q->key[k4]){
flag=1;
p->next=q->next;
t->next=q;
q->next=p;
q=p->next;
}
else {
p=p->next;
q=q->next;
}
}//if(k2)
else {
p=p->next;
q=q->next;
}
}//if(k1)
else {
p=p->next;
q=q->next;
}
}//if(3)
else {
p=p->next;
q=q->next;
}
t=t->next;
}//while
}//while
return L;
}
?
//LSD基数排序,L为学生成绩链表,order[]为次关键字顺序
Student RadixSort(Student &L,int order[]){
Student front[radix],end[radix],p,t;
int m,k;
for(int n=3;n>=0;n--){
k=order[n];
for(int d=1;d<=3;d++){ //位数
for(int i=0;i<radix;i++){ //初始化各链队首尾指针
front[i]=end[i]=NULL;
}
p=L->next;
while(p){
if(d==1){
m=p->key[k]%10; //第一趟取个位分配
}
else if(d==2){
m=(p->key[k]%100)/10; //第二趟分配取十位
}
else{
m=p->key[k]/100; //第三趟取百位分配
}
if(front[m]==NULL){ //采用尾插法建立单链表
front[m]=p;
end[m]=p;
}
else{
end[m]->next=p;
end[m]=p;
}
p=p->next;
}//while
L->next=NULL;
for(int j=radix-1;j>=0;j--){ //对每一个链队从大到小进行收集
if(front[j]){
if(L->next==NULL){
L->next=front[j]; //L指向链队头
t=end[j]; //t指向队尾
}
else{
t->next=front[j]; //t->next指向下一个链队头
t=end[j];
}
}
t->next=NULL; //最后一个结点指向空
}//for
}
}
return L;
}
?
//在屏幕上显示
void Print(Student &L){
ofstream fout;
fout.open("C:\\Out.txt",ios::app);
if(!fout){
cout<<"无法打开文件!!!"<<endl;
}
cout<<" --------------------------------------------------"<<endl;
cout<<setw(8)<<"姓名"<<setw(8)<<"学号"<<setw(8)<<"语文"<<setw(8)<<"数学"<<setw(8)<<"英语"<<setw(8)<<"总分"<<endl;
fout<<" --------------------------------------------------"<<endl;
fout<<setw(8)<<"姓名"<<setw(8)<<"学号"<<setw(8)<<"语文"<<setw(8)<<"数学"<<setw(8)<<"英语"<<setw(8)<<"总分"<<endl;
Student p;
p=L->next;
while(p){
cout<<setw(8)<<p->name;
cout<<setw(8)<<p->ID;
fout<<setw(8)<<p->name;
fout<<setw(8)<<p->ID;
for(int i=0;i<4;i++){ //依次输出4个数据
cout<<setw(8)<<p->key[i];
fout<<setw(8)<<p->key[i];
}
cout<<endl;
fout<<endl;
p=p->next;
}
cout<<endl;
fout<<endl;
}
?
?
以上是关于两种多关键字排序策略比较的主要内容,如果未能解决你的问题,请参考以下文章