基数sorting与计数sorting与桶sorting。 有什么不同?

我正在阅读基数,计数和桶sorting的定义,似乎所有这些都只是下面的代码:

public static void sort(int[] a, int maxVal){ int [] bucket=new int[maxVal+1]; for (int i=0; i<bucket.length; i++){ bucket[i]=0; } for (int i=0; i<a.length; i++){ bucket[a[i]]++; } int outPos=0; for (int i=0; i<bucket.length; i++){ for (int j=0; j<bucket[i]; j++){ a[outPos++]=i; } } } 

我知道我不能对,所以我错过了什么? 如果您认为可以帮助解释Java或C语言,请显示代码

我们先用C语言重写一下你的代码,因为C比较熟悉我的解释。 所以让我们回忆一下你的代码:

 int counting_sort(int a[], int a_len, int maxVal) { int i, j, outPos = 0; int bucket_len = maxVal+1; int bucket[bucket_len]; /* simple bucket structure */ memset(bucket, 0, sizeof(int) * bucket_len); /* one loop bucket processing */ for (i = 0; i < a_len; i++) { bucket[a[i]]++; /* simple work with buckets */ } for (i=0; i < bucket_len; i++) { for (j = 0; j < bucket[i]; j++) { a[outPos++] = i; } } return 0; } 

现在让我们给这个家伙一些现实的数据:

[126,348,343,432,316,171,556,223,670,201]

在输出我们有

[126,171,201,223,316,343,348,432,556,670]

看来一切都好? 还没。 让我们看看maxVal。 这是670(!)在这里sorting10个元素的数组我们使用了670个元素的数组,主要是零。 非常。 为了处理这种sorting问题,我们有两种可能的推广方法:

1)第一种方式 – 按数字进行分类。 这被称为基数sorting。 让我们展示一些代码,试图尽可能地对代码进行计数sorting。 再看看评论:

 int radix_sort(int a[], int a_len, int ndigits) { int i; int b[a_len]; int expn = 1; /* additional loop for digits */ for (i = 0; i != ndigits; ++i) { int j; int bucket[10] = {0}; /* still simple buckets */ /* bucket processing becomes tricky */ for (j = 0; j != a_len; ++j) bucket[ a[j] / expn % 10 ]++; for (j = 1; j != 10; ++j) bucket[j] += bucket[j - 1]; for (j = a_len - 1; j >= 0; --j) b[--bucket[a[j] / expn % 10]] = a[j]; for (j = 0; j != a_len; ++j) a[j] = b[j]; expn *= 10; } } 

我们在N附近交易乘数以记忆。 利润? 也许。 但在某些情况下,N附近的乘数非常重要。 程序,每天工作,每周工作与用户看法有很大的不同,即使两者分别工作1 * O(N)和7 * O(N)。 所以我们来到第二个概括:

2)第二种方法 – 使桶更复杂。 这被称为桶sorting。

让我们再次从一些代码开始。 在哲学论点之前,我更喜欢更多的代码。 仍然看看评论,他们是必不可less的。

 int bucket_sort(int a[], int a_len, int maxVal) { int i, aidx; typedef struct tag_list { int elem; struct tag_list *next; } list_t, *list_p; list_p bucket[10] = {0}; /* sophisticated buckets */ /* one loop simple processing with one more inner loop to get sorted buckets (insert-sort on lists, Cormen-style) */ for (i = 0; i != a_len; ++i) { int bnum = (10 * a[i]) / maxVal; list_p bptr = bucket[bnum]; list_p belem = malloc(sizeof(list_t)); belem->elem = a[i]; if (bptr == 0) { bucket[bnum] = belem; belem->next = 0; continue; } else if (a[i] <= bptr->elem) { belem->next = bptr; bucket[bnum] = belem; continue; } else { while (bptr != 0) { if ((bptr->elem <= a[i]) && ((bptr->next == 0) || (bptr->next->elem > a[i]))) { belem->next = bptr->next; bptr->next = belem; break; } } } } /* one loop (looks as two) to get all back */ aidx = 0; for (i = 0; i != 10; ++i) { list_p bptr = bucket[i]; while (bptr) { list_p optr = bptr; a[aidx] = bptr->elem; aidx += 1; bptr = bptr->next; free(optr); } } return 0; } 

那么我们在这里有什么? 我们正在交易一些复杂的桶结构和对dynamic分配内存的要求,但赢得静态内存,平均乘数接近N.

