Int[] vs ArrayList<>() в мемоизации, динамическое программирование в Java

Недавно я смотрел учебник по динамическому программированию на Youtube, объясняющий динамическое программирование, но репетитор решил проблемы с JavaScript. Я, с другой стороны, использую Java для структур данных и алгоритмов. При реализации динамического программирования для решения вопроса. Я обнаружил, что получил решение проблемы при использовании int[], но получил неправильный ответ при использовании ArrayList<Integer>, потому что каким-то образом ArrayList, уже сохраненный в HashMap, модифицировался внутри.

Вопрос: Напишите функцию bestSum(targetSum, numbers), которая принимает targetSum и массив чисел в качестве аргументов и возвращает массив, содержащий кратчайшую комбинацию чисел, которая в сумме дает точно заданную сумму. Пример:
bestSum(7,new int[]{2,1,3}) => [3,3,1] //другие возможности, но не ответ:[2,2,2,1], [1,1,1, 1,1,1,1], [2,2,1,1,1] и т. д.
bestSum(100,new int[]{2,5,25}) => [25,25,25,25]

Код с использованием int[]:

public class Persist {
    public static HashMap<Integer,int[]> memo = new HashMap<>();
    public static int[] bestSum(int n, int[] arr){
        if (memo.containsKey(n)){
            //System.out.printf("From memo: %d->"+ Arrays.toString(memo.get(n)) +"%n",n);
            return memo.get(n);
        }

        if (n==0)return new int[0];

        if (n<0)return null;

        int[] minn = null;

        for(int i = 0;i<arr.length;i++){
            
            //recursion
            var temp = bestSum(n-arr[i],arr);

            if (temp!=null){

                // ttemp is used to add arr[i] to the initial arr <<temp>>
                int[] ttemp = new int[temp.length+1];
                System.arraycopy(temp,0,ttemp,0,temp.length);
                ttemp[temp.length] = arr[i];
                temp = ttemp;

                if (minn==null||temp.length<minn.length){
                    minn = temp;
                }
            }
        }
        //System.out.println(n+": "+minn);
        memo.put(n,minn);
        //System.out.println(memo.get(n));
        return minn;
    }
    public static void main(String[] args){
        System.out.println(Arrays.toString(bestSum(7, new int[]{2,1,3})));
    }
}

Код с использованием ArrayList<Integer> :

public class Persist {
    public static HashMap<Integer,ArrayList<Integer>> memo = new HashMap<>();
    public static ArrayList<Integer> bestSum(int n, int[] arr){
        if (memo.containsKey(n)){
            //System.out.printf("From memo: %d->"+ memo.get(n)+"%n",n);
            return memo.get(n);
        }
        if (n==0)return new ArrayList<>();
        if (n<0)return null;
        ArrayList<Integer> minn = null;
        for(int i = 0;i<arr.length;i++){
            var temp = bestSum(n-arr[i],arr);
            if (temp!=null){
                temp.add(arr[i]);
                if (minn==null||temp.size()<minn.size()){
                    minn = temp;

                }
            }
        }
        //System.out.println(n+": "+minn);
        memo.put(n,minn);
        //System.out.println(memo.get(n));
        return minn;
    }
    public static void main(String[] args){
        System.out.println(bestSum(7,new int[]{2,1,3}));
    }
}

Единственное различие между двумя фрагментами кода заключается в использовании int[] и ArrayList<Integer> соответственно, но один работает, а другой нет. Я хотел бы знать, почему, спасибо. Ссылка на Youtube объяснение bestSum()

Напомните мне, вы получаете глубокую копию, когда делаете memo.put(n, minn);?

Surt 14.12.2020 02:11

@ Surt Я сделал что-то подобное, но все равно получил неправильные ответы. memo.put(n,minn==null?null:new ArrayList<>(minn));

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

Ответы 1

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

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

Если вы отлаживаете и смотрите на свой hashmapmemo, вы видите, что размеры int[] достигают только 3, тогда как в хэш-карте arraylist большинство значений имеет размер 7

У меня была похожая проблема: кастинг не работает с типом объекта вложенного списка и возвращает пустые списки (List<List<Integer>>)

то же самое относится к С++?

lordvidex 14.12.2020 08:08

Также hashCode() значения реализации ArrayList одинаков для ключей: 5,6,7

lordvidex 14.12.2020 08:14

@lordvidex C++ не копируется, если вы не укажете ссылку, хотя копия не распространяется через указатели, если не определены соответствующие конструкторы копирования.

Surt 14.12.2020 11:41

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