基数排序不排序“一些”数字?

Posted

技术标签:

【中文标题】基数排序不排序“一些”数字?【英文标题】:Radix sort not sorting "some" numbers? 【发布时间】:2017-12-04 07:17:58 【问题描述】:

我正在尝试实现Radix sort,据我所知,它首先比较个位,按正确的顺序放置,然后是十位等等。

我尝试实现它但没有得到令人满意的结果,我在这里做错了什么? 这是我的代码:

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

struct heap 
    int data;
;

int main()

    int arr[18] = 545, 934, 829, 883, 842, 957, 241, 856, 878, 101, 555, 20, 964, 529, 156, 161, 566, 820;
    int i;

    struct heap *heaparr = malloc(sizeof(struct heap) * 18);

    for(i=0; i<18; i++) 
        heaparr[i].data = arr[i];
    

    int k = 0, temp, div = 1;
    int ind = 0;

    while(div<=100 )
        k=0;
        ind = 0;
        while(k<10) 
            for(i=0; i<18; i++) 
                if ((heaparr[i].data/div)% 10 == k) 
                    temp = heaparr[ind].data;
                    heaparr[ind].data = heaparr[i].data;
                    heaparr[i].data = temp;
                    ind++;
                
            
            k = k+1;
        
        div = div*10;
    

    printf("\n");
    free(heaparr);
    return 0;

它给了我结果

20 101 156 161 241 545 555 566 529 842 856 820 829 878 883 957 934 964

应该是这样的。

20 101 156 161 241 529 545 555 566 820 829 842 856 878 883 934 964 957 

【问题讨论】:

您应该使用for (int div = 1; div &lt;= 100; div *= 10),而不是在while 循环中将循环控制拆分为3 行。同样,使用for (int k = 0; k &lt; 10; k++) 而不是在while 循环中将循环控制拆分为3 行。并且请始终如一地缩进你的代码——这真的不难做到,它确实对可读性产生了巨大的影响。 问题是您没有按顺序保留从一开始交换的 other。基数排序要求中间排序是稳定的,你的不是。 谢谢,我会记住这一点@AnttiHaapala 你能解释一下吗? @PaulNicolashunter en.wikipedia.org/wiki/Sorting_algorithm#Stability @PaulNicolashunter 把它画在纸上。 【参考方案1】:

基数排序要求中间排序是稳定的,您可以在Wikipedia 中阅读。这是一张很好的图片,解释了什么是稳定性:

“扑克牌上稳定排序的一个例子。当纸牌用稳定排序按等级排序时,两个 5 在它们最初所在的排序输出中必须保持相同的顺序。当它们用 a 排序时非稳定排序,5s 可能在排序输出中以相反的顺序结束。”


因此,在您的代码中,当您交换两个元素时,您并没有确保从一开始就为您获取的元素保留稳定性并向后推,这打破了基数排序的要求,从而产生不良影响。

受GeeksforGeeks 的启发,我将您的代码转换为使用Counting sort(对于中间排序步骤来说是稳定的):

#include <stdio.h>
#include <stdlib.h>
#include <math.h>       /* pow */

struct heap 
    int data;
;

void print(struct heap* heaparr)

    for(int i = 0; i < 18; i++)
        printf("%d ", heaparr[i].data);
    printf("\n");



// A function to do counting sort of arr[] according to
// the digit represented by exp.
struct heap* countSort(struct heap* heaparr, int n, int exp)

    int output[n]; // output array
    int i, count[10] = 0;

    // Store count of occurrences in count[]
    for (i = 0; i < n; i++)
        count[ (heaparr[i].data/exp)%10 ]++;

    // Change count[i] so that count[i] now contains actual
    //  position of this digit in output[]
    for (i = 1; i < 10; i++)
        count[i] += count[i - 1];

    // Build the output array
    for (i = n - 1; i >= 0; i--)
    
        output[count[ (heaparr[i].data/exp)%10 ] - 1] = heaparr[i].data;
        count[ (heaparr[i].data/exp)%10 ]--;
    

    // Copy the output array to arr[], so that arr[] now
    // contains sorted numbers according to current digit
    for (i = 0; i < n; i++)
        heaparr[i].data = output[i];
    return heaparr;


int main()

    int arr[18] = 545, 934, 829, 883, 842, 957, 241, 856, 878, 101, 555, 20, 964, 529, 156, 161, 566, 820;
    int i;

    struct heap *heaparr = malloc(sizeof(struct heap) * 18);

    for(i=0; i<18; i++) 
        heaparr[i].data = arr[i];
    

    int k = 0, div = 1;

    while(div<=100 )
        k=0;
        while(k<10) 
            for(i=0; i<18; i++) 
                countSort(heaparr, 18, (int)pow(10, heaparr[i].data/div% 10));
            
            k = k+1;
        
        div = div*10;
    
    print(heaparr);
    free(heaparr);
    return 0;

给出:

20 101 156 161 241 529 545 555 566 820 829 842 856 878 883 934 957 964

但是,这应该只是一个让您入门的示例。

【讨论】:

所以我预先检查了 545 是否不早于 529。 很抱歉再次提醒您,但这不会增加复杂性吗?像。如果我再次横向检查基数和其他 O(n^2) 复杂性排序之间的区别,但在某种程度上我认为这可能是一个好方法。谢谢 是的,我明白你的观点@PaulNicolashunter,这就是为什么我在我的例子中只使用了一个稳定的排序。不客气!【参考方案2】:

@gsamaras 击败了我,但这里有一个更小的最小示例。

Start with:
  52, 93, 84, 54, 24

1s 位置中的整个传递将值保留在相同位置。 现在看看处理10s地点时的交换操作:

10s, k=0
10s, k=1
10s, k=2
  24, 93, 84, 54, 52 //The first swap mis-orders the "50s"
   ^---------------^
10s, k=3
10s, k=4
10s, k=5
  24, 54, 84, 93, 52
       ^-------^
  24, 54, 52, 93, 84 //Hence they stay out-of-order when they're moved up
           ^-------^
10s, k=6
10s, k=7
10s, k=8
  24, 54, 52, 84, 93
               ^---^
10s, k=9

考虑将元素向右移动以腾出空间,而不是交换,或者不适当地处理每个小数位(即在两个缓冲区之间交替)。

【讨论】:

你甚至可以删除93, 84 @AnttiHaapala:是的。我是后来才看到的。我留下它是因为有“太小而无法说明”之类的东西 所以我必须占用额外的空间来交换数字?原谅我,后一个找不到。 我不是 100% 确定你在问什么,但我认为这是关于不合时宜的版本。我的意思是:现在,您交换条目,这样您就不必大量移动值。您可以通过扫描heaparr 并在执行第一名时写入新的heaparr2 来实现相同的目的。在 10 位的下一次传球中,颠倒他们的角色:扫描heaparr2 并写信给heaparr。最后,返回最后写入的一个,释放另一个。 “复杂性”有多种含义。在“更难写”的意义上,这取决于你是更喜欢malloc 还是memmove。根据我的经验,我弄乱了memmove 的索引和长度,而不是弄乱了交换指针。还有另一种复杂性,即“计算复杂性”或“计算机需要做多少工作”。从这个意义上说,指针版本更好,因为它避免了重复移动大块数组元素。

以上是关于基数排序不排序“一些”数字?的主要内容,如果未能解决你的问题,请参考以下文章

基数排序 Java 实现

C++ 基数排序算法

基数排序(radixSort)

对 32 位数字使用基数排序

20191209-八大排序之基数排序

基数排序