数组按余数排序

时间:2015-05-16 06:24:58

标签: java arrays performance sorting

一位采访者今天用这种问题破坏了我的生活 你有一个包含一百万个整数的数组,你需要用余数对它们进行排序 你不知道他们要分成什么整数 2.您不能使用比较器等课程来提供帮助 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};

我想出的方法是loop = 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循环完成此操作?

1 个答案:

答案 0 :(得分:2)

速度&gt;存储器

你可以使用一堆桶来循环一次,每个剩余一个桶。只需根据剩余部分将数据转储到存储桶中,然后合并存储桶。当然这违反了记忆约束。

使用您的数组来保存存储桶

存储桶的问题是您至少需要添加对数组中每个项的引用。您可以采取哪些措施来避免这种情况,即将数组划分为存储桶并维护对每个存储桶的开始和结束索引的引用。当然这会占用一些内存,但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

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

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

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