目录
第一章 系统概述
一、目的:
掌握本学期所学的链表、排序等相关知识,在实践中巩固
二、任务:
设计一个界面,用户输入数据,先智能推荐最优算法作为温馨提示,再让用户选择需要使用自己需要的算法,然后调用相关函数并输出结果;随机生成 1w 个数,比较哪种算法的性能最优
三、开发环境:
windows10 家庭中文版
第二章 系统设计
一、系统主要算法设计思想:
1.用户输入:
为了提高用户体验,提供两种输入方法(数据流输入和控制台输入):
①.数据流输入:用户可在 txt 文件里输入数据,优点是便于随时修改数据且每次调试程序不需要一次次重新输入。
②.控制台输入:用户在控制台输入数据,建立链表,将每次输入的数据经过处理后,将数字存入链表的结点的 data 域里,输入完成后统计数据个数 n,定义动态数组并对数组赋值。(倘若用户输入的时候输入了字符,程序会处理字符读取数字,并不会使程序出错。)
2.排序算法:
一共有七种排序算法,分别是冒泡排序、快速排序、直接插入排序、希尔排序、简单选择排序、堆排序和归并排序。
①.冒泡排序:把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。
②.快速排序:通过多次比较和交换来实现排序,其排序流程如下:首先设定一个分界值,通过该分界值将数组分成左右两部分。将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。重复上述过程,可以看出,这是递归的过程。
③.直接插入排序:每一趟将一个待排的记录,按其关键字的大小插入到已经排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。
④.希尔排序:把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
⑤.简单选择排序:从 0 开始,每次确定一个位置的元素。假设当前需要确定的位置下标为 i,则将 i 处的元素与后面的元素逐个比较,并将每次比较结果中较小的元素存放在 i 处,从而保证 i 处一直保存着最小元素。
⑥.堆排序:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余 n-1 个元素重新构造成一个堆,这样会得到 n 个元素的次小值。如此反复执行,便能得到一个有序序列了。
⑦.归并排序:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。将两个有序表合并成一个有序表,为二路归并排序。
3.持续运行:
只有当用户输入 0 才能使排序程序结束,因此用户可以多次使用排序算法,提高程序的实用性。
二、排序算法时间性能:
(为了体现不同算法时间性能明显差异,依次使用 1w、10w、100w个数据进行检测,检测结果如下:)
1.随机生成 1w 个数据时,快速排序、希尔排序、堆排序时间性能最好,在 2ms 左右,此时这三种时间性能上无明显差异,之后的排名依次是归并排序 13ms、直接插入排序 65ms、简单选择排序 112ms、冒泡排序318ms。
2.随机生成 10w 个数据时,时间性能最高的是快速排序 14ms,其次是堆排序 21ms 和希尔排序 22ms,归并排序用了 750ms,直接插入排序用了 6s 多,简单选择排序 11s 多,冒泡排序用了38s多,差异较为明显。3.随机生成 100w 个数据时,时间性能最高的仍是快速排序 182ms,其次是堆排序 322ms,第三是希尔排序 408ms,其他排序算法时间复杂度太高。
性能总结:快速排序>堆排序>希尔排序>归并排序>直接插入排序>简单选择排序>冒泡排序。
三、程序流程图:
程序开始时,先欢迎用户使用系统,用户可以根据自己的需求选择输入方式,有数据流输入方式和控制台面板输入方式可供选择。倘若用户选择数据流输入,我们将自动读取文件数据;倘若用户选择控制台面板输入,随后我们对用户输入的数据智能读取数字,智能读取直接避免了用户误输字符带来的干扰。不论哪种输入方式,都会将数据存入数组elem 中,随后为用户推荐排序方式并让用户选择喜欢的排序方式。直至用户选择 0 才能退出排序程序,因此可以根据需要多次排序。随后对排序算法进行性能检测,通过分别输入 1w、10w、100w 个数据,比较相关算法的运行时长,做出算法的时间性能分析。最后释放内存,退出系统,感谢使用。
四、代码函数功能分析
1.程序主要分为一个源文件“排序.cpp”和两个头文件“sort.h”和“show.h”。
2.头文件“show.h”里有两个函数,第一个是 show1,输出程序刚开始时的界面,第二个是 show2,输出排序算法的目录。
3.头文件“sort.h”里存放很多类模板的函数,包括了 output 函数(输出顺序表),copy 函数(拷贝顺序表),check 函数(检测顺序表是否排序完),Bubblesort 函数(冒泡排序函数),Quicksort 函数(快速排序),StraightInsertsort 函数(直接插入排序),Shellsort 函数(希尔排序),Simpleselectionsort 函数(简单选择排序),FilterDown和 Heapsort 函数(堆排序),Merge 和 Mergesort(归并排序),randomnum函数(用于生成数据集),timeper 函数(用于测试和比较每种排序算法的运行时长)。
4.源代码里包含着一个类和主函数。类 node 里含有三个函数,creat_shuru 函数(用于输入数据时剔除字符,读取数字),countlen函数(用于计算链表结点个数),fuzhi 函数(用于将链表中结点的数据域的值赋给顺序表元素)。源代码的主函数通过定义变量、调用函数等操作实现程序整体功能。
第三章 系统实现
一、开发使用的语言:
C++
二、开发使用的工具:
VS 2019
三、系统运行截图:
这是程序开始时的运行界面:

