问题描述
在 the Wikipedia page about Counting Sort 上,它声明:
可以修改算法,使其在作为输入的同一数组中将项目按排序顺序排列,仅使用计数数组作为辅助存储;然而,改进的原位计数排序版本并不稳定。
我很好奇这种算法是如何实现的,但我无权访问引用的来源。有人可以解释一下它是如何工作的吗?
解决方法
计数后,您有每个值的计数数组,例如:
[4,3,1,2]
向右移动:
[0,4,1]
使用累积和,将其更改为排序结果中每个值的起始位置数组:
[0,7,8]
现在您遍历数组,将每个项目交换到其值的开始位置,并增加开始位置,以便下一个将进入正确的位置,但前提是该项目属于相同或更早位置。如果它属于后面的位置,那么就跳过它,当我们到达它时,属于那里的项目将被交换(因为它必须属于前面的位置)。
交换会将新元素带入目标位置,因此在同一位置重复,直到找到未交换的元素。
这是 JavaScript 中的算法,使用从 0 到 9 的值:
const len = 25;
const array = [];
for (let i=0; i<len; ++i) {
array.push(Math.floor(Math.random()*10));
}
console.log("Original array:",String(array));
const cp = [0,0];
for (const val of array) {
++cp[val];
}
console.log("Counts:",String(cp));
for (let i=cp.length-1; i>0; --i) {
cp[i] = cp[i-1];
}
cp[0]=0;
for (let i=1; i<cp.length; ++i) {
cp[i]+=cp[i-1];
}
console.log("Start pointers: ",String(cp));
let pos=0;
while(pos < array.length) {
let v = array[pos];
let dest = cp[v];
if (dest <= pos) {
if (dest < pos) {
// v belongs at an earlier position. Swap it into place
array[pos] = array[dest];
array[dest] = v;
} else {
// v belongs at the same position. don't visit it again
++pos;
}
// increment the pointer for this value
cp[v] = dest+1;
} else {
// v belongs at a later position. Something later must belong here.
// Skip it and let the later item swap in when we get there
++pos;
}
}
console.log("Sorted array:",String(array));
完全未经测试的 C++ 代码
#include <array>
#include <vector>
#include <algorithm>
#include <numeric>
void counting_sort(std::vector<uint8_t>& values) {
std::array<uint8_t,256> count;
for(auto& value : values)
count[value]++; // count
std::partial_sum(count.begin(),count.end(),count.begin()); // sum
std::array<uint8_t,256> position;
position[0] = 0; // first position of first value
std::copy_n(count.begin(),count.size()-1,std::next(position.begin())); // moved by one
int pos = 0;
while (pos < count.size()) {
while (count[pos] > position[pos]) {
auto& from = position[pos]; // current position
auto& to = position[values[from]]; // final position
if (to != from)
std::swap(values[from],// from current position
values[to]); // where the value needs to go
to++; // update destination position
}
pos++;
}
}
在交换的同时,您不断交换,直到应该放在第一个位置的值交换到该位置。
0 // pos
[3,2,1] // values
[0,1] // count
[_0_,3] // count
[_0_,2] // position
1 // pos
[0,_1_,3] // count
[0,_0_,2] // position
values[position[pos]] -> 3
position[3] -> 2
position[pos] -> 0
[_3_,_1_]
swap
[1,3]
position[values[pos]]++
[0,_2_] // position
[0,_3_] // position
1 // pos
[0,3] // position
values[position[pos]] -> 1
position[1] -> 0
position[pos] -> 0
positions are the same so update to
[0,3] // position
[0,3] // position
[0,3] // position
update pos
pos = 2
[0,_2_,3] // position
positions are the same to update to
[0,3] // position
count&position are the same so update pos
pos = 3
count&position are the same so update pos
pos = 4
done