我一直在网上浏览一段时间,我想知道是否通常使用Radix Sort的'稳定'的事实实现?
基数排序的两个分类是最低有效数字(LSD)基数排序和最高有效数字(MSD)基数排序。
寻找LSD或MSD的例子。
答案 0 :(得分:5)
我的版本更详细,但即使对于大量项目也能快速执行:
var testArray = [ 331, 454, 230, 34, 343, 45, 59, 453, 345, 231, 9 ];
function radixBucketSort (arr) {
var idx1, idx2, idx3, len1, len2, radix, radixKey;
var radices = {}, buckets = {}, num, curr;
var currLen, radixStr, currBucket;
len1 = arr.length;
len2 = 10; // radix sort uses ten buckets
// find the relevant radices to process for efficiency
for (idx1 = 0;idx1 < len1;idx1++) {
radices[arr[idx1].toString().length] = 0;
}
// loop for each radix. For each radix we put all the items
// in buckets, and then pull them out of the buckets.
for (radix in radices) {
// put each array item in a bucket based on its radix value
len1 = arr.length;
for (idx1 = 0;idx1 < len1;idx1++) {
curr = arr[idx1];
// item length is used to find its current radix value
currLen = curr.toString().length;
// only put the item in a radix bucket if the item
// key is as long as the radix
if (currLen >= radix) {
// radix starts from beginning of key, so need to
// adjust to get redix values from start of stringified key
radixKey = curr.toString()[currLen - radix];
// create the bucket if it does not already exist
if (!buckets.hasOwnProperty(radixKey)) {
buckets[radixKey] = [];
}
// put the array value in the bucket
buckets[radixKey].push(curr);
} else {
if (!buckets.hasOwnProperty('0')) {
buckets['0'] = [];
}
buckets['0'].push(curr);
}
}
// for current radix, items are in buckets, now put them
// back in the array based on their buckets
// this index moves us through the array as we insert items
idx1 = 0;
// go through all the buckets
for (idx2 = 0;idx2 < len2;idx2++) {
// only process buckets with items
if (buckets[idx2] != null) {
currBucket = buckets[idx2];
// insert all bucket items into array
len1 = currBucket.length;
for (idx3 = 0;idx3 < len1;idx3++) {
arr[idx1++] = currBucket[idx3];
}
}
}
buckets = {};
}
}
radixBucketSort(testArray);
console.dir(testArray);
答案 1 :(得分:1)
Javascript LSD排序:
var counter = [[]];
function sortLSD(array, maxDigitSymbols) {
var mod = 10;
var dev = 1;
for (var i = 0; i < maxDigitSymbols; i++, dev *= 10, mod *= 10) {
for (var j = 0; j < array.length; j++) {
var bucket = parseInt((array[j] % mod) / dev);
if (counter[bucket] == null ) {
counter[bucket] = [];
}
counter[bucket].push(array[j]);
}
var pos = 0;
for (var j = 0; j < counter.length; j++) {
var value = null ;
if (counter[j] != null ) {
while ((value = counter[j].shift()) != null ) {
array[pos++] = value;
}
}
}
}
return array;
}
var test = [22, 1,2,9,3,2,5,14,66];
console.log(sortLSD(test, 2));
答案 2 :(得分:1)
使用以下代码,您可以传递包含大量项目的数组。
var counter = [
[]
]; // Radix sort Array container to hold arrays from 0th digit to 9th digits
function radixSortLSD(array) {
var max = 0,
mod = 10,
dev = 1; //max
for (var i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
// determine the large item length
var maxDigitLength = (max + '').length;
for (var i = 0; i < maxDigitLength; i++, dev *= 10, mod *= 10) {
for (var j = 0; j < array.length; j++) {
var bucket = Math.floor((array[j] % mod) / dev); // Formula to get the significant digit
if (counter[bucket] == undefined) {
counter[bucket] = [];
}
counter[bucket].push(array[j]);
}
var pos = 0;
for (var j = 0; j < counter.length; j++) {
var value = undefined;
if (counter[j] != undefined) {
while ((value = counter[j].shift()) != undefined) {
array[pos++] = value;
}
}
}
}
console.log("ARRAY: " + array);
};
var sampleArray = [1, 121, 99553435535353534, 345, 0];
radixSortLSD(sampleArray);
答案 3 :(得分:1)
以下函数对Uint32值进行LSB基数排序。顺便说一下,它比内置排序功能更快。
它使用类型化数组来提高性能,但是如果传递普通数组,只要它们只包含32位值就可以正常工作:
function radixSortUint32(input) {
const arrayConstr = input.length < (1 << 16) ?
Uint16Array :
Uint32Array;
const numberOfBins = 256 * 4;
let count = new arrayConstr(numberOfBins);
let output = new Uint32Array(input.length);
// count all bytes in one pass
for (let i = 0; i < input.length; i++) {
let val = input[i];
count[val & 0xFF]++;
count[((val >> 8) & 0xFF) + 256]++;
count[((val >> 16) & 0xFF) + 512]++;
count[((val >> 24) & 0xFF) + 768]++;
}
// create summed array
for (let j = 0; j < 4; j++) {
let t = 0,
sum = 0,
offset = j * 256;
for (let i = 0; i < 256; i++) {
t = count[i + offset];
count[i + offset] = sum;
sum += t;
}
}
for (let i = 0; i < input.length; i++) {
let val = input[i];
output[count[val & 0xFF]++] = val;
}
for (let i = 0; i < input.length; i++) {
let val = output[i];
input[count[((val >> 8) & 0xFF) + 256]++] = val;
}
for (let i = 0; i < input.length; i++) {
let val = input[i];
output[count[((val >> 16) & 0xFF) + 512]++] = val;
}
for (let i = 0; i < input.length; i++) {
let val = output[i];
input[count[((val >> 24) & 0xFF) + 768]++] = val;
}
return input;
}
以下是您对Int32
值重复使用上述内容的方法:
function radixSortInt32(input) {
// make use of ArrayBuffer to "reinterpret cast"
// the Int32Array as a Uint32Array
let uinput = input.buffer ?
new Uint32Array(input.buffer):
Uint32Array.from(input);
// adjust to positive nrs
for (let i = 0; i < uinput.length; i++) {
uinput[i] += 0x80000000;
}
// re-use radixSortUint32
radixSortUint32(uinput);
// adjust back to signed nrs
for (let i = 0; i < uinput.length; i++) {
uinput[i] -= 0x80000000;
}
// for plain arrays, fake in-place behaviour
if (input.buffer === undefined){
for (let i = 0; i < input.length; i++){
input[i] = uinput[i];
}
}
return input;
}
Float32
值的类似技巧:
function radixSortFloat32(input) {
// make use of ArrayBuffer to "reinterpret cast"
// the Float32Array as a Uint32Array
let uinput = input.buffer ?
new Uint32Array(input.buffer) :
new Uint32Array(Float32Array.from(input).buffer);
// Similar to radixSortInt32, but uses a more complicated trick
// See: http://stereopsis.com/radixSort.html
for (let i = 0; i < uinput.length; i++) {
if (uinput[i] & 0x80000000) {
uinput[i] ^= 0xFFFFFFFF;
} else {
uinput[i] ^= 0x80000000;
}
}
// re-use radixSortUint32
radixSortUint32(uinput);
// adjust back to original floating point nrs
for (let i = 0; i < uinput.length; i++) {
if (uinput[i] & 0x80000000) {
uinput[i] ^= 0x80000000;
} else {
uinput[i] ^= 0xFFFFFFFF;
}
}
if (input.buffer === undefined){
let floatTemp = new Float32Array(uinput.buffer);
for(let i = 0; i < input.length; i++){
input[i] = floatTemp[i];
}
}
return input;
}
我制作了一组这些函数,它们适用于32位或更少的所有TypedArrays。那就是:
Full gist here。我以后可能会去Float64,然后基本上我们会支持所有 javascript数字。
TypedArray benchmarks shows radix beats the built-in sort function
It's faster with plain arrays too, although not quite as much because of the added overhead
答案 4 :(得分:0)
我在CRLS第3版第8.3节中遇到了基数排序
这本书提供了基数排序的神秘起源。它描述了MSD版本过时和棘手。它还建议实施LSD。
这里我使用这种技术提供基数排序的实现。
让我们从伪代码开始:
/**
* @param k: the max of input, used to create a range for our buckets
* @param exp: 1, 10, 100, 1000, ... used to calculate the nth digit
*/
Array.prototype.countingSort = function (k, exp) {
/* eslint consistent-this:0 */
/* self of course refers to this array */
const self = this;
/**
* let's say the this[i] = 123, if exp is 100 returns 1, if exp 10 returns 2, if exp is 1 returns 3
* @param i
* @returns {*}
*/
function index(i) {
if (exp)
return Math.floor(self[i] / exp) % 10;
return i;
}
const LENGTH = this.length;
/* create an array of zeroes */
let C = Array.apply(null, new Array(k)).map(() => 0);
let B = [];
for (let i = 0; i < LENGTH; i++)
C[index(i)]++;
for (let i = 1; i < k; i++)
C[i] += C[i - 1];
for (let i = LENGTH - 1; i >= 0; i--) {
B[--C[index(i)]] = this[i];
}
B.forEach((e, i) => {
self[i] = e
});
}
这是唯一棘手的部分,其余部分非常简单
Array.prototype.radixSort = function () {
const MAX = Math.max.apply(null, this);
/* let's say the max is 1926, we should only use exponents 1, 10, 100, 1000 */
for (let exp = 1; MAX / exp > 1; exp *= 10) {
this.countingSort(10, exp);
}
}
现在,您可以在此处测试此方法
let a = [589, 111, 777, 65, 124, 852, 345, 888, 553, 654, 549, 448, 222, 165];
a.radixSort()
console.log(a);
最后,如书中所提到的,这种特殊算法的工作原理只是因为count-sort是一种就地排序算法,这意味着如果两个元素相关联,它们在输入数组中的出现顺序就会被保留。
答案 5 :(得分:0)
function radixSort(arr) {
const base = 10;
let divider = 1;
let maxVal = Number.NEGATIVE_INFINITY;
while (divider === 1 || divider <= maxVal) {
const buckets = [...Array(10)].map(() => []);
for (let val of arr) {
buckets[Math.floor((val / divider) % base)].push(val);
maxVal = val > maxVal ? val : maxVal;
}
arr = [].concat(...buckets);
divider *= base;
}
return arr;
}
免责声明:仅适用于正整数。
Math.max
,因为它会为超大型数组使用大量资源。答案 6 :(得分:0)
通过按位运算对LSD基数进行排序可能是这样的:
const initialMask = 0b1111;
const bits = 4;
const getBuckets = () => Array.from(
{ length: (2 * initialMask) + 1 },
() => [],
);
function radixSort(array) {
let max = 0;
array.forEach(n => {
const abs = Math.abs(n);
if (abs > max) max = abs;
});
if (max >= 0x80000000) {
throw new Error('cannot perform bitwise operations on numbers >= 0x80000000');
}
for (
let mask = initialMask,
shifted = 0,
buckets = getBuckets();
true;
mask = (mask << bits),
shifted = (shifted + bits),
buckets = getBuckets()
) {
array.forEach(n => {
const digit = mask & Math.abs(n);
const bucket = (Math.sign(n) * (digit >> shifted)) + initialMask;
buckets[bucket].push(n);
});
let i = 0;
buckets.forEach(bucket => bucket.forEach(n => {
array[i] = n;
i += 1;
}));
if ((max ^ mask) <= mask) break;
}
}
const getArray = () => Array.from(
{ length: 1e6 },
() => Math.floor(Math.random() * 0x80000000) * Math.sign(Math.random() - 0.5),
);
const isSorted = array => {
for (let i = 1; i < array.length; i += 1) {
if (array[i - 1] > array[i]) return false;
}
return true;
}
const radixArray = getArray();
const nativeArray = radixArray.slice();
const radixStart = +new Date();
radixSort(radixArray);
const radixEnd = +new Date();
const nativeStart = +new Date();
nativeArray.sort();
const nativeEnd = +new Date();
document.write(`
<dl>
<dt>Sorted array in</dt>
<dd>${radixEnd - radixStart}ms</dd>
<dt>Properly sorted</dt>
<dd>${isSorted(radixArray)}</dd>
<dt>Sorted with Array.prototype.sort in</dt>
<dd>${nativeEnd - nativeStart}ms</dd>
</dl>
`);
这是怎么回事?
我们以8为底进行排序(0b1111
帮助概念化按位运算)。
我们创建0b1111 * 2 + 1
个存储桶,这是集合[-0b1111 … 0b1111]
中的项目数
我们使用“掩码”获取给定数字的每个基数8位,例如
如果n = 0b101000101010
,n & 0b1111
给我们0b1010
,这是n
的第一个8位基数。
对于每次迭代,我们得到n & 0b11110000
,然后得到n & 0b111100000000
,它将每个连续的基数8位隔离开。
对于n & 0b11110000
,我们得到0b00100000
,从中我们想要0b0010
,因此我们向右移4位。下一次迭代将移位8位,依此类推。
要考虑负值,我们基本上是同时执行两个基数排序:负值反向排序,正值按正常顺序排序。如果数字是负数,我们说数字7应该是0,6是1,1,5是2,等等。
如果它是正数,我们说基数7应该在索引14、6在13处,等等。
最后的检查-(max ^ mask) <= mask
-确定掩码是否采用最大值的最高有效位。如果有,则对数组进行排序。
当然,基数排序只能与整数一起使用。
如果您需要使用大于0x80000000
的数字,则可以使用字符串来实现。
答案 7 :(得分:0)
我确信所有这些答案都能奏效,但是我坚决相信可以解释幕后的情况。因此,这是辅助方法的答案:
ref