按数字顺序排序N个数字

时间:2010-08-01 13:00:18

标签: algorithm sorting digits radix-sort

给出N数范围例如。 [1到100],按数字顺序对数字进行排序(即)对于数字1到100,排序的输出结果为 1 10 100 11 12 13。 。 。 19 2 20 21 ..... 99

这就像Radix Sort一样,但只是数字的排序顺序与正常的基数排序相反。

我尝试将每个数字中的所有数字存储为链接列表,以便更快地进行操作,但这会导致很大的空间复杂性。

我需要一个有问题的工作算法。

从所有答案中,“转换为字符串”是一种选择,但是没有其他方法可以做到这一点吗? 还可以给出如上所述的用于排序字符串的算法。

7 个答案:

答案 0 :(得分:11)

使用您喜欢的任何排序算法,但将数字作为字符串进行比较,而不是数字。这基本上是常规数字的lexiographic排序。这是C中的一个gnome排序示例:

#include <stdlib.h>
#include <string.h>

void sort(int* array, int length) {
    int* iter = array;
    char buf1[12], buf2[12];
    while(iter++ < array+length) {
        if(iter == array || (strcmp(itoa(*iter, &buf1, 10), itoa(*(iter-1), &buf2, 10) >= 0) {
            iter++;
        } else {
            *iter ^= *(iter+1);
            *(iter+1) ^= *iter;
            *iter ^= *(iter+1);
            iter--;
        }
    }
}

当然,这需要itoa中存在非标准stdlib.h函数。更标准的替代方法是使用sprintf,但这会使代码更加混乱。你可能最好先将整个数组转换为字符串,然后排序,然后将其转换回来。

修改:作为参考,此处的相关位为strcmp(itoa(*iter, &buf1, 10), itoa(*(iter-1), &buf2, 10) >= 0,用于替换*iter >= *(iter-1)

答案 1 :(得分:4)

我有一个解决方案,但不完全是一个算法。你需要做的就是将所有数字转换成字符串&amp;将它们排序为字符串..

答案 2 :(得分:3)

以下是使用递归函数(代码使用Java)的方法:

void doOperation(List<Integer> list, int prefix, int minimum, int maximum) {
    for (int i = 0; i <= 9; i++) {
        int newNumber = prefix * 10 + i;
        if (newNumber >= minimum && newNumber <= maximum) {
            list.add(newNumber);
        }
        if (newNumber > 0 && newNumber <= maximum) {
            doOperation(list, newNumber, minimum, maximum);
        }
    }
}

你这样称呼它:

List<Integer> numberList = new ArrayList<Integer>();
int min=1, max =100;
doOperation(numberList, 0, min, max);
System.out.println(numberList.toString());

编辑:

我用C ++ here翻译了我的代码:

#include <stdio.h> 

void doOperation(int list[], int &index, int prefix, int minimum, int maximum) {
    for (int i = 0; i <= 9; i++) {
        int newNumber = prefix * 10 + i;
        if (newNumber >= minimum && newNumber <= maximum) {
            list[index++] = newNumber;
        }
        if (newNumber > 0 && newNumber <= maximum) {
            doOperation(list, index, newNumber, minimum, maximum);
        }
    }
}

int main(void) { 
        int min=1, max =100;
        int* numberList = new int[max-min+1];
        int index = 0;
        doOperation(numberList, index, 0, min, max);
        printf("["); 
        for(int i=0; i<max-min+1; i++) {
                printf("%d ", numberList[i]); 
        }
        printf("]"); 
        return 0; 
}

基本上,我的想法是:对于每个数字(0-9),如果它位于minimummaximum之间,我会将其添加到数组中。然后,我用这个数字作为前缀调用相同的函数。它也是如此:对于每个数字,它将它添加到前缀(prefix * 10 + i),如果它在限制之间,则将其添加到数组中。当newNumber大于最大值时停止。

答案 3 :(得分:2)

我认为如果将数字转换为字符串,则可以使用字符串比较对它们进行排序。 你可以使用anny排序alghorighm。

“1”&lt; “10”&lt; “100”&lt; “11”......

答案 4 :(得分:2)

优化存储数字的方式:使用binary-coded decimal (BCD)类型,可以轻松访问特定数字。然后您可以使用当前算法,Steve Jessop正确识别为most significant digit radix sort

  

我试图存储所有数字   每个数字作为链接列表   更快的操作,但它导致一个   大空间复杂性。

将每个数字存储在链接列表中会以两种不同的方式浪费空间:

  1. 数字(0-9)只需要4位存储器来存储,但您可能使用8到64位的任何位置。 charshort类型需要8位,而int最多可能需要64位。那就是使用比最佳解决方案多2到16倍的内存!
  2. 链接列表会增加额外的不必要的内存开销。对于每个数字,您需要额外的32到64位来存储下一个链接的内存地址。同样,这会将每位数所需的内存增加8倍到16倍。
  3. 内存效率更高的解决方案将BCD个数字连续存储在内存中

    1. BCD每位仅使用4位。
    2. 将数字存储在连续的内存块中,就像数组一样。这消除了存储存储器地址的需要。您不需要链接列表从中间轻松插入/删除的能力。如果您需要将数字增长到未知长度的能力,那么还有其他抽象数据类型允许更少的开销。例如,vector
    3. 一个选项,如果其他操作(如加法/乘法)不重要,则分配足够的内存来存储每个BCD数字加一个BCD终结符。 BCD终结符可以是4比特的任何组合,不用于表示BCD数字(如二进制1111)。以这种方式存储会使加法和乘法等其他操作更加棘手。

      请注意,这与转换为字符串和按字典顺序排序这些字符串的想法非常相似。整数在内部存储为计算机中的二进制(基数2)。存储在BCD中更像是基数10(实际上是基数16,但忽略了6种组合),字符串就像基数256.字符串将使用大约两倍的内存,但是已经有很多函数被写入排序字符串。 BCD可能需要根据您的需求开发自定义BCD类型。

答案 5 :(得分:1)

编辑:我错过了它是一个连续的范围。在这种情况下,所有关于排序数组的答案都是错误的(包括你的想法在问题中说明它就像一个基数排序),而True Soft的答案是正确的。

就像Radix Sort一样,只是数字按相反的顺序排序

很好地发现:-)如果你真的这样做,有趣的是,它被称为MSD基数排序。

http://en.wikipedia.org/wiki/Radix_sort#Most_significant_digit_radix_sorts

您可以非常简单地实施一项,或者使用大量高科技和大肆宣传。在大多数编程语言中,您的特定示例面临轻微的困难。从整数的自然存储格式中提取十进制数字不是特别快的操作。您可以忽略这一点,看看它最终需要多长时间(推荐),或者您可以通过在排序之前将所有数字转换为十进制字符串来添加更多的大张旗鼓。

当然,您不必将其作为基数排序实现:您可以使用比较排序算法和适当的比较器。例如在C中,以下内容适用于qsort(除非我搞砸了):

int lex_compare(void *a, void *b) {
    char a_str[12];  // assuming 32bit int
    char b_str[12];
    sprintf(a_str, "%d", *(int*)a);
    sprintf(b_str, "%d", *(int*)b);
    return strcmp(a_str,b_str);
}

效率不是很高,因为它做了很多重复工作,但很简单。

答案 6 :(得分:1)

如果您不想将它们转换为字符串,但有足够的空间来存储列表的额外副本,那么我将存储比副本中的元素少10的最大功率。这可能是最简单的循环。现在调用原始数组x和10 y的幂。

int findPower(int x) {
   int y = 1;
   while (y * 10 < x) {
      y = y * 10;
   }
   return y;
}

你也可以直接计算它们

y = exp10(floor(log10(x)));

但我怀疑迭代可能比转换到浮点和从浮点转换更快。

为了比较ij元素

bool compare(int i, int j) {
  if (y[i] < y[j]) {
    int ti = x[i] * (y[j] / y[i]);
    if (ti == x[j]) {
      return (y[i] < y[j]);  // the compiler will optimize this
    } else {
      return (ti < x[j]);
    }
  } else if (y[i] > y[j]) {
    int tj = x[j] * (y[i] / y[j]);
    if (x[i] == tj) {
      return (y[i] < y[j]);  // the compiler will optimize this
    } else {
      return (x[i] < tj);
    }
  } else {
     return (x[i] < x[j];
  }
}

这里要做的是我们将较小的数字乘以10的适当幂,使两个数字的数字相等,然后进行比较。如果两个修改后的数字相等,则比较数字长度。

如果没有空间来存储y数组,则可以在每次比较时计算它们。

通常,您最好使用预优化的数字转换例程。