【问题标题】:Array Sort by Remainder数组按余数排序
【发布时间】:2015-07-28 03:31:05
【问题描述】:

今天一位面试官用这样的问题毁了我的生活。
您有一个包含一百万个整数的数组,您需要按余数对它们进行排序。
1. 你不知道他们要除以什么整数。
2. 不能使用 Comparator 等类来提供帮助。
3. 尽可能少循环。
4. 记住要节省内存。

例如

int[] ints = {5434, 3454, 2, 0, 356, 896, 7324, 888, 99, 78365, 111};  
int divider = 27;  

应该是

int[] int2 = {0, 2, 111, 356, 896, 5434, 7324, 78365, 99, 888, 3454};

我想出的方法 loops = divider / 2.
它可以工作,但如果分频器是 250,那么它会循环 125 次。

public void sortByMod(int[] millionInts, int divideBy) {
    long time = System.nanoTime();
    int[] b = new int[millionInts.length];
    int remainder, remainderMin = 0, remainderMax = divideBy, positionMin = 0, positionMax = millionInts.length - 1;
    for (int i = 0; i < millionInts.length;) {
        for (int j = 0; j < millionInts.length; j++) {
            remainder = millionInts[j] % divideBy;
            if (remainder == remainderMin) {
                b[positionMin] = millionInts[j];
                positionMin++;
                i++;
            } else if (remainder == remainderMax) {
                b[positionMax] = millionInts[j];
                positionMax--;
                i++;
            }
        }
        remainderMax--;
        remainderMin++;
    }
    System.out.println("time = " + (System.nanoTime() - time));
    System.out.println("loopcount = " + remainderMin);
}

我写了另一种方法,它可以在 2 个循环中完成,但读起来很混乱。
它违反了内存限制,但速度非常快。

public void sortByModPro(int[] millionInts, int divideBy) {
    int[] range = new int[divideBy];
    int[] remainders = new int[millionInts.length];
    int[] newArray = new int[millionInts.length];
    long times = System.nanoTime();
    for (int i = 0; i < millionInts.length; i++) {
        remainders[i] = millionInts[i] % divideBy;
        range[millionInts[i] % divideBy]++;
    }
    for (int i = range.length - 1, past = millionInts.length; i >= 0; i--) {
        range[i] = past - range[i];
        past = range[i];
    }
    for (int i = 0; i < millionInts.length; i++) {
        newArray[range[remainders[i]]] = millionInts[i];
        range[remainders[i]]++;
    }
    System.out.println("time = " + (System.nanoTime() - times));
}

你会如何用 1 个循环来做到这一点?

【问题讨论】:

  • Java 中的数组不能容纳一百万个整数。面试官不知道吗?
  • 谷歌快速排序,并用一个小的变化来实现它:而不是直接比较数组中的整数,而是比较它们的余数。
  • @ChetanKinger 当然可以。为什么不能呢?
  • @JBNizet length 在 Java 中的数组存储在 int 中。
  • 是的,所以? 2^31 远大于 100 万。 ideone.com/fGUU89

标签: java arrays performance sorting


【解决方案1】:

速度 > 记忆

您只能使用一堆桶循环一次,每个剩余部分一个。只需根据余数转储桶中的数字,然后合并桶。当然这违反了内存限制。

用你的数组来装桶

存储桶的问题是您至少需要为数组中的每个项目添加一个引用。为了避免这种情况,您可以做的是将数组划分为存储桶并维护对每个存储桶的开始和结束索引的引用。当然这会占用一些内存,但是divideBy参数应该很小吧?

所以,这里有一些伪代码:

// init each bucket with 0 elements
for (remainder=0; remainder<divideBy; remainder++) {
  buckets = {
    start : 0, // startIndex in the array
    end: 0, // the index after the last item actually placed in the bucket
    count: 0 // how many items should be in the bucket  
  }
}
// count how many elements fit in each bucket
for (i=0; i<N; i++) {
  buckets[array[i]%divideBy].count++;
}
// init the start and end points of each bucket
elementsCounted=0;
for (remainder=0; remainder<divideBy; remainder++) {
  buckets[remainder].start = elementsCounted;
  buckets[remainder].end = elementsCounted;
  elementsCounted += buckets[remainder].count;
}

// at this point each bucket starts where it should in the array, but has no elements

// loop through the array and place items in the right bucket by swapping them
for (i=0; i<N; i++) {
  remainder = array[i]%divideBy;
  if (i < buckets[remainder].start || i >= buckets[remainder].end) {
    // array[i] is in the wrong bucket, swap it at the end of the right bucket
    swap(array[i], array[buckets[remainder].end]);
    buckets[remainder].end++;
    i--;
  }  
}

// everything is in the right place

你会注意到在最后的 for 中有一个 i--,所以从技术上讲它可以永远持续下去。情况并非如此,只有当 array[i] 不在正确的位置时,我才会留在原地。每次迭代都会将元素放置在正确的位置,或者如果元素不在正确的位置,则前进到下一个位置。总而言之,它最多会迭代 2N 次。

总时间复杂度:O(3N+divideBy) = O(N + divideBy)

使用的总额外空间:divideBy*sizeof(bucket) = divideBy*12

【讨论】:

  • 这看起来像我放在一起的第二种方法。我的意思是分频器可能很小,但他说可能是 2000 年,对于这样的事情来说并不算大。
  • 这个方法看起来很棒,我打算在早上尝试实现它。再次感谢,我会在实施时给您检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-25
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
  • 2013-03-17
  • 2021-09-03
相关资源
最近更新 更多