这个算法主要用在关键数字(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; }
运行结果: