两种多关键字排序策略比较

两种多关键字排序策略比较

一 目的

使用LSD和MSD法的稳定的内部排序法或者"分配""收集"的方法实现学生成绩按照多关键字进行排序,实现不同单科次序下的成绩排名,并对两种方法产生的结果进行比较。学生成绩有语文,数学,英语和总分,数据以从old.txt文件中读取和随机产生并保存到new.txt文件中两种方式产生,排序式要求以总分主次序从大到小进行排序,当总分相等时,对用户要求的次序顺序进行排序,最后将按照多关键字排序的结果保存到out.txt文件中。

二 需求分析

1、排序方式分析

1LSD法:选择"分配""收集"的基数排序算法,使用单链表存储分数,从最低的次序开始排,最后排总分,排序时,依次从数的个位、十位、百位进行分配和收集,最终得到从大到小的成绩顺序;

2MSD法:选择稳定的内部排序中的冒泡排序算法,使用单链表存储分数,从总分开始比较,比较相邻两个数之间的大小,前一个数小于后一个数则交换两数据元素的指针指向,如果相等,则进行后一个次序的比较,最终得到从大到小的成绩顺序。

2、输入数据

1old.txt里的学生信息;

2)需要从old.txt文件中读取的数据组数n50<=n<=1000);

3)需要随机生成的数据组数m0<=m=1000);

4)用户输入的排序次序。

3、输出数据

1)随机生成的数据信息写入new.txt文件中;

2)使用冒泡排序产生的排序结果打印到屏幕和out.txt文件中;

3)使用基数排序产生的排序结构打印到屏幕和out.txt文件中。

4、程序需要的功能

1)菜单界面,显示提示信息,选择数据产生的方式以及需要多少组数据;

2)选择功能,提示用户选择哪种排序方式,以及排序次序;

3)读取文件数据,创建单链表;

4MSD冒泡排序,对成绩进行排序;

5LSD基数排序,对成绩进行排序;

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)输入除12以外的数,程序无法正常运行。

1)错误分析:switch语句中未考虑除12以外的情形。

2)问题解决:在switch语句中加default,提示用户输入其他字符是重新输入12

六 测试结果

1、操作界面

运行程序,输入1,选择第一种数据产生方式;输入50,从old.txt文件中读取50组数据,输出所读取的数据打印在屏幕上。

输入1,选择第一种排序方式,输入次序3210,得到得结果正确。

输入2,选择第二种排序方式,输入次序3210,得到得结果正确。

输入0,结束此次排序。

输入2,选择第二钟数据产生方式,输入50,随机产生50组数据并保存到new.txt文件钟,并从中读取数据,打印在屏幕上。

输入1,选择第一钟排序方式,输入次序3210,得到得结果正确。

输入2,选择第二种排序方式,输入次序3210,得到得结果正确。

输入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类分数的排序顺序,以输入3210为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。

8)此时屏幕上会提示用户,输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。

9)输入1,按回车键,再次显示选择排序方式,输入2,使用LSD法的基数排序对该组数据进行排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3210为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。

10)屏幕再次显示输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。输入0,结束对该组数据的排序。屏幕显示输入0结束,输入其他值则可重新选择生成成绩再开始排序。

11)输入1,按回车键,界面再次出现程序说明,以及数据产生选项。

12)输入2,按回车键,选择随机产生数据并导入new.txt文件中,屏幕显示需要随机产生多少组数据,输入50,按回车键,界面显示出学生信息数据。

13)输入1,按回车,对该组成绩进行MSD法的冒泡排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3210为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。

14)此时屏幕上会提示用户,输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。

15)输入1,按回车键,再次显示选择排序方式,输入2,使用LSD法的基数排序对该组数据进行排序,屏幕提示要求用户输入关键字排序的顺序,依次输入4类分数的排序顺序,以输入3210为例子,程序对该组数据以总分,英语,数学,语文的顺序进行排序,并输出排序结果和排序时间。

16)屏幕再次显示输入0结束对该组数据的排序,输入其他的值继续对该组数据进行排序。输入0,结束对该组数据的排序。屏幕显示输入0结束,输入其他值则可重新选择生成成绩再开始排序。输入0,结束排序。

3、查看文本文本文件,打开文件new.txtout.txt进行查看

1new.txt文件。

2out.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;

}

 

 

原文地址:https://www.cnblogs.com/ZhangStudy/p/12492422.html