我被困在递归地实现基数排序

Posted

技术标签:

【中文标题】我被困在递归地实现基数排序【英文标题】:I am stuck at implementing Radix sort recursively 【发布时间】:2016-12-25 15:39:17 【问题描述】:

我需要实现一个对 0 到 99999 范围内的数字进行递归排序的程序(这基本上是基数排序)。这个过程本身有点简单:用户在 main 方法中输入一个包含这些数字的数组。然后,主要方法调用排序方法,在该方法中我创建了一个名为“空间”的二维数组,包含 10 行和 1 列。然后,我将数组中的每个数字除以数字,第一次运行时为 10.000。因此,例如,23456 / 10000 = 2,3456 = 2(在 java 中),因此,程序将这个数字放在空格 [2] [0] 中,所以放在第二行。然后,我们获取整行并扩展它,这是在 putInBucket 方法中完成的。我们这样做是为了确保我们可以将另一个数字放入同一行。

我们对“数字”数组中的每个数字都执行此操作。然后,我们想处理这些行并按照相同的原则再次对它们进行排序,但现在我们看一下第二个数字。我们想从左到右,而不是从右到左。所以,如果我们的第二行看起来像这样

[23456, 24567],

我们想比较 3 和 4,得出 23456

我们借助排序方法末尾的递归调用来做到这一点。现在,这就是我迷路的地方。我根本不知道如何操作数字变量,以便能够处理每个数字的第二个、第三个、... 数字。如您所见,在第一次运行中,这可以简单地通过除以 10.000 来完成,但我没有找到离这里更远的方法。

请注意:是的,这是一道家庭作业题,因此,我只能在这里使用原语。我们还没有经历过 math.pow(...) 之类的东西。提前致谢!

public static int[] sort(int[] numbers, int digit) 

  if (numbers.length == 0)
    return numbers;

  int[][]space = new int[10][1];
  int i, j = 0;

  for (j = 0; j < numbers.length; j++) 
    i = numbers[j] / digit;
    space[i][0] = numbers[j];
    space[i] = putInBucket(space[i], numbers[j]);
  

  for (i = 0; i < space[i].length; i++) 
    sort(space[i], digit); //not sure how to work with digit here
  

  return ... //not sure what to return here



private static int[] putInBucket(int[] bucket, int number) 

  int[] bucket_new = new int[bucket.length+1];

  for (int i = 1; i < bucket_new.length; i++) 
    bucket_new[i] = bucket[i-1];
  

  return bucket_new;



public static void main (String [] argv) 

  int[] numbers = IO.readInts("Numbers: ");
  int digit = 10000;
  int[] bucket = sort(numbers, digit); 


【问题讨论】:

第一步,使用您的 IDE 自动格式化此代码,使其具有一致的缩进(一致的缩进使您和我们更容易看到代码的结构)。事实上,我建议您将 ide 配置为在保存文件时自动格式化代码。 我正在使用 vim,我认为没有自动格式化。 这里的缩进有什么问题? 几乎所有东西。例如,所有行都从它们所属的方法所在的列开始。所以我们看不到它们是这个方法的一部分,方法在哪里结束,等等。如果你不能使用 vim 自动缩进,那么手动缩进,或者使用你可以缩进的编辑器。基本上所有以代码为中心的编辑器都允许这样做。 就我个人而言,我使用 eclipse,尽管其他人更喜欢 IntelliJ(社区版是免费的)或 netbeans。任何这些都适合初学者。 【参考方案1】:

要提取最后一位,余数运算符%是你的朋友:

123 % 10 == 3

如果你还没有覆盖% 运算符,你可以使用

123 % 10 == 123 - (123 / 10 * 10) == 3

要提取另一个数字,您可以先将其移动到末尾/

123 / 10 == 12
12 % 10 == 2

因此,您可以使用

提取任意数字
(number / mask) % 10 

其中掩码 ∈ ..., 10000, 1000, 100, 10, 1。

额外功劳

基数排序通常在二进制数字系统中实现,因为可以在不执行除法的情况下提取二进制数字(或其序列),这样更有效:

x % 16 == x & 15;
x \ 16 == x >> 4;

