\
这里介绍的排序算法复杂性都是:
- 编码容易,是简单排序场景的首选
- 在一些特殊的情况,简单排序算法效率反而更高
- 有时可以作为一个复杂算法的子过程
一:选择排序(Selection Sort)
算法思想
在所给的一组数中选出最小值,然后与这组数第一个数的位置互换。
接下来从第二个数到最后一个数范围中国,选出最小,与第二个数互换。
以此类推,也就是每次选最小然后往前放。
代码实现
template<typename T>
void selection_sort(T a[],int n){
for(int i=0;i<n;i++){
//寻找[i,n)的最小值的索引位置 int minIndex=i; for(int j=i+1;j<n;j++){ if(a[j]<a[minIndex]){
minIndex=j;
}
}
swap(a[i],a[minIndex]);
}
}
- 选择排序的两个参数:传入的数组以及数组包含的元素个数
- 思想就是先标记第一个数为最小值索引,然后从第二数遍历下去,找出真正的最小值的索引,记录下来,然后与第一个数交换。重复执行该步骤
- 注意:swap()是c++标准库的函数,在std命名空间。老标准在
- 为了类型更灵活,使用模板函数:template<typename 定义类型> (不要加; )
程序实现
给出一个结构体学生,对学生的分数进行排序输出,如果出现分数相同的情况,按照名字的字母的顺序输出(从小到大)
定义两个文件:
- 头文件----定义结构体学生以及所需的运算符重载 student.h
#ifndef MY_stu
#define MY_stu
#include<iostream>
using namespace std;
struct student{
string name;
int score;
bool operator< (const student& stu){
return score!=stu.score?score<stu.score:name<stu.name;
}
friend ostream& operator<< (ostream& os,const student& stu){
return os<<"Student:"<<stu.name<<','<<stu.score;
}
};
#endif
注意这里定义了两个操作符重载:
- 第一个是 < ---因为这里作为排序的if判断内容,所以返回bool值
- 第二个是 << ---用于自定义输出,但是这里必须定义成友元函数friend才能访问结构体变量
这里对字符串比较时候,直接用了c++库
- 主函数----定义选择排序操作以及测试用例 selection_sort.cpp
#include<iostream>
#include"student.h"
using namespace std;
//选择排序
template<typename T>
void selection_sort(T a[],int n){
for(int i=0;i<n;i++){
//寻找[i,n)的最小值的索引位置
int minIndex=i;
for(int j=i+1;j<n;j++){
if(a[j]<a[minIndex]){
minIndex=j;
}
}
swap(a[i],a[minIndex]);
}
}
int main(){
student stu[6]={{"Alice",99},{"Ben",77},{"Julia",77},{"David",88},{"Amy",97},{"Tom",88}};
selection_sort(stu,6);
for(int i=0;i<6;i++){
cout<<stu[i].name<<","<<stu[i].score<<endl;
}
}
对结构体初始化时候,千万要注意变量内容与定义顺序一一对应
生成随机数的方法
#include<iostream>
#include<ctime>
#include<cassert>
using namespace std;
namespace SortHelper{
int* generateRandomArray(int n,int rangeL,int rangeR){
assert(rangeL<=rangeR);
int* arr =new int[n];
srand(time(NULL));//初始化随机种子,会提供一个种子,这个种子会对应一个随机数
for(int i=0;i<n;i++){
arr[i]=rand()%(rangeR-rangeL+1)+rangeL;
}
return arr;
}
}
int main(){
int n=10000;
int* arr=SortHelper::generateRandomArray(n,0,n);
for(int i=0;i<n;i++){
cout<<arr[i]<<",";
}
cout<<endl;
delete[] arr;
return 0;
}
下面对值得注意的地方进行总结:
选用返回类型是指针的形式去生成数组,有三个参数:
数组的个数,数组的范围(最小值----最大值)这里默认后者大,使用之前需要判断,所以调用标准库 去使用:assert(表达式)
随机数生成中:
首选从 标准库中引入srand初始化种子**srand(time(NULL));-------**使用系统时间来初始化
rand()%写出范围;
用srand()函数获取这个编号,然后rand()就按顺序获取这些数字最后别忘了,有new就要有delete,这里数组多个元素,用:delete[ ]
计算程序运行时间的方法
由于计算运行时间会把“函数”作为一个变量,首先需要知道函数作为参数的写法
#include<iostream>
using namespace std;
int sum(int& a,int& b){
return a+b;
}
void sum_fun(int(*p)(int&,int&),int c,int d){
cout<<"运行sum结果:"<<p(c,d)<<endl;
}
int main(){
int a=1000;
int b=9000;
sum_fun(sum,a,b);
return 0;
}
函数传参数方法:
函数类型( 新定义函数名)(原函数参数类型)*
//判断排序是否成功
template<typename T>
bool is_sort_success(T a[],int n){
for(int i=1;i<n;i++){
if (a[i]<a[i-1]){
return false;
}
}
return true;
}
先判断排序正确性以保证生成时间有用,函数返回类型是bool.
//打印程序运行时间
template<typename T>
void program_run_time(const string& name,void(*p)(T[],int),T a[],int n){
clock_t starttime=clock();
p(a,n);
clock_t finishtime=clock();
assert(is_sort_success(a,n));
cout<<name<<"的运行时间:"<<double(finishtime-starttime)/CLOCKS_PER_SEC<<"s";
}
由于clock_t的类型不是数,为了得到s需要强制用double去类型转换,除以头文件定义的常量:CLOCKS_PER_SEC即可得到需要的秒