【发布时间】:2018-08-07 08:09:35
【问题描述】:
首先,我知道这里有一个类似的问题: Radix Sort for Negative Integers
但它与这个不重复。
我正在研究基数排序,并且有一个关于 Sedgewick 教授和韦恩教授的 LSD 基数排序实现的问题。
public static void sort(int[] a) {
final int BITS = 32; // each int is 32 bits
final int R = 1 << BITS_PER_BYTE; // each bytes is between 0 and 255
final int MASK = R - 1; // 0xFF
final int w = BITS / BITS_PER_BYTE; // each int is 4 bytes
int n = a.length;
int[] aux = new int[n];
for (int d = 0; d < w; d++) {
// compute frequency counts
int[] count = new int[R+1];
for (int i = 0; i < n; i++) {
int c = (a[i] >> BITS_PER_BYTE*d) & MASK;
count[c + 1]++;
}
// compute cumulates
for (int r = 0; r < R; r++)
count[r+1] += count[r];
// for most significant byte, 0x80-0xFF comes before 0x00-0x7F
if (d == w-1) {
int shift1 = count[R] - count[R/2];
int shift2 = count[R/2];
for (int r = 0; r < R/2; r++)
count[r] += shift1;
for (int r = R/2; r < R; r++)
count[r] -= shift2;
}
// move data
for (int i = 0; i < n; i++) {
int c = (a[i] >> BITS_PER_BYTE*d) & MASK;
aux[count[c]++] = a[i];
}
// copy back
for (int i = 0; i < n; i++)
a[i] = aux[i];
}
最高有效字节发生了什么?它比我想出的任何东西都要优雅。
我对自己解释该代码块的能力没有信心,很明显它处理的是负数,但我不确定如何。
谁能更详细地解释这段代码?
更新
我想我对变量 shift1 和 shift2 的命名也感到困惑。如果我们稍微重命名一下,并添加一两条评论:
if (d == w-1) {
int totalNegatives= count[R] - count[R/2];
int totalPositives= count[R/2];
for (int r = 0; r < R/2; r++)
// all positive number must come after any negative number
count[r] += totalNegatives;
for (int r = R/2; r < R; r++)
// all negative numbers must come before any positive number
count[r] -= totalPositives;
}
这变得更容易理解。
这个想法是第一个正数只能在最后一个负数之后的位置,并且所有正数必须按排序顺序在负数之后。因此,我们只需将总负数的计数添加到所有正数中,以确保正数确实会出现在负数之后。 负数的类比相同。
【问题讨论】:
-
评论 - 建议改进,如果 count 是一个矩阵:
count[w][R+1],那么只需要一次读取即可生成所有计数,然后生成索引(累积总和)。 a[] 和 aux[] 也是引用,因此 a[] 和 aux[] 可以交换(使用类似 int[] tmp 的东西)而不是复制回来。只要基数排序通过的次数是偶数,那么排序的结果将最终回到 a[] 中。这些建议也适用于 C/C++(假设指针用于引用数组)。 -
如果 java 不能很好地优化矩阵的使用,可以使用
w计数实例为w的特定实例硬编码排序,对于w== 4,然后: count0[], count1[], count2[], count3[]. -
@rcgldr 很棒的建议!