这个算法主要用在关键数字(key number)小的时候,按照关键数字排序的效率是非常高的。
比如我们有6个教室的学生需要按教室号排序,如下是学生的信息数据结构:
struct StudentData
{
string name;
int classNum;
StudentData():name("No Name"), classNum(0){}
StudentData(int num, string na):classNum(num), name(na){}
void print();
};
void StudentData::print()
{
cout<<classNum<<"\t\t"<<name<<endl;
}
需要的空间就是关键数字的最大值的空间,比如关键字最大是6,那么就需要6+1个空间。如果是从后面开始填表,那么就只需要6个空间。
思路:
主要需要三个步骤:
1 新建一个关键数字大小+1的vector vi,并以关键字出现的次数为vi的下标,计算其出现的总次数
2 把前面教室号出现的总次数加起来就是本教室号开始填表的位置
3 根据教室号开始填表,每填写一个学生信息,那么就需要把填表下标+1.
三部曲,就只需要三个循环,如C++程序:
class Solution {
public:
void countingSort(vector<StudentData> &vstu)
{
vector<int> vi(MAXCLASS+1);
//三部曲1:计算所有classNum出现的次数
for(auto x:vstu)
{
vi[x.classNum]++;
}
//三部曲2:计算需要摆放数据的初始位置
for (int i = 1; i < vi.size(); i++)
{
vi[i] += vi[i-1];
}
vector<StudentData> tempVstu(vstu.size());
for(auto x:vstu)
{
//三部曲3: x.classNum是student的教室号,前面的教室学生填写完毕,就开始填写这个教室的学生
//所以这个教室号在其他教室学生信息填写完毕之后才填写,所以开始位置是前面学生总和
//x.classNum-1代表前面所有教室号的学生总数,也是本教室号开始填写学生信息的开始号码
//用这个号码在vi中抽去前面填写好的下标
//++代表填了一个学生信息,那么后面填写的学生号需要+1.
tempVstu[vi[x.classNum-1]++] = x;
}
//复制回原来数列
vstu = tempVstu;
}
};
下面是主程序:
int main()
{
vector<StudentData> vstu;
vstu.push_back(StudentData(1,"s1"));
vstu.push_back(StudentData(2,"s2"));
vstu.push_back(StudentData(3,"s3"));
vstu.push_back(StudentData(5,"s5"));
vstu.push_back(StudentData(4,"s4"));
vstu.push_back(StudentData(3,"s33"));
vstu.push_back(StudentData(6,"s6"));
vstu.push_back(StudentData(2,"s22"));
vstu.push_back(StudentData(5,"s55"));
vstu.push_back(StudentData(2,"s222"));
vstu.push_back(StudentData(6,"s66"));
vstu.push_back(StudentData(4,"s44"));
cout<<"Before sort:\nclass number\tstudent name\n";
for (auto x:vstu)
x.print();
cout<<endl;
Solution solu;
solu.countingSort(vstu);
cout<<"After sort:\nclass number\tstudent name\n";
for (auto x:vstu)
x.print();
system("pause");
return 0;
}
运行结果: