您能解释一下以下两种算法的工作原理吗?
int countSort(int arr[], int n, int exp)
{
int output[n];
int i, count[n] ;
for (int i=0; i < n; i++)
count[i] = 0;
for (i = 0; i < n; i++)
count[ (arr[i]/exp)%n ]++;
for (i = 1; i < n; i++)
count[i] += count[i - 1];
for (i = n - 1; i >= 0; i--)
{
output[count[ (arr[i]/exp)%n] - 1] = arr[i];
count[(arr[i]/exp)%n]--;
}
for (i = 0; i < n; i++)
arr[i] = output[i];
}
void sort(int arr[], int n)
{
countSort(arr, n, 1);
countSort(arr, n, n);
}
我想在这个数组中应用算法:
在调用函数countSort(arr,n,1)之后,我们得到了这个:
当我调用函数countSort(arr,n,n)时,在此for循环中:
for (i = n - 1; i >= 0; i--)
{
output[count[ (arr[i]/exp)%n] - 1] = arr[i];
count[(arr[i]/exp)%n]--;
}
我得到输出[-1] = arr [4]。
但阵列没有这样的位置......
我做错了吗?
编辑:考虑到数组arr [] = {10,6,8,2,3},数组计数将包含以下元素:
这些数字代表什么?我们如何使用它们?
答案 0 :(得分:2)
计算排序非常简单 - 让我们假设您有一个包含范围1..3
中数字的数组:
[3,1,2,3,1,1,3,1,2]
您可以计算数组中每个数字出现的次数:
count[1] = 4
count[2] = 2
count[3] = 3
现在您知道在排序数组中,
号码1
将占据0..3
个位置(从0
到count[1] - 1
),然后是
位置2
上的号码4..5
(从count[1]
到count[1] + count[2] - 1
),然后是
位置3
上的号码6..8
(从count[1] + count[2]
到count[1] + count[2] + count[3] - 1
)。
现在您知道每个数字的最终位置,您只需将每个数字插入正确的位置即可。这基本上是countSort
函数的作用。
但是,在现实生活中,您的输入数组不会只包含范围1..3
中的数字,因此解决方案是首先对最低有效位(LSD)上的数字进行排序,然后对LSD-1 ...进行排序。最重要的数字
这样,您可以通过对范围0..9
中的数字进行排序(十进制数字系统中的单个数字范围)来对较大的数字进行排序
此代码:(arr[i]/exp)%n
中的countSort
仅用于获取这些数字。 n
是数字系统的基础,因此对于十进制,您应使用n = 10
,exp
应以1
开头,并在每次迭代中乘以基数以获得连续数字。
例如,如果我们想从右侧获得第三位数字,我们会使用n = 10
和exp = 10^2
:
x = 1234
,
(x/exp)%n = 2
。
此算法称为基数排序,在维基百科上详细解释:http://en.wikipedia.org/wiki/Radix_sort
答案 1 :(得分:1)
花了一些时间来挑选你的countSort
例程并试图确定你正在做什么与普通的radix
排序相比。有些版本会分割迭代和实际的排序例程,这似乎是您尝试使用countSort
和sort
函数的方法。然而,经过这次练习后,很明显你错过了包括排序例程的必要部分。在修复原始代码中的各种编译/声明问题后,以下内容会添加您忽略的部分。
在countSort
函数中,count
数组的大小错误。它必须是base
的大小,在本例中为10
。 (您有5
)您在整个函数中混淆了exp
和base
的使用。 exp
变量逐步通过10
的幂,允许您在与modulo base
操作结合时获取数组中每个元素的值和位置。你改为modulo n
了。这个问题也渗透到你的循环范围中,你有许多循环索引迭代0 < n
,其中正确的范围是0 < base
。
您错过了在原始数组中找到最大值,然后用于限制通过数组的次数来执行排序。实际上,countSort
中的所有现有循环都必须属于外循环迭代while (m / exp > 0)
。最后,您在外部循环中省略了将exp
增量应用于对数组中的每个元素应用排序所必需的。我猜你刚才感到困惑,但我赞扬你努力重写排序例程而不仅仅是从其他地方复制/粘贴。 (您可能已复制/粘贴,但如果是这样的话,则还有其他问题......)
随着每个问题的解决,排序工作。看看变化并了解它在做什么。 radix sort/count sort
是distribution sorts
依赖于数字出现的位置和操纵索引而不是将值相互比较,这使得这种类型的排序首先难以理解。如果您有任何疑问,请告诉我。我尝试在整个函数中保留您的命名约定,添加了一对被省略的并且防止以10
为基础进行硬编码。
#include <stdio.h>
void prnarray (int *a, int sz);
void countSort (int arr[], int n, int base)
{
int exp = 1;
int m = arr[0];
int output[n];
int count[base];
int i;
for (i = 1; i < n; i++) /* find the maximum value */
m = (arr[i] > m) ? arr[i] : m;
while (m / exp > 0)
{
for (i = 0; i < base; i++)
count[i] = 0; /* zero bucket array (count) */
for (i = 0; i < n; i++)
count[ (arr[i]/exp) % base ]++; /* count keys to go in each bucket */
for (i = 1; i < base; i++) /* indexes after end of each bucket */
count[i] += count[i - 1];
for (i = n - 1; i >= 0; i--) /* map bucket indexes to keys */
{
output[count[ (arr[i]/exp) % base] - 1] = arr[i];
count[(arr[i]/exp)%n]--;
}
for (i = 0; i < n; i++) /* fill array with sorted output */
arr[i] = output[i];
exp *= base; /* inc exp for next group of keys */
}
}
int main (void) {
int arr[] = { 10, 6, 8, 2, 3 };
int n = 5;
int base = 10;
printf ("\n The original array is:\n\n");
prnarray (arr, n);
countSort (arr, n, base);
printf ("\n The sorted array is\n\n");
prnarray (arr, n);
printf ("\n");
return 0;
}
void prnarray (int *a, int sz)
{
register int i;
printf (" [");
for (i = 0; i < sz; i++)
printf (" %d", a[i]);
printf (" ]\n");
}
<强>输出:强>
$ ./bin/sort_count
The original array is:
[ 10 6 8 2 3 ]
The sorted array is
[ 2 3 6 8 10 ]