此外,如果您要真正实现这一点,则需要一种更有效的方法来增加存储桶(您的实现需要 O(n) 才能将单个元素添加到存储桶,因此向存储桶添加 n 个元素需要 O (n^2),这使您的基数排序比插入排序慢)。 Dynamic arrays 通常使用更高效的geometric expansion 来实现。

【讨论】:

是的,我们介绍了模运算符。 我知道的基数排序使用least significant digit first。 不是递归的,可以在下面的文章中找到。 啊好吧,不知道那个变种。好的,那么先提取最重要的数字。无论哪种情况,您都可以使用 (number \ mask) % 10 提取数字,其中 mask ∈ ..., 10000, 1000, 100, 10, 1。 @meriton - 看看example radix sort。这是使用 256(一个字节)作为“数字”的 lsd(最低有效位)基数排序。生成一个计数矩阵,然后将其转换为“输出”数组的索引。代码通过交换指针在每次通过时交替移动方向。由于它是偶数遍,因此排序后的数据最终会出现在原始数组中。该示例返回一个指向已排序数组的指针,但这不是必需的,因为已排序的数据最终会出现在原始数组中。【参考方案2】:

这应该有效:

public static int[] sort(int[] numbers, int digit) 

     if (numbers.length == 0 || digit <= 0)
           return numbers;

     int[][]space = new int[10][10];
     int[] len = new int[10];
     int i, j = 0;

      for (j = 0; j < numbers.length; j++) 
            i = (numbers[j] / digit) % 10;
            len[i]++;
            for (int k = len[i] - 1; k > 0; k--) 
                space[i][k] = space[i][k - 1];
            
            space[i][0] = numbers[j];
      


      for (i = 0; i < 10; i++) 
          int[] bucket = new int[len[i]];
          for (int k = 0; k < len[i]; k++) 
              bucket[k] = space[i][k];
          space[i] = sort(bucket, digit / 10); 
      

      int k = 0;

      for (i = 0; i < 10; i++) 
          for (j = 0; j < len[i]; j++) 
              numbers[k] = space[i][j];
              k++;
          
      

      return numbers; 


a) 首先,space 被分配为只有一列。因此,space[i] = bucket 将不起作用。

相反,您可以将其声明为 int[10][10]。 (注意:它只支持一个桶中最多 10 个值)。或者您可以以编程方式分配新数组。或者当然,List 可能更适合。

b)i = (numbers[j] / digit) % 10;

仅获取所需的数字。例如:如果号码是12130,并且digit = 1000,我们希望将i 设置为2,而不是12

c) putInBucket 替换为就地循环。

d) 对于space 中的每个bucket,我们通过递归调用sort 将其排序低一位。

e) 最后,要返回的结果 (numbers),可以通过从数字 0 到 9 循环 space 来创建。

注意: 这个解决方案可能会变得更好。

【讨论】:

我不能替换 putInBucket 方法,抱歉。 如果是这种情况,只需将循环替换为space[i] = putInBucket(space[i])。另外,如果您不使用它,为什么要传递numbers[j]?我怀疑你应该在putInBucket 中设置bucket_new[0] = number,如果这是给你的方法签名。 我正在使用这些数字。它们在扩展数组的末尾复制,然后返回整个内容。如果您从 12345 开始并使用第一行和此数字调用 putInBucket 方法,那么您将得到一个如下所示的新数组:[0, 12345]。然后,如果出现另一个以 1 开头的数字,例如 13456,那么我们可以把它放在第一个位置,所以我们会得到 [13456, 12345] 并再次扩展它: [0, 13456, 12345] .这意味着要进一步排序。 我明白这一点。但是请查看您发布的putInBucket 方法。变量number 根本没有在方法体中使用!这就是我要说的。您可能希望在 putInBucket 中使用 bucket_new[0] = number 而不是在 sort 方法中使用 space[i][0] = numbers[j],但忘记了。 有人告诉我,由于可见性,我需要使用数字参数,尽管我没有清楚地使用它。您可能想查看我的更新版本:***.com/questions/41323554/…

以上是关于我被困在递归地实现基数排序的主要内容,如果未能解决你的问题,请参考以下文章

就地基数排序的空间开销

---------快排-----表排-----基数排序(桶排序)-----

归并排序法和基数排序法

基数排序是不是用于后缀排序?

从向量中最快擦除元素或更好地使用内存(排序基数)

快速排序及优化