现在让我们回顾一下我们在代码中看到了什么

  1. 计数sorting – 简单的桶,简单的处理,内存开销
  2. 基数sorting – 简单的桶,复杂的处理,速度开销(仍然需要额外的静态内存)
  3. 桶sorting – 精密的桶,简单的处理,需要dynamic内存,平均好

基数和桶sorting是计数sorting的两个有用的概括。 他们有很多相似点和彼此之间的共同点,但是在任何情况下,我们都在失去一些东西并赢得一些东西。 软件工程是关于这些机会之间的平衡。

基数sorting与计数sorting与桶sorting。 有什么不同?

桶sorting将要sorting的键或元素放入桶中。 它们在桶中的位置是任意的,可以是复合键的一部分,也可以是任何你喜欢的分布。 单个桶可能需要进一步分类。

在内存中sorting比在磁盘上sorting要快。 但是,如果你有更多的数据比适合内存,你需要另一种select。 你可以做的是一个水桶sorting,其中水桶足够小,以适应内存。 即每个桶中有大量的条目。 这些你可以快速分类。

基数sorting是特定types的桶sorting。 它从最高的n位或n位开始,可以使用基数sorting等对这些桶进行sorting,直到对每个条目进行sorting。

计数sorting就像使用基数sorting,除了你正在使用整个值。 它不是logging每个对象,而是每个对象都有一个存储桶,只计算出现次数。 如果您的密钥数量有限,而且您有很多重复的密码,则这种方法很有效。

你的代码是简单的计数sorting的变种,没有数据,只是键。

基数sorting是基于这种方法。 计数sorting的问题是内存需求: int [] bucket=new int[maxVal+1]; 。 基数sorting解决了这个问题。 这个想法是使用计数sorting几次,首先是较低的数字,然后更高。 例如,要对可能使用的32位整数进行sorting:

 sort(a, 65535) using lower half as key sort(a, 65535) using higher half as key 

它工作,因为计数sorting是稳定的 – 它保持相同的密钥数据的顺序。 这就像在电子表格中sort by B; sort by Asort by B; sort by A sort by B; sort by A会给您按Asorting的元素,当As相等时按B排列。

桶sorting是计数sorting的一种推广。 你可以用它来从一些可预测的概率分布(例如uniform (0,1) )中sorting实数。 这个想法是使用计数sorting(使用floor(x*N_BUCKETS)作为键),然后只对每个桶单独进行sorting。

根据Geekviewpoint:

基数: http : //www.geekviewpoint.com/java/sorting/radixsort

基数sorting,如计数sorting和桶sorting,是基于整数的algorithm(即input数组的值被认为是整数)。 因此,基数sorting是理论上最快的sortingalgorithm之一。 基数sorting的特殊之处在于它为每个密码(即数字)创build一个桶。 因此,类似于桶sorting,基数sorting中的每个桶必须是可以容纳不同密钥的可增长列表。

存储桶: http : //www.geekviewpoint.com/java/sorting/bucketsort

考虑到计数sorting合理地说是上限,桶sorting实际上是非常好的。 而且计数sorting非常快。 bucketsorting的特殊之处在于它使用散列函数对input数组的键进行分区,以便多个键可以散列到同一个分区。 因此,每个桶必须有效地成为一个可增长的名单; 类似于基数sorting。

计数: http : //www.geekviewpoint.com/java/sorting/countingsort

计数sorting的特别区别在于它为每个值创build一个存储桶,并在每个存储桶中保留一个计数器。 然后每次在input集合中遇到一个值时,相应的计数器就会递增。 由于计数sorting为每个值创build一个桶,所以施加的限制是事先知道input数组中的最大值。

他们在他们的网站上更详细地解释它。

编辑:

  • 如果你使用基数sorting,你的数字是十进制的,那么你需要10个桶,每个数字从0到9。

  • 如果你正在使用计数sorting,那么你需要为input中的每个唯一值设置一个桶(实际上,对于0到max之间的每个值,都需要一个桶)。

  • 如果你正在使用bucketsort,你不知道你将使用多less桶。 无论你正在使用的哈希函数将决定桶的数量。

首先让我们来看看基数sorting和桶sorting之间的区别,因为这通常是一个令人困惑的事情,因为这个想法看起来是一样的。 然后我们看看Counting Sort,它就像是这两个版本的主要版本,计数sorting有什么问题会导致其他两个被使用

