修炼算法内功:基础排序

内容:1、简单的选择排序;

           2、使用模板(泛型)使算法更加灵活;

           3、使用结构体完成学生的name和score属性的排序;

           4、随机生成算法测试用例。


写在前面:为什么学习O(n^2)的排序算法?

基础

  • 编码简单,易于实现,是一些简单场景的首选
  • 在一些特殊情况下,,简单的排序算法更有效
  • 简单的排序算法思想可衍生出复杂的排序算法
  • 作为子过程,改进更复杂的排序算法

1、Selection Sort 选择排序

基本思路

如:8 6 2 3 1 5 7 4

对一个序列A中的元素A[1]~A[n],令i从1到n的枚举,进行n趟操作,每趟从排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,直到排序完成。

示例:

[1,8]中1最小(1,8交换)——>1 6 2 3 8 5 7 4

[2,8]中2最小(2,6交换)——>1 2 6 3 8 5 7 4

[3,8]中3最小(3,6交换)——>1 2 3 6 8 5 7 4

[4,8]中4最小(4,6交换)——>1 2 3 4 8 5 7 6

[5,8]中5最小(5,8交换)——>1 2 3 4 5 8 7 6

[6,8]中6最小(6,8交换)——>1 2 3 4 5 6 7 8

[7,8]中6最小(7,8不换)——>1 2 3 4 5 6 7 8

排序完成

代码块1:

void selectionSort(int 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]);//C++标准库内置函数,头文件为algorithm
        }
}

 2、使用模板(泛型)编写算法

添加模板template<typename T>,即可定义T类型数组。如:

void selectionSort(T A[],int n);//T代替了之前的int型

这样我们就可以使用更多类型的数组,使排序更加多样化。如:

代码块2:

int a[10]={2,3,5,7,6,1,4,9,8,10};
    selectionSort(a,10);
    for(int i=0;i<10;i++)
    cout<<a[i]<<" ";
    cout<<endl;

    float b[4]={2.1,3.2,1.8,4.9};
    selectionSort(b,4);
    for(int i=0;i<4;i++)
    cout<<b[i]<<" ";
    cout<<endl;
    
    string c[4]={"B","D","C","A"};
    selectionSort(c,4);
    for(int i=0;i<4;i++)
    cout<<c[i]<<" ";
    cout<<endl;

运行结果:

以上,我使用int、float和string三种类型完成对应类型的排序工作,但我只调用了一种函数。这就是模板(泛型)带来的好处,可以引用不同类型的数据类型,使自己的算法更加灵活多变。

3、使用头文件定义Student结构体,实现学生分数排序。

student.h

#pragma once
#ifndef SELECTIONSORT_STUDENT_H
#define SELECTIONSORT_STUDENT_H

#include<iostream>
#include<string>

using namespace std;

struct Student {//结构体的定义
	string name;//定义名字
	int score;//定义成绩

	bool operator<(const Student &otherStudent){//方法的重载
		return score < otherStudent.score;
	}
	friend ostream&  operator<<(ostream &os, const Student  &Student) {
		os << "Student:"<< Student.name << " "<<Student.score<< endl;
		return os;
	}
};

#endif // !SELECTIONSORT_STUDENT_H





注:引用头文件——>#include"student.h"  注意,自己声明的头文件使用"",而不是<>。

代码块4:

Student d[4] = { {"D",80},{"A",70},{ "C",60 },{ "B",80 } };
	selectionSort(d, 4);
	for (int i = 0; i<4; i++)
		cout << d[i];
	cout << endl;

运行结果:

思考:学生D和学生B成绩一样,是按照什么顺序排列的呢?怎么排列使它更加合理化呢?

     学生D和学生B是按照原先数组给定的顺序排列的;如果使排序更加合理化,我们可以将学生的姓名进行一次排序,类似于我们平时的 姓名首字母排序。

     如何实现name属性排序呢?

    我们在使用方法判断score大小时,可以利用条件运算符(三目运算符)实现name大小比较。即:

    return score != otherStudent.score ? score < otherStudent.score : name < otherStudent.name;

   是否相等——>不相等比较成绩

                 ——>相等则比较姓名

代码块5:

return score != otherStudent.score ? score < otherStudent.score : name < otherStudent.name;

  运行结果:

学生B的字母是排在学生D的字母前面的。

       由此可见,自定义比较的威力是非常强大的,要学会自己定义,深入思考算法精髓,使自己的编程更加个性化,而不是仅仅局限于现成的代码和算法,甘心做个代码打印机。

4、随机生成算法测试用例

       后面在比较不同排序算法效率的时候,可能会用到一万、十万、百万这种量级的数组,对于这种数组,不能手动生成。为此写一个新的方法生成随机数组,以及其他测试相关的辅助方法。

     

猜你喜欢

转载自blog.csdn.net/biubiuxin/article/details/81151176
今日推荐