排序

97 阅读3分钟

\

 

这里介绍的排序算法复杂性都是:O(n^{2})

  • 编码容易,是简单排序场景的首选
  • 在一些特殊的情况,简单排序算法效率反而更高
  • 有时可以作为一个复杂算法的子过程

一:选择排序(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 定义类型> (不要加;

程序实现

给出一个结构体学生,对学生的分数进行排序输出,如果出现分数相同的情况,按照名字的字母的顺序输出(从小到大)

定义两个文件:

  1. 头文件----定义结构体学生以及所需的运算符重载     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++库

  1. 主函数----定义选择排序操作以及测试用例   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即可得到需要的秒