Algorithm 最高有效v.s.最低有效基数排序

Algorithm 最高有效v.s.最低有效基数排序,algorithm,sorting,radix-sort,Algorithm,Sorting,Radix Sort,如果我只需要对ASCII字符组成的字符串进行排序,想知道使用最高有效v.s.最低有效基数排序之间的区别是什么?我认为他们应该有相同的结果,但被下面链接的陈述弄糊涂了,如果有人能帮助澄清,那就太好了 最高有效数字(MSD)基数排序可用于按字典顺序对键进行排序。与最低有效位(LSD)基数排序不同,最高有效位基数排序不一定保留重复键的原始顺序 提前感谢,, LinLSD基数排序可以在每次传递后逻辑连接已排序的存储箱(如果使用计数/基数排序,则将其视为单个存储箱)。MSD基数排序必须在每次传递后对每个

如果我只需要对ASCII字符组成的字符串进行排序,想知道使用最高有效v.s.最低有效基数排序之间的区别是什么?我认为他们应该有相同的结果,但被下面链接的陈述弄糊涂了,如果有人能帮助澄清,那就太好了

最高有效数字(MSD)基数排序可用于按字典顺序对键进行排序。与最低有效位(LSD)基数排序不同,最高有效位基数排序不一定保留重复键的原始顺序

提前感谢,,
Lin

LSD基数排序可以在每次传递后逻辑连接已排序的存储箱(如果使用计数/基数排序,则将其视为单个存储箱)。MSD基数排序必须在每次传递后对每个箱子进行独立递归排序。如果按字节排序,则第一次通过后256个存储箱,第二次通过后65536个存储箱,第三次通过后16777216(1600万)个存储箱

这就是为什么旧的卡片分拣机首先对数据LSD进行排序。链接到其中一个正在运行的视频。卡片被送入并面朝下落入滑槽。在视频中,卡片分拣机将卡片放入存储箱“0”至“9”,然后操作员从0存储箱中取出卡片,然后从1存储箱中取出卡片,并将其放在0存储箱卡片的顶部(后面),然后将2存储箱卡片放在卡组的后面,依此类推,“连接”存储箱中的卡片。对于大型卡片组,卡片分拣机上方的每个箱子上方都会设置一组架子,以便在卡片组太大而无法用手握住时放置卡片

32位无符号整数的C++ C++ LSD基数排序,其中每个“数字”是字节。大多数代码生成一个计数矩阵,将其转换为标记可变大小箱子之间边界的索引。实际的基数排序在最后一个嵌套循环中

//  a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
{
size_t mIndex[4][256] = {0};            // count / index matrix
size_t i,j,m,n;
uint32_t u;
    for(i = 0; i < count; i++){         // generate histograms
        u = a[i];
        for(j = 0; j < 4; j++){
            mIndex[j][(size_t)(u & 0xff)]++;
            u >>= 8;
        }       
    }
    for(j = 0; j < 4; j++){             // convert to indices
        m = 0;
        for(i = 0; i < 256; i++){
            n = mIndex[j][i];
            mIndex[j][i] = m;
            m += n;
        }       
    }
    for(j = 0; j < 4; j++){             // radix sort
        for(i = 0; i < count; i++){     //  sort by current lsb
            u = a[i];
            m = (size_t)(u>>(j<<3))&0xff;
            b[mIndex[j][m]++] = u;
        }
        std::swap(a, b);                //  swap ptrs
    }
    return(a);
}
//a是输入数组,b是工作数组
uint32_t*半径排序(uint32_t*a、uint32_t*b、大小计数)
{
size_t mIndex[4][256]={0};//计数/索引矩阵
尺寸i,j,m,n;
uint32_t u;
对于(i=0;i>=8;
}       
}
对于(j=0;j<4;j++){//转换为索引
m=0;
对于(i=0;i<256;i++){
n=mIndex[j][i];
mIndex[j][i]=m;
m+=n;
}       
}
对于(j=0;j<4;j++){//基数排序
对于(i=0;im=(size_t)(u>>(j让您困惑的是,几乎所有LSD基数排序都保留重复键的顺序。这是因为它们完全依赖此属性工作。例如,如果您有两次这样的迭代,则先按1位排序,然后按10位排序:

22        21        11
21   ->   11   ->   21
11        22        22
当我们按十进行排序时,我们需要保留按一排序时得到的打破平局的顺序,以便21和22以正确的顺序出现,即使它们在10的位置上有相同的数字。如果您以相同的方式实现第一次排序(按一),那么您必须执行所有其他排序(为什么不执行?),那么排序是稳定的

MSD基数排序可以使用与LSD基数排序相同的排序步骤来编写,在这种情况下,它也将是稳定的。但是,还有其他更有效的方法来实现MSD基数排序,但它们不具有此属性

不保留顺序或重复项的MSD first基数排序通常在适当的位置,即,它们不分配单独的数组来保存已排序的元素


请注意,如果您只是通过比较字符串的ASCII码点对字符串列表进行排序,则所有这些都没有任何区别。“保留重复键的顺序”仅在它们附加了额外信息时才起作用。例如,如果键具有关联的值,或者如果您以独立于大小写的方式进行排序,并且希望“Abe”和“Abe”的顺序与它们的顺序相同。

谢谢Matt,投赞成票。:)在你的评论中,“通常更有效的方法来实现没有此属性的MSD基数排序”,在什么样的实现中MSD不能保持顺序?我能想到的唯一方法就是像LSD那样。非常感谢你的建议。:)顺便说一句,马特做了进一步的分析,在你的22、21、11的例子中,当使用MSD第一次排序时,我们先对十进行排序,然后在对十进行排序时,我们还可以保留“平分顺序”"当我们以十为单位进行排序时,我们得到了答案。问题是什么?谢谢。是的,正如我所说,您可以先使用MSD的相同排序步骤,这将保持顺序。我将添加一条评论,说明不使用MSD的方法。感谢Matt,期待您的更新。:)您阅读了rcgldr的回复吗?似乎使用MSD会增加使用更多垃圾箱的成本--例如,如果我们按字节进行基数排序,假设有几个元素的MS字节是0xFE,它们的第二个MS字节可能比一些MS字节小于0xFE的元素小,在这种情况下,如果我们纯粹按第二个MS字节排序,而不考虑顺序或MS字节,那么顺序将是错误的——这就是为什么我们需要256*256个BIN作为第二个MS字节的原因?这是正确的理解吗?谢谢。执行MSB第一次排序不需要更多的bin。按char0排序后,可以递归对每个bin分别进行排序。每个第二级排序中的所有字符串都具有相同的第一个char,因此您仍然只需要256个bin。为了提高排序速度,有许多小事情可以起作用。您好rcgldr,做了更多的研究,我想我已经明白了“LSD基数排序可以在每次传递后逻辑地连接已排序的箱子”的意思,但MSD不能。例如,如果我们按字节进行基数排序,假设一些元素的MS字节为0xFE,它们的第二个MS字节可能比MS字节小于0xFE的某些元素小