用户可以根据需求,自己选择喜欢的输入方式,有数据流输入,也有控制台面板输入。系统提高了容错率——如果用户不选择这两种方式中的一种,会自动纠错让用户重新选择,避免程序卡住

如果用户输入了字符的话,系统也可以自动纠错,提取数字部分, 跳过字符,提高了程序的容错率和实用性。如,用户输入 a1,程序提取 出数字 1;用户输入:35e23,程序自动提取数字 35 和 23,提取的数字 先存在链表里的结点数据域,之后被赋值给顺序表的元素
输出排序前的数据,为用户推荐最优算法,提供排序方法目录,让用户选择:

为了更加清晰直观的体现不同的排序算法,程序将输出每一种排序算法的每一趟结果,以下排序算法均会输出每趟排序结果以及排序的最终结果。
输入
1
时,冒泡排序排序过程以及结果如下:

输入 2 时,快速排序过程以及结果如下:
输入
3
时,直接插入排序的过程以及结果如下:
输入 4 时,希尔排序的排序过程以及结果如下:
输入
5
时,简单选择排序的排序过程以及结果如下:

输入 6 时,堆排序排序过程和结果如下:
输入 7 时,归并排序排序过程以及结果如下:
输入
0
时,排序程序退出并输出
Thanks for using
!

此后调用函数,生成
1w
个随机数据并检测各种排序算法的时间性
能:

当增加随机数据到
10w
个时,各排序算法时间性能如下:

当随机数据增加到
100w
个时,快速、堆、希尔排序算法时间性能如下:

当测试为一百万时,只有快速排序、堆排序、希尔排序可以快速运行,其他四种排序时间复杂度太高无法运行。但当数据仅仅为一万、十万时,七种排序算法的时间性能都可以检测出来。最终得到结果,七种排序算法时间性能的排序为:快速排序>堆排序>希尔排序>归并排序>直接插入排序>简单选择排序>冒泡排序。
第四章 代码展示
一共三个文件,源.cpp、sort.h、show.h
源.cpp:
#include<iostream>
#include<fstream>
#include<ctime>
#include<stdlib.h>
#include"sort.h"
#include"show.h"
using namespace std;
int len = 0;
template <class ElemType>
class node
{
public:
int data;
node<ElemType>* next;
node* qqq, * ppp;
node* creat_shuru()
{
char x;
int t = 0, flag = 0;
node* d;
d = new node;
while (flag == 0)
{
cin >> x;
if (x == '\n')
return NULL;
if (x >= 48 && x <= 57)
flag = 1;
else
flag = 0;
}
while (flag == 1)
{
if (x >= 48 && x <= 57)
{
flag = 1;
t = t * 10 + ((int)x - 48);
x = getchar();
}
else
flag = 0;
}
d->data = t;
len++;
if (x == '\n')
d->next = NULL;
else
d->next = creat_shuru();
return d;
}
//计算链表长度
int countlen(node* p)
{
int flag = 0;
while (p != NULL)
{
flag++;
p = p->next;
}return flag;
}
void fuzhi(node* p, ElemType elem[])
{
int i = 0;
while (p != NULL)
{
elem[i] = p->data;
p = p->next;
i++;
}
}
//顺序表赋值
};
int main()
{
show1();
//欢迎使用
cout << "What input method do you want to use? " << endl << endl;
cout << "①Data flow input (数据流输入,系统将自动为您读取文件“testdata.txt”的数据)" << endl << endl;
cout << "②Control panel input (控制台面板输入,每个数据以空格分开,按回车结束输入,输错了字符也不用担心,系统会为您智能读取数字)" << endl << endl;
cout << "You favorite:";
int input_way;
cin >> input_way;
cout << endl;
int way, i;
int* elem, n = 0, b = 0;//n统计文件内数据个数,b辅助空间
int* copy;
elem = nullptr;
while (input_way != 1 && input_way != 2)
{
cout << "Error! Please re-enter!" << endl << endl;
cout << "You favorite:";
cin >> input_way;
}
if (input_way == 1)
{
ifstream txtfile1("testdata.txt", ios::in);
while (!txtfile1.eof())
{
txtfile1 >> b;
n++;
}
elem = new int[n];
copy = new int[n];
ifstream txtfile;
txtfile.open("testdata.txt");
if (!txtfile)
{
cerr << "file open error!" << endl;
exit(1);
}
for (i = 0; i < n; i++)
txtfile >> elem[i];
txtfile.close();
}
if (input_way == 2)
{
cout << "Please input your data:";
node <int> node1;
node1.qqq = node1.creat_shuru();
node1.ppp = node1.qqq;
n = node1.countlen(node1.qqq);
elem = new int[n];
node1.fuzhi(node1.ppp, elem);
}
//将数据集中的数据传入数组
//数据流读入数据好处:①使用动态数组,节省内存 ②当测试数据过多时,每次调试时不需要一个一个敲 ③可以随时修改前面输入的数据,提高容错率、更方便
//控制面板输入好处:对用户较友好
cout << endl << "Before sorting,the data is:";
output(elem, n);
copy = new int[n];
do
{
show2();
cin >> way;
cout << endl;
if (way != 0)
cout << "----------------------Here is the sorting result----------------" << endl << endl;
if (way == 0)break;
else copynum(elem, copy, n);
if (way == 1)Bubblesort(copy, n, 1);
if (way == 2)Quicksort(copy, 0, n - 1, n, 1);
if (way == 3)StraightInsertsort(copy, n, 1);
if (way == 4)Shellsort(copy, n, 1);
if (way == 5)Simpleselectionsort(copy, n, 1);
if (way == 6)Heapsort(copy, n, 1);
if (way == 7)Mergesort(copy, n, 1);
cout << endl << "The final result:" << endl;
output(copy, n);
cout << "-------------------------------------------------------------------------";
} while (way != 0);
if (way == 0) cout << "Thanks for using!" << endl;
//用户自己选择需要的排序方式
cout << endl << "Next we will randomly generate " << N << " data and detect the optimal performance algorithm" << endl;
int* ran;
int* store;
ran = new int[N];
store = new int[N];
randomnum(ran);
timeper(ran, store, N);
delete[]elem;
delete[]copy;
delete[]ran;
delete[]store;
}
sort.h:
#include<iostream>
#include<ctime>
#define RAND_MAX 0x7fff
#define N 10000
using namespace std;
template<class ElemType>
void output(ElemType elem[], int n)
{
for (int i = 0; i < n; i++)
cout << elem[i] << " ";
cout << endl;
}
//输出数组数据
template<class ElemType>
void copynum(ElemType elem[], ElemType copy[], int n)
{
for (int i = 0;i < n;i++)
{
copy[i] = elem[i];
}
}
//拷贝数组数据
template<class ElemType>
int check(ElemType elem[], int n)
{
int i, flag = 0;
for (i = 0;i < n - 1;i++)
if (elem[i] > elem[i + 1])
flag = 1;
return flag;
}
//检查是否排序成功
template<class ElemType>
void Bubblesort(ElemType elem[], int n, int x)
{
int flag = 1;
bool finish = false;
int temp;
while (finish == false)
{
if (!finish) {
finish = true;
for (int j = 0; j < n - 1; j++)
{
if (elem[j] > elem[j + 1])
{
temp = elem[j];
elem[j] = elem[j + 1];
elem[j + 1] = temp;
finish = false;
}
}
}
if (!finish)
{
if (x == 1)
{
cout << "The " << flag << "th sort result is:" << endl;
output(elem, n);
flag++;
}
}
if (finish) {
break;
}
}
}
//冒泡排序
template<class ElemType>
void Quicksort(ElemType elem[], int low, int high, int n, int x)
{
ElemType e = elem[low];
int i = low, j = high;
while (i < j)
{
while (i < j && elem[j] >= e)
j--;
if (i < j)
elem[i++] = elem[j];
while (i < j && elem[i] <= e)
i++;
if (i < j)
elem[j--] = elem[i];
}
elem[i] = e;
if (x == 1)
{
cout << "Sort interval:" << low << "--" << high << ";The base position is:" << i << endl;
output(elem, n);
}
if (low < i - 1) Quicksort(elem, low, i - 1, n, x);
if (i + 1 < high) Quicksort(elem, i + 1, high, n, x);
}
//快速排序
template<class ElemType>
void StraightInsertsort(ElemType elem[], int n, int x)
{
int i, j;
for (i = 1;i < n;i++)
{
ElemType e = elem[i];
for (j = i - 1;j >= 0 && e < elem[j];j--)
elem[j + 1] = elem[j];
elem[j + 1] = e;
if (x == 1)
{
cout << "The " << i << "th sort result is:" << endl;
output(elem, n);
}
}
}
//直接插入排序
template<class ElemType>
void Shellsort(ElemType elem[], int n, int x)
{
int i, j, d = n / 2;
ElemType e;
while (d > 0)
{
for (i = d;i < n;i++)
{
e = elem[i];
j = i - d;
while (j > 0 && e < elem[j])
{
elem[j + d] = elem[j];
j -= d;
}
elem[j + d] = e;
}
if (x == 1)
{
cout << "When d=" << d << ",the result is:" << endl;
output(elem, n);
}
d = d / 2;
}
}
//希尔排序
template<class ElemType>
void Simpleselectionsort(ElemType elem[], int n, int x)
{
int k, temp;
for (int i = 0;i < n - 1;i++)
{
k = i;
for (int j = i + 1;j < n;j++)
if (elem[j] < elem[k])
k = j;
if (k != i)
{
temp = elem[i];
elem[i] = elem[k];
elem[k] = temp;
}
if (x == 1)
{
cout << "The " << i + 1 << "th sort result is:" << endl;
output(elem, n);
}
}
}
//简单选择排序
template<class ElemType>
void FilterDown(ElemType elem[], int low, int high)
{
int f = low, i = 2 * low + 1;
ElemType e = elem[low];
while (i <= high)
{
if (i < high && elem[i] < elem[i + 1])
i++;
if (e < elem[i])
{
elem[f] = elem[i];
f = i;
i = 2 * f + 1;
}
else
break;
}
elem[f] = e;
}
template<class ElemType>
void Heapsort(ElemType elem[], int n, int x)
{
int i, temp;
int flag = 1;
for (i = (n - 2) / 2;i >= 0;--i)
FilterDown(elem, i, n - 1);
for (i = n - 1;i > 0;--i)
{
temp = elem[0];
elem[0] = elem[i];
elem[i] = temp;
FilterDown(elem, 0, i - 1);
if (x == 1)
{
cout << "The " << flag << "th sort result is:" << endl;
output(elem, n);
flag++;
}
}
}
//堆排序
template<class ElemType>
void Merge(ElemType elem[], int low, int mid, int high)
{
ElemType* tmpElem = new ElemType[high + 1];
int i = low, j = mid + 1, k = low;
while (i <= mid && j <= high)
if (elem[i] <= elem[j])
tmpElem[k++] = elem[i++];
else
tmpElem[k++] = elem[j++];
while (i <= mid)
tmpElem[k++] = elem[i++];
while (j <= high)
tmpElem[k++] = elem[j++];
for (i = low;i <= high;i++)
elem[i] = tmpElem[i];
delete[]tmpElem;
}
template<class ElemType>
void Mergesort(ElemType elem[], int n, int x)
{
int len = 1, i;
while (len < n)
{
i = 0;
while (i + 2 * len <= n)
{
Merge(elem, i, i + len - 1, i + 2 * len - 1);
i += 2 * len;
}
if (i + len < n)
{
Merge(elem, i, i + len - 1, n - 1);
}
len *= 2;
if (x == 1)
{
cout << "When len=" << len << ",the result is:" << endl;
output(elem, n);
}
}
}
//归并排序
const double CLOCKS_PER_SECOND = ((clock_t)1000);
//CLOCK_PER_SECOND这个常量表示每一秒(per second)有多少个时钟计时单元
const double CLOCKS_PER_MILLISECOND = ((clock_t)1);
//CLOCK_PER_SECOND这个常量表示每毫秒(per millisecond)有多少个时钟计时单元
template<class ElemType>
void randomnum(ElemType ran[])
{
for (int i = 0; i < N; i++)
{
ran[i] = rand();
}
fstream txtfile;
fstream txtfile2("randomnum.txt", fstream::out | ios_base::trunc);
txtfile2.close();
txtfile.open("randomnum.txt", ios::out | ios::trunc);
if (!txtfile)
{
cerr << "randomnum.txt open error!";
exit(1);
}
for (int i = 0; i < N; i++)
{
txtfile << ran[i] << " ";
}
txtfile.close();
}
//避免每次调试的时候生成数据在文件里堆积 所以先清空文件再写入数据
//生成10000个随机数 并输出到文件random.txt里
template<class ElemType>
int timeper(ElemType ran[], ElemType store[], int n)
{
int t = 0;
double timerem[7];
clock_t startTime1, startTime2, startTime3, startTime4, startTime5, startTime6, startTime7;
clock_t endTime1, endTime2, endTime3, endTime4, endTime5, endTime6, endTime7;
copynum(ran, store, n);
startTime1 = clock();
Bubblesort(store, n, 0);
endTime1 = clock();
cout << endl << "Bubblesort time(冒泡排序):";
timerem[0] = ((double)endTime1 - (double)startTime1) / CLOCKS_PER_MILLISECOND;
if (timerem[0] < 1000)
cout << timerem[0] << "ms" << endl;
else
cout << (double)timerem[0] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime2 = clock();
Quicksort(store, 0, n - 1, n, 0);
endTime2 = clock();
cout << endl << "Quicksort time(快速排序):";
timerem[1] = ((double)endTime2 - (double)startTime2) / CLOCKS_PER_MILLISECOND;
if (timerem[1] < 1000)
cout << timerem[1] << "ms" << endl;
else
cout << (double)timerem[1] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime3 = clock();
StraightInsertsort(store, n, 0);
endTime3 = clock();
cout << endl << "StraightInsertsort time(直接插入排序):";
timerem[2] = ((double)endTime3 - (double)startTime3) / CLOCKS_PER_MILLISECOND;
if (timerem[2] < 1000)
cout << timerem[2] << "ms" << endl;
else
cout << (double)timerem[2] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime4 = clock();
Shellsort(store, n, 0);
endTime4 = clock();
cout << endl << "Shellsort time(希尔排序):";
timerem[3] = ((double)endTime4 - (double)startTime4) / CLOCKS_PER_MILLISECOND;
if (timerem[3] < 1000)
cout << timerem[3] << "ms" << endl;
else
cout << (double)timerem[3] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime5 = clock();
Simpleselectionsort(store, n, 0);
endTime5 = clock();
cout << endl << "Simpleselectionsort time(简单选择排序):";
timerem[4] = ((double)endTime5 - (double)startTime5) / CLOCKS_PER_MILLISECOND;
if (timerem[4] < 1000)
cout << timerem[4] << "ms" << endl;
else
cout << (double)timerem[4] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime6 = clock();
Heapsort(store, n, 0);
endTime6 = clock();
cout << endl << "Heapsort time(堆排序):";
timerem[5] = ((double)endTime6 - (double)startTime6) / CLOCKS_PER_MILLISECOND;
if (timerem[5] < 1000)
cout << timerem[5] << "ms" << endl;
else
cout << (double)timerem[5] / 1000 << "s" << endl;
copynum(ran, store, n);
startTime7 = clock();
Mergesort(store, n, 0);
endTime7 = clock();
cout << endl << "Mergesort time(归并排序):";
timerem[6] = ((double)endTime7 - (double)startTime7) / CLOCKS_PER_MILLISECOND;
if (timerem[6] < 1000)
cout << timerem[6] << "ms" << endl;
else
cout << (double)timerem[6] / 1000 << "s" << endl;
double flag;
int i;
flag = timerem[0];
for (i = 0;i < 7;i++)
if (timerem[i] < flag)
{
flag = timerem[i];
t = i;
}
cout << endl << "The best way to perform is:";
if (t == 0)cout << "Bubblesort(冒泡排序)" << endl << endl;
if (t == 1)cout << "Quicksort(快速排序)" << endl << endl;
if (t == 2)cout << "StraightInsertsort(直接插入排序)" << endl << endl;
if (t == 3)cout << "Shellsort(希尔排序)" << endl << endl;
if (t == 4)cout << "Simpleselectionsort(简单选择排序)" << endl << endl;
if (t == 5)cout << "Heapsort(堆排序)" << endl << endl;
if (t == 6)cout << "Mergesort(归并排序)" << endl << endl;
return t;
}
show.h:
#include<iostream>
void show1()
{
cout << endl << endl;
cout << "\t\t ****************************************************\n";
cout << "\t\t * Welcome to the sorter *\n";
cout << "\t\t * *\n";
cout << "\t\t * *\n";
cout << "\t\t * Developers: YanWenxuan *\n";
cout << "\t\t * *\n";
cout << "\t\t * *\n";
cout << "\t\t * January 2021 *\n";
cout << "\t\t****************************************************\n";
cout << "\n\n";
}
void show2()
{
cout << endl;
cout << "The algorithm we intelligently recommend for you based on time performance is:②Quicksort(快速排序)" << endl << endl;
cout << "Now you can choose the sort you like : " << endl << endl;
cout << "①Bubblesort(冒泡排序)" << endl << endl;;
cout << "②Quicksort(快速排序)" << endl << endl;;
cout << "③StraightInsertsort(直接插入排序)" << endl << endl;;
cout << "④Shellsort(希尔排序)" << endl << endl;
cout << "⑤Simpleselectionsort(简单选择排序)" << endl << endl;
cout << "⑥Heapsort(堆排序)" << endl << endl;
cout << "⑦Mergesort(归并排序)" << endl << endl;
cout << "If you want to terminate, you can enter 0 to exit." << endl << endl;
cout << "You favorite:";
}