基数和桶sorting的初始传递是相同的。元素被放置在“桶”中,即0-10,11-20,…等等,这取决于最大号码中的位数,即基数。 然而,在下一个过程中,bucketsorting将这些“桶”命令并将它们附加到一个数组中。 然而,基数sorting方法在没有进一步sorting的情况下将桶附加到桶中,并且基于数字的第二个数字(十位)来“重新桶”。 因此,桶sorting对于“密集”数组更有效,而基数sorting可以很好地处理稀疏数组。 那么考虑一下这个桶sorting

假设你有一个nlogging的列表,每个logging都有一个从1到k的数字(我们将这个问题推广一点,所以k不一定等于n)。

我们可以通过build立一个链表来解决这个问题。 我们将每个inputlogging移动到数组的适当位置的列表中,然后按顺序将所有列表连接在一起。

  bucket sort(L) { list Y[k+1] for (i = 0; i <= k; i++) Y[i] = empty while L nonempty { let X = first record in L move X to Y[key(X)] } for (i = 0; i <= k; i++) concatenate Y[i] onto end of L } 

k很大时怎么办? 考虑数字x = a + 10 b + 100 c + 1000 d + …的十进制表示,其中a,b,c等全部在范围0..9内。 这些数字很容易进行桶sorting。

  radix sort(L): { bucket sort by a bucket sort by b bucket sort by c ... } 

或者更简单

 radix sort(L): { while (some key is nonzero) { bucket sort(keys mod 10) keys = keys / 10 } } 

为什么我们先做最不重要的数字呢? 对于这个问题,为什么我们要做一个以上的sorting,因为最后一个就是把所有的东西都放到位的那个sorting呢? 答案:如果我们试图手工分类,我们倾向于做一些不同的事情:首先做一个桶sorting,然后recursion地排列共享第一个数字的值。 这是有效的,但效率不高,因为它将问题分解成许多子问题。 相比之下,基数sorting决不会将其分开。 它只是将桶分类多次应用到同一个列表中。 在基数sorting中,水平分拣的最后一道是对整个订单影响最大的一条。 所以我们希望它是使用最重要的数字。 以前的分拣通道只用于处理最后一个通道中两个项目具有相同密钥(mod 10)的情况。

现在我们已经知道Countingsorting所做的一切就是保持一个带有k个元素的辅助数组C,它们都被初始化为0。

我们对input数组A进行一次遍历,并且对于A中的每个元素i,我们看到,我们将C [i]增加1.在遍历A的n个元素并更新C之后,C的索引j处的值对应一旦我们有了C,我们就可以通过遍历C来构造A的sorting版本,并且插入每个元素ja总数C [j]时间到一个新的列表(或A本身)。 通过C遍历需要O(k)次。最终的结果是一个sorting的A,总共花了O(n + k)时间来完成。

计数sorting的下降是如果元素的范围太大,可能不太实际。 例如,如果我们需要sorting的n个元素的范围是从1到n 3,那么简单地创build辅助数组C将花费O(n ^ 3)时间,并且计数sorting将渐进地比插入sorting差。 这也需要O(n ^ 3)空间,这个空间比我们迄今所学的任何其他sortingalgorithm所使用的任何空间都大。 基数sorting有助于通过按位数对元素进行sorting来解决此问题

注意:回答和进一步阅读的来源:

http://htmltolatex.sourceforge.net/samples/sample4.html

第一个答案: 桶sorting和基数sorting有什么区别?

基数sorting使用一种计数sorting的forms作为子程序(好的,可以使用,但大多数情况下它会计算sorting)。

正如kasavbere所回答的,Countingsort是一种特殊forms的斗式sorting。

而Bucketsort将钥匙分成桶,然后单独分类桶。

使用计数sorting来sorting数组:

 #define MAX_INPUT 1000 void sort(int arr[100], int n) { static int hash[MAX_INPUT], i, j; memset(hash, 0, sizeof hash); for (i = 0; i < n; ++i) ++hash[arr[i]]; j = 0; for (i = 0; i < MAX_INPUT; ++i) while (hash[i]--) arr[j++] = i; } 

这只是O(MAX_INPUT) ,因此按线性时间sorting。 对于桶sorting,它是非常不同的。 这是一个实现