int schoolToIndex(string school) {
if (school == "UCB") return 0;
if (school == "UCD") return 1;
if (school == "UCI") return 2;
if (school == "UCLA") return 3;
if (school == "UCM") return 4;
if (school == "UCSD") return 5;
if (school == "UCSF") return 6;
cerr << "Unknown school " << school << endl;
return -1;
}
void sortByGroupById2(Student students[], int len) {
int numberofschools = 7;
int counters[numberofschools];
for (int i = 0; i < numberofschools; i++) {
counters[i] = 0;
}
for (int i = 0; i < numberofschools; i++) {
counters[schoolToIndex(students[i].getSchool())]++;
}
Student *sortedArray = new Student[len];
for (int i = 0; i < len; i++) {
sortedArray[counters[schoolToIndex(students[i].getSchool())]] = students[i];
counters[schoolToIndex(students[i].getSchool())]++;
}
for (int i = 0; i < len; i++) {
students[i] = sortedArray[i];
}
}
int main() {
const int LEN = 350000;
// Rough timing
Student* uc2 = readStudentsFromFile("uc_students_sorted_by_id.txt", LEN);
time(&start);
sortByGroupById2(uc2, LEN);
time(&end);
cout << "Using counting sort it took " << difftime(end, start) << " seconds." << endl;
writeStudentsToFile(uc1, LEN, "uc_by_school_by_id1.txt");
writeStudentsToFile(uc2, LEN, "uc_by_school_by_id2.txt");
return 0;
}
我遇到的具体问题在于代码
sortedArray[counters[schoolToIndex(students[i].getSchool())]] = students[i],
我的起始索引sortedArray
是学校的学生人数。我不确定该如何做的是将起始指数作为之前学校的累计学生人数。
例如,如果我想要UCLA的起始索引,我需要添加UCB和UCD和UCI的学生数量,以获得此桶的起始索引。
所以我的行动计划是让计数器数组存储学生人数的组合值。 例如,如果我的计数器数组有[5,10,15,20]作为学生数,我希望它存储[5,15,30,50]作为我的sortedArray的开始索引数组。
我可以使用任何方法吗?我使用递归吗?
答案 0 :(得分:2)
计数排序的一部分是将counters[]
数组从简单的直方图转换为索引转换为sortedArray[]
。
为此,您使用名为部分和的算法。
对于每个元素,使其等于所有先前元素加上该元素的总和。例如:
0 1 3 0 4 0 --> 0 1 4 4 7 7
(您可以手动执行此操作或使用std::partial_sum()
中的<numeric>
功能。)
现在,您可以使用索引将内容移动到输出中的最后位置。为了保持稳定,请从students[]
中的 last 元素开始,然后在 histogram 输出索引数组中查找。
从值中减去一个(修改输出索引)并将源元素复制到最终数组:
for (int i = len; i-->0; )
{
sortedArray[ --counters[ students[i].getSchool() ] ] = students[i];
}
希望这有帮助。
答案 1 :(得分:0)
对于一系列起始索引,您可能最终想要的是[0,5,15,30](请注意,最后一次计数为20不使用)。您可以使计数器1元素更大来执行此操作,或者您可以使用两个计数变量。计数需要扫描所有学生,即len,而不仅仅是学校数量。
使用两个临时变量sum和cnt:
for (int i = 0; i < len; i++) {
counters[schoolToIndex(students[i].getSchool())]++;
}
sum = 0;
for (int i = 0; i < numberofschools; i++) {
cnt = counters[schoolToIndex(students[i].getSchool())];
counters[schoolToIndex(students[i].getSchool())] = sum;
sum += cnt;
}
如果你把柜台做得更大:
int counters[numberofschools+1];
// ...
for (int i = 0; i <= numberofschools; i++) {
counters[i] = 0;
}
for (int i = 0; i < len; i++) {
// note the [1 + ...] only used here, not later in the actual sort
counters[1+schoolToIndex(students[i].getSchool())]++;
}
for (int i = 2; i <= numberofschools; i++) {
counters[schoolToIndex(students[i ].getSchool())] +=
counters[schoolToIndex(students[i-1].getSchool())];
}
在任何一种情况下,都不使用最后一个计数/索引,因为它是数据末尾的索引,并且该数组将用作起始索引的数组。
从第一个元素开始到最后一个元素结束,排序将是稳定的。我看到另一个答案,另一个方法是从最后一个元素开始向后遍历到第一个元素也是稳定的,但不像从第一个元素开始那样缓存友好。