将元素向左移动N步(挑战)

时间:2015-08-05 14:03:38

标签: java arrays algorithm pseudocode

假设我们有这个数组:

{"a", "b", "c", "d", null, "f", "g", null, "i", "j", "k", null, "l"};

然后我已经制作了一个方法将元素推向左边并将所有空值保留在右边:

void pushFromLeftToLeft() {

  int nullCount = 0;

  for (int i = 0; i < BUCKET_CAPACITY; i++) {
      if (data[i] == null) {
        nullCount++;
      }
      else if (nullCount != 0) {
        data[i-nullCount] = data[i];
      }
  }
  // set the last elements to null 
  for (int i = BUCKET_CAPACITY - nullCount; i < BUCKET_CAPACITY; i++) {
     data[i] = null;  
  }

}

这导致:

{"a", "b", "c", "d", "f", "g", "i", "j", "k", "l", null, null, null};

我知道可以通过制作相同大小的数组并循环遍历原始数据并在当前索引处分配给新的数组(如果不为空)来更简单。但是我选择了更多的复杂性,因此对于非常大的阵列来说它更有效。

虽然这很有效但我有另一个想法。我一直试图解决这个算法,但这让我很难过。 这个想法如下:

我们再次从这个数组开始:

{"a", "b", "c", "d", null, "f", "g", null, "i", "j", "k", null, "l"};

我想添加“m”和“o”添加数组的结尾。要做到这一点,我想再次将元素向左移动以腾出空间,但只需要腾出空间。 所以我们可以在最后有2个空值时停止,结果是:

{"a", "b", "c", "d", null, "f", "g", "i", "j", "k", "l", null, null};

接下来是挑战:

  • 从右到左工作
  • 将元素直接移动到正确的位置,因此不是向左移动“l”,而是向左移动一次,而是在一步中移动2个索引。
  • 不要使用新数组,只使用尽可能小的缓冲区
  • (简而言之,效率很高)

到目前为止,我多次尝试都失败了。以下是我的最新消息,可能无济于事。我希望有人可以帮助解决这个有趣的挑战。

String[] data = new String[] {"a", "b", "c", "d", null, "f", "g", null, "i", "j", "k", null, "l"};
int BUCKET_CAPACITY = data.length;

void pushFromRightToLeft(int offset) {

  int currentOffset = offset; // if we find a null this decreases

  Object[] buffer = new Object[offset];

  {
    int bufferIndex1 = (BUCKET_CAPACITY-1) % buffer.length;
    buffer[bufferIndex1] = data[BUCKET_CAPACITY-1];
  }


  for (int i = BUCKET_CAPACITY-1; i >= offset; i--) {

    int bufferIndex1 = i % buffer.length;
    int bufferIndex2 = (i+1) % buffer.length;

    if (buffer[bufferIndex1] == null) {
      //nullCount++;
      currentOffset--;
      // what to do, we don't want to move the null forward...
      // move last buffered item into this place
      data[i] = (String) buffer[bufferIndex2];
    } 
    else {

      buffer[bufferIndex2] = data[i-currentOffset]; 
      data[i-currentOffset] = (String) buffer[bufferIndex1]; 

      if (currentOffset == 0) {
        println("break on: "+i);
        break;
      }

    }

  } 

}

(对于这种情况,pushFromRightToLeft(1); pushFromRightToLeft(2);pushFromRightToLeft(3);应该有用。)

3 个答案:

答案 0 :(得分:4)

你可以从右到左,计算空值,当达到所需的空值数时,再向右移动,将元素移动到位:

public static void moveNulls(Object[] arr, int howMany) {
    int offset = arr.length;
    int nullCount = 0;
    while(nullCount < howMany) {
        if(arr[--offset] == null)
            nullCount++;
        if(offset == 0 && nullCount < howMany)
            throw new IllegalStateException("Not enough nulls");
    }
    int target = offset;
    while(offset < arr.length) {
        if(arr[offset] != null)
            arr[target++]=arr[offset++];
        else
            offset++;
    }
    Arrays.fill(arr, target, offset, null);
}

此算法不需要额外的内存。用法:

for(int i=0; i<5; i++) {
    String[] arr = {"a", "b", "c", "d", null, "f", "g", null, "i", "j", "k", null, "l"};
    moveNulls(arr, i);
    System.out.println(Arrays.toString(arr));
}

输出:

[a, b, c, d, null, f, g, null, i, j, k, null, l]
[a, b, c, d, null, f, g, null, i, j, k, l, null]
[a, b, c, d, null, f, g, i, j, k, l, null, null]
[a, b, c, d, f, g, i, j, k, l, null, null, null]
Exception in thread "main" java.lang.IllegalStateException: Not enough nulls

答案 1 :(得分:0)

纯粹作为未经优化的算法:

List<String> shiftInFromRight(String[] data, List<String> inserts) {
    for (int i = data.length - 1; i >= 0 && !inserts.isEmpty(); --i) {
         String old = data[i];
         data[i] = inserts.remove(inserts.size() - 1);
         if (old != null) {
             inserts.add(0, old);
         }
    }
    return inserts; // The overflow if any.
}

要插入的内容可能是一个数组(它只会收缩,因此额外的循环计数器就足够了)。给定nullCounter,不需要产生溢出。

void shiftInFromRight(String[] data, String[] inserts) {
    final int N = inserts.length;
    int n = N;
    int right = n;
    for (int i = data.length - 1; i >= 0 && n > 0; --i) {
         String old = data[i];
         --n;
         --right;
         if (right < 0) { // Round-robin.
             right += n;
         }
         data[i] = inserts[right];
         if (old != null) {
             inserts[right] = old;
             ++n;
         }
    }
    if (n > 0) {
        throw new OutOfMemoryException(); // joke
    }
}

答案 2 :(得分:0)

这是我的结果。

请务必查看Tagir Valeev的解决方案,这稍快一点! 使用此解决方案,缓冲区可以增加时间。

void moveNulls(Object[] data, int howMany) {

  if (howMany == 0) return;

  Object[] buffer = new Object[howMany*2];
  int b_getIndex = 0;
  int b_setIndex = howMany;

  int nullCount = 0;


  for (int i = data.length-1; i >= howMany; i--) {

    if (data[i] == null) {
      nullCount++;
    }
    else {
      buffer[b_setIndex] = data[i];
      b_setIndex++;
    }

    data[i] = buffer[b_getIndex];

    b_getIndex++;
    if (b_setIndex == buffer.length) b_setIndex = 0; 
    if (b_getIndex == buffer.length) b_getIndex = 0;

    if (nullCount == howMany) break;
  } 

}