Как найти самую длинную возрастающую подпоследовательность?

#java #arraylist

#java #arraylist

Вопрос:

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


например, если входные данные были: 3,5,11,8,4,7,1,2,10,12,9,6

Вывод : [1, 2, 6, 9, 12]

мой желаемый результат: 3,4,7,10,12

Я отладил способ вставки и печати arraylist и его исправления, я считаю, что здесь есть что-то неправильное, почему это было неправильно?почему это меняет порядок чисел !?

 public static ArrayList<Integer> lengthOfsequance(ArrayList<Integer> nums) {

    if (nums == null || nums.size() == 0)
        return null;
    ArrayList<Integer> list = new ArrayList<Integer>();
    for (int num : nums) 
    {
        if (list.size() == 0 || num > list.get(list.size() - 1)) 
            list.add(num);
        else
        {
            int i = 0;
            int j = list.size() - 1;
            while (i < j) 
            {
                int mid = (i   j) / 2;
                if (list.get(mid) < num) 
                    i = mid   1;
                else
                    j = mid;
            }
           list.set(j, num);
        }
    }
    return list;
}
  

Комментарии:

1. Каков ваш желаемый результат в вашем примере?

2. 1,2,6,9,12 не является подпоследовательностью вашего указанного ввода 3,5,11,8,4,7,1,2,10,12,9,6.

3. Я думаю, что это был результат его программы, а не то, что он хотел

4. то, что я хотел, это 3,4,7,10,12, а не 1,2,6,9,12, также я не должен изменять порядок чисел

5. Я ценю сюрприз, принимаю 🙂

Ответ №1:

Ваш подход не может работать. Вы используете один список для сбора самой длинной последовательности. Но ваша входная последовательность может иметь несколько возрастающих последовательностей. Может быть, тот, который идет от индекса 0 до 5; и другой, который идет от индекса 10 до 20.

Другими словами; вам нужна другая стратегия; и «прямой» подход — это что-то вроде:

  1. повторите список входных данных, чтобы «собрать» все возрастающие последовательности (другими словами: это будет список списков).
  2. После этого вы просматриваете этот список списков, чтобы найти максимальную длину

Конечно, затем это можно дополнительно оптимизировать — вам не нужно запоминать все возрастающие последовательности, вам просто нужно помнить о «самой длинной завершенной» последовательности, которую вы видели раньше.

Но, как уже было сказано: первой, наивной реализацией было бы просто идентифицировать все последовательности «увеличение завершение» во входном списке. И затем вы прокладываете свой путь оттуда.

(поскольку я предполагаю, что это какой-то проект домашнего задания, я только даю руководящие идеи, а не исходный код. Реализация оставлена в качестве упражнения для читателя)

Редактировать: для решения комментария «как избежать запоминания «равных» последовательностей».

Предположим, что ваш ввод — это такая последовательность: 1,2,3,0,1,2,3,4,1,2,3 Когда вы ищете там возрастающие последовательности, вы должны найти:

(1, 2, 3) и (0, 1, 2, 3, 4), и (1, 2, 3)

Вы правы: если вы представляете вышеупомянутые кортежи как List<Integer> ; тогда «правильный» код будет собирать три списка; где первый и третий будут равны (потому что они содержат одинаковые числа).

И когда вы сохраняете эти списки внутри List<List<Integer>> , да, ваш код должен учитывать дубликаты. Одним из способов избежать этого может быть использование a Set<List<Integer>> вместо — потому что Set по своей природе гарантирует, что все записи, хранящиеся в этой коллекции, не равны. Другими словами: когда вы добавляете два списка с одинаковым содержимым в объект Set, тогда, в конце концов, набор будет содержать только один из этих двух списков. (следует помнить одну вещь — когда вы используете здесь HashSet , порядок операций добавления теряется).

Наконец: вместо использования Set<List<Integer>> вы могли бы также использовать a Map<Integer, Integer> с «ключом», являющимся длиной последовательности; и «значение», являющееся первым индексом в списке ввода.

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

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

Комментарии:

1. Спасибо! я подумал о том, что вы сказали, моя единственная проблема в том, что когда я хочу запомнить все увеличивающиеся последовательности, как я узнаю, что я не совсем готов вспомнить эту подпоследовательность раньше?

2. Обновил мой ответ, чтобы рассмотреть эту часть… снова.

3. тогда не будет ли соответствие равно o (2 ^ n)?

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