基数排序工作

时间:2012-06-28 19:59:26

标签: c radix-sort

我想知道以下基数排序程序的逻辑。

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>

typedef unsigned uint;
#define swap(a, b) { tmp = a; a = b; b = tmp; }
#define each(i, x) for (i = 0; i < x; i++)

/* sort unsigned ints */
static void rad_sort_u(uint *from, uint *to, uint bit)
{
    if (!bit || to < from + 1) return;

uint *ll = from, *rr = to - 1, tmp;
while (1) {
    /* find left most with bit, and right most without bit, swap */
    while (ll < rr && !(*ll & bit)) ll++;
    while (ll < rr &&  (*rr & bit)) rr--;
    if (ll >= rr) break;
    swap(*ll, *rr);
}

if (!(bit & *ll) && ll < to) ll++;
bit >>= 1;

rad_sort_u(from, ll, bit);
rad_sort_u(ll, to, bit);
}

/* sort signed ints: flip highest bit, sort as unsigned, flip back */
static void radix_sort(int *a, const size_t len)
{
size_t i;
uint *x = (uint*) a;

each(i, len) x[i] ^= INT_MIN;
rad_sort_u(x, x + len, INT_MIN);
each(i, len) x[i] ^= INT_MIN;
}

static inline void radix_sort_unsigned(uint *a, const size_t len)
{
rad_sort_u(a, a + len, (uint)INT_MIN);
}

int main(void)
{
int len = 16, x[16], i;
size_t len = 16, i;
each(i, len) x[i] = rand() % 512 - 256;

radix_sort(x, len);

each(i, len) printf("%d%c", x[i], i + 1 < len ? ' ' : '\n');

return 0;
}

我被困了,因为我不太明白while(1)循环..

到目前为止,我所知道的是:

INT_MIN=-2147483648

这与bit

rad_short_u()的值相同

我调试了程序,由于rand() % 512-256,还会生成一些-ve值,

在第一次传递期间,它将所有-ve值交换到一侧(从开始)和之后的+ ve 从下一次传递开始,它向左移动到1位,这样位的值从此变为1073741824,直到它变为1个数组保持不变。

请帮助我理解程序逻辑。

2 个答案:

答案 0 :(得分:3)

要理解这个程序,您需要了解快速排序和最重要位基数排序。

与quicksort一样,它将数组分成几部分,然后递归地对部分进行排序。它首先根据最高位的值进行分区。然后它在两个部分进行递归。但这一次,对于每一半,它基于第二个最重要的位进行分区。然后它再次划分,并且对于每1/4,它在第3个最重要位上分区......

请注意,虽然我说的是“1/2”,“1/4”等,但它通常不会将数组精确地分成1/2,1 / 4等。每个分区的大小将会取决于数组中数字的分布。对于普通的快速排序,每个分区的大小将取决于被选为“枢轴”的元素,但对于这个“基数快速排序”而言并非如此 - “枢轴”的顺序是固定的。

另外,请注意,与普通快速排序不同,普通快速排序可以在某些输入上变为二次且变得非常慢,这种“快速排序”可以保证以固定的次数完成。实际上,无论输入如何,所需的传球次数都是常数。 (这是基数排序的典型属性 - 性能往往对输入不敏感。)

另一个有趣的属性:正常的快速排序会将数组分成3个部分 - 比枢轴更小,相等和更大。但是这个“快速排序”总是在每次传递时将其输入分成两个部分 - 在被测位置为0位,在1位时为

我认为这个算法的名称实际上是“二元快速排序”。

答案 1 :(得分:0)

您的while(1)循环在无符号整数上按位运行。对于每个位,它从列表的顶部和底部开始,找到第一对整数,其中位设置在底部而不是顶部,并交换它们。这会纠正该位的这些值的排序。

它继续这样做,直到顶部/底部相遇。最后,每次通过while(1)循环都会导致列表中包含该位未设置的所有数字列表,并且该位的所有数字都设置在列表顶部。

然后按列顺序对列表进行排序,从MSB开始,然后是第二个MSB,...,最后是LSB。值INT_MIN对于有符号整数是负的,但对应于二进制补码中的MSB。

x[i] ^= INT_MIN行允许基数排序正确处理负数。有符号整数存储在二进制补码中。这实际上意味着负数的MSB设置。

如果您天真地将基数排序应用于有符号整数,则最终将正数排序为低于负数的索引。

x[i] ^= INT_MIN翻转MSB,解决了这个问题。第二个x[i] ^= INT_MIN翻了回来。