Home > database >  How to shift everything in a 2D array to the left
How to shift everything in a 2D array to the left

Time:02-01

I need to take a 2D array and move everything as far left as possible. It is a 4x4 array and I have tried to do it but either only move certain items or the index goes out of bounds.

The gameBoard array looks like this:

{0 2 4 2}
{0 0 2 0}
{2 2 0 0}
{0 4 0 2}

and after you call the swipeLeft() method it should look like this:

{2 4 2 0}
{2 0 0 0}
{2 2 0 0}
{4 2 0 0}

There is also the issue of placing a zero into the previous index that you moved it from.

I created a double for loop to just loop through the array and tried to code something that would move it over but it hasn't worked.

Here was the code I had so far

public void swipeLeft() {
   
    for ( int r = 0; r < gameBoard.length; r   ) {
        for ( int c = 0; c < gameBoard[r].length; c   ) {
            
            gameBoard[r][c] = gameBoard[r][ (c 1) % 
                                                gameBoard.length];
        }
    }

    
}

CodePudding user response:

From the example in the question, it appears to me that what is wanted is to shift all non-zero elements to the left, and zero elements are shifted to the right. The order of the non-zero elements is to be retained.

Note that each row is independent of other rows.

One way to approach this is to create a method that works on a 1D array. This method takes a 1D array as a parameter, and returns another 1D array with the elements shifted:

public static int [] zeroShift (int [] arr) {
    int [] left = new int [arr.length];
    int count = 0;
    for (int i = 0; i < arr.length; i  ) {
        if (arr[i] != 0) {
            left [count  ] = arr [i];
        }
    }
    return  left;
}

This copies each non-zero element to a new array of the same size, keeping track (count) of how many have been copied so far. Note this relies on left being initialized to all-zeros.

Once that method is working, it can be used for gameBoard on a row-by-row basis:

 public void swipeLeft() {
    for (int r = 0; r < gameBoard.length; r  ) {
        gameBoard [r] = zeroShift (gameBoard [r]);
    }  

    // output for testing 
    for (int i = 0; i < gameBoard.length;   i) {
        System.out.println(Arrays.toString(gameBoard[i]));
    }
}

CodePudding user response:

Based on your desired OUTPUT, it looks like swipeLeft() is supposed to push all non-zero values to the very left of their row, displacing the zeroes to the right of all non-zero values.

If that's correct, this is similar to Old Dog Programmer's approach, except all shifting is done "in place" without creating any new arrays:

import java.util.*;
class Main {

  private static int[][] gameBoard;
  
  public static void main(String[] args) {
    gameBoard = new int[][] {
      {0, 2, 4, 2},
      {0, 0, 2, 0},
      {2, 2, 0, 0},
      {0, 4, 0, 2}
    };

    System.out.println("Before:");
    displayBoard();

    swipeLeft();
    
    System.out.println("\nAfter:");
    displayBoard();
  }

  public static void displayBoard() {
    for(int[] row : gameBoard) {
      System.out.println(Arrays.toString(row));  
    }
  }

  public static void swipeLeft() {
    for(int[] row : gameBoard) {
      // find the first blank (zero) spot
      int nextIndex = 0;
      while(nextIndex < row.length && row[nextIndex] != 0) {
        nextIndex  ;
      }
      // start with the first blank, and shift any non-zero
      // values afterwards to the left
      for(int col=nextIndex; col < row.length; col  ) {
        if (row[col] != 0) {
          row[nextIndex] = row[col];
          row[col] = 0;
          nextIndex  ;
        }
      }
    }
  }
  
}

Output:

Before:
[0, 2, 4, 2]
[0, 0, 2, 0]
[2, 2, 0, 0]
[0, 4, 0, 2]

After:
[2, 4, 2, 0]
[2, 0, 0, 0]
[2, 2, 0, 0]
[4, 2, 0, 0]

CodePudding user response:

To rotate the array in place, you should roteate the array 3 times:

  1. 123456 -> 654312
  2. 654321
  3. 3456..
  4. ....12
public static void shiftLeft(int[] arr, int offs) {
    if (offs <= 0)
        return;

    offs = arr.length - offs % arr.length - 1;

    for (int i = 0, j = arr.length - 1; i < j; i  , j--)
        swap(arr, i, j);
    for (int i = 0, j = offs; i < j; i  , j--)
        swap(arr, i, j);
    for (int i = offs   1, j = arr.length - 1; i < j; i  , j--)
        swap(arr, i, j);
}

private static void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

CodePudding user response:

So your code intends to rotate the board one column to the left. Rotate? Well, the numbers you push out on the left might come back on the end, right?

Probably the line

gameBoard[r][c] = gameBoard[r][ (c 1) % gameBoard.length];

should be

gameBoard[r][c] = gameBoard[r][ (c 1) % gameBoard[r].length];

But try to do this stuff with pen & paper, and you should notice that you are going to loose one column/copy the values from the second column into the first, then copy that into the last column again.

You will need to change two items:

  • store the value from the first column somewhere if you still need it so you can push it into the last one.
  • only rotate the column data if it needs to be rotated. Or in other words, rotate the remainder of the row if you find a zero. In this case you do not need to remember the first column, as you will overwrite a zero and push a zero into the last column. And then it would not be called rotate but shift.

Exercise this with pen & paper until you can write down instructions for someone else to perform the same operation. Then you are ready to also write it in Java.

  • Related