Переместить строки двумерного массива

моя задача - сдвинуть строки массива на фиксированную величину «сдвига» и заполнить сдвинутые строки нулями:

Например, массив:

1 1 1 1 1

1 1 3 1 1

1 6 2 1 4

1 1 1 8 1

1 1 1 1 1

сдвиг на 2 должен привести к:

0 0 0 0 0

0 0 0 0 0

1 1 1 1 1

1 1 3 1 1

1 6 2 1 4

public static void main(String args[]) {
        int[][] a = { { 1, 1, 1, 1, 1 }, { 1, 1, 3, 1, 1 }, { 1, 6, 2, 1, 4 }, { 1, 1, 1, 8, 1 }, { 1, 1, 1, 1, 1, } };

        System.out.println(Arrays.deepToString(move(a, 2)));

    }
public static int[][] move(int[][] a, int shift) {

        for (int i = a.length - 1; i >= shift; i--) {
            a[i] = a[i - shift];

        }

        for (int i = 0; i < shift; i++) {

            for (int j = 0; j < a[0].length; j++) {
                a[i][j] = 0;
            }

        }

        return a;
    }

Мой результат: [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [1, 6, 2, 1, 4]]

Если я распечатаю свой массив после первого цикла for, это правильно, я не понимаю, почему второй цикл for меняет строки 2 и 3.

В первом цикле for вы назначаете ссылки на эти строки, а не копируете в них данные. Это означает, что когда вы присваиваете значения, строки смотрят в одни и те же места, поэтому «обе» видят 0.

Edward Peters 18.09.2023 18:43

Для подобных проблем вам может помочь записать ответы простыми словами (или псевдокодом), а затем закодировать решение на основе этого. Кроме того, вы можете сначала попытаться найти решение для копирования массива. Создайте новый массив, заполненный нулями, затем скопируйте каждую строку исходного массива с индексом 0 в новый массив, начиная со смещения (т. е. +2), пока вы не сможете больше копировать строки. Затем, как только это решение заработает, создайте решение «на месте», в котором вы перезапишете строки. Как правило, второй подход более сложен, чем первый. Так что при обучении такой подход должен помочь.

hfontanez 18.09.2023 19:04

Если вы хотите избежать ссылки на всю строку и возникновения этой проблемы, вы можете скопировать отдельные элементы в каждой строке, проходя столбец за столбцом, вместо этого с помощью вложенного цикла for.

Idle_Mind 18.09.2023 19:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Когда вы копируете строку из одной позиции в другую, вы копируете ссылку на ту же строку в памяти (проверьте это). Вы должны учитывать это, чтобы ваш код работал правильно:

public static int[][] move(int[][] a, int shift) {
    int rows = a.length;
    int cols = a[0].length;
    
    // New array to store the result
    int[][] result = new int[rows][cols];
    
    for (int i = 0; i < rows - shift; i++) {
        // Copy the rows
        result[i + shift] = a[i].clone();
    }

    // Loop it
    for (int i = 0; i < shift; i++) {
        for (int j = 0; j < cols; j++) {
            // Fill with zeros
            result[i][j] = 0;
        }
    }

    return result;
}

Я знаю, что поначалу это должно сбивать с толку: такое странное поведение связано с указателями. Изменяя значения строк для вашей функции shift, вы изменили ссылочные типы третьей и четвертой строк на первую и вторую строки.

Проблема в первом цикле. Как следствие, любые изменения, внесенные в первую строку, также отражаются в третьей строке. То же самое касается второго ряда, четвертого. Вы связали их вместе.

Поэтому поменял их и на втором цикле.

Я бы посоветовал вам изменить каждое значение строки вместо ссылочных типов, например:

for (int i = a.length - 1; i >= shift; i--) {
        
    for (int j = 0; j < a[0].length; j++) {
        a[i][j] = a[i - shift][j];
    }
        
}

Ваше здоровье!

Спасибо за ваш ответ: «Я бы посоветовал вам изменить каждое значение строки вместо ссылочных типов как таковых:» Я понимаю, что вы имеете в виду, но как мне это сделать? Я понимаю проблему со ссылкой, но как мне сказать Java, чтобы она изменила значение, а не только ссылку?

Moritz 18.09.2023 19:08

вы не можете, вы пытаетесь изменить указатель, и вы дали ему новый адрес. Единственное значение, которое может содержать указатель, — это ссылка на другой объект, поэтому ваши единственные возможные варианты — либо создать его клон (другой ответ), либо скопировать материал по значению (мой ответ).

Manuel 18.09.2023 19:41

Вот один из способов сделать это. Обратите внимание, что вы можете сделать это, не создавая новый массив для хранения результата. (т. е. вы можете провести смену на месте).

Ключевой момент, на который следует обратить внимание: вы копируете ссылки в массиве. Когда вы сдвигаете массивы слева направо, проблем не возникает. Но вы не можете просто заполнить существующие строки нулями, поскольку вы перезапишете ранее скопированные объекты массива, и они будут изменены.

Кроме того, чем меньше значение сдвига, тем больше требуется копирования. Исключением является значение 0, при котором вы просто возвращаете заданный массив.

int[][] a = {{1,1,1,1,1},{1,1,3,1,1},{1,6,2,1,4},{1,1,1,8,1},
         {1,1,1,1,1}};

System.out.println(Arrays.deepToString(move(a, 2)));

принты

[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [1, 1, 1, 1, 1], [1, 1, 3, 1, 1], [1, 6, 2, 1, 4]]
  • Сначала проверьте действительные аргументы:
    • выдать исключение, если сдвиг выходит за пределы.

    • Если массив равен null, empty или значение сдвига равно 0, просто верните исходный массив.

  • В противном случае создайте новый массив нулей для вставки в освободившиеся ячейки.
  • Теперь выполните итерацию по массиву в обратном порядке от last cell index к shift value, копируя соответствующие ссылки на массив по ходу действия.
  • После завершения просто скопируйте clone нулевого массива в освободившиеся ячейки, используя shift как index, и уменьшите соответственно.
public static int[][] move(int[][] a, int shift) {
  
   // handle special cases
   if (shift > a.length || shift < 0) {
            throw new IllegalArgumentException(
                    "Shift value(%d) out of range for array"
                            .formatted(a.length));
   }
   
   if (a == null || a.length == 0 || shift == 0) {
       return a;
   }
   
   // create zero array replacement
   int[] zeros = new int[a[0].length];
   Arrays.fill(zeros, 0);
   
   // copy the references
   for (int i = a.length-1; i >= shift; i--) {
       a[i] = a[i-shift];
   }
   // fill the "vacated" cells.
   while(shift-- > 0) {
       a[shift] = zeros.clone(); 
   }
   return a;
}

Другие вопросы по теме