Вы программист, и вам дан массив целых чисел, и ваша задача состоит в том, чтобы напечатать все возможные подпоследовательности, но все подпоследовательности должны быть в порядке, то есть слева -> справа.
Что, черт возьми, я имею в виду под каждой возможной подпоследовательностью?
Допустим, массив содержит 3 элемента, например {5,2,1}. И если вы погуглите слово подпоследовательность, вы получите что-то вроде этого
последовательность, которая может быть получена из данной последовательности путем удаления некоторых элементов или их отсутствия без изменения порядка оставшихся элементов.
да, я также скопировал и вставил его, в любом случае продолжая тему, поэтому для массива {5,2,1} возможная подпоследовательность будет
{5,2,1} {5,2} {5,1} {5} {2,1} {2} {1} {}
где каждая последовательность идет по порядку слева направо. {1,5} не может быть подпоследовательностью, потому что она не в порядке.
И чтобы подтвердить, что те 8 подпоследовательностей, которые мы написали выше, являются всеми возможными последовательностями, существует формула для проверки количества подпоследовательностей, которые могут быть сформированы на любой последовательности, и это 2^n где n — количество элементов в массиве или в элементах этой последовательности.
Теперь нам не надо разбираться, как работает эта формула, мы программисты, а не математики.
И для последовательности из 3 мы получаем 8, применяя эту формулу, которая, кстати, не что иное, как (2 * 2 * 2).
Теперь как написать программу, которая может вывести все возможные подпоследовательности для n элементов?
Не знаю, как вам, но когда я слышу слово «каждая подпоследовательность», мне на ум приходит только один алгоритм — рекурсия.
Теперь позвольте мне устроить вам сердечный приступ, показав код напрямую.
Вывод кода ниже
Давайте сначала разберемся с кодом, но не с его потоком пока.
мы создали массив и предварительно инициализировали его с помощью {5,2,1}, после чего я создал пустой связанный список, который будет использоваться для хранения всех возможных последовательностей, и после этого мы вызываем функцию printEverySubSequence и передаем ее следующие аргументы
0 — — — — -›, который является первым индексом массива
arr — — — —› сам массив
size — — — -› размер этого массива, который в данном случае будет равен 3
tempList — -› пустой связанный список, который мы создали.
Теперь функция printEverySubSequence, где происходит настоящее волшебство.
Ключевым моментом в функции printEverySubSequence является то, что мы начинаем с первого элемента и формируем все возможные последовательности, которые могут быть сформированы этим первым индексом, а затем переходим к следующему индексу и снова повторяем цикл.
И чтобы получить все возможные подпоследовательности для этого первого элемента, мы должны сделать два выбора: либо выбрать этот элемент, либо оставить/игнорировать его.
Например, если у меня есть массив {5,2,1}, и если я возьму первый элемент 5, у меня есть два варианта: я могу взять 5 или оставить его. И так как мы должны формировать каждую подпоследовательность, почему бы не сделать оба выбора по отдельности. И после того, как мы сделали оба выбора, мы продвигаемся вперед в массиве, увеличивая значение индекса на 1 и применяя то же самое к двум вновь сгенерированным вариантам/последовательностям, которые были созданы путем выбора этих двух вариантов и повторения цикла снова.
Вот дерево рекурсии, чтобы понять его визуально.
В этом дереве рекурсии каждый уровень представляет индекс массива, уровень 1 — это индекс 0, уровень 2 — это индекс 1, а последний уровень представляет индекс 2. А листовые узлы или наборы, выделенные желтым цветом, — это возможные последовательности, которые мы можем есть для массива {5,2,1}.
И в printEverySubSequence мы делаем эти два выбора, сначала добавляя текущий элемент индекса в tempList(это наш первый выбор), а затем снова вызывая функцию printEverySubSequence с этим tempList и увеличенным индексом. И как только он возвращается из этой подрекурсии, мы удаляем элемент из tempList, который является нашим вторым выбором, и снова вызываем функцию printEverySubSequence с tempList и снова индексируем с шагом 1.
И для базового случая при каждом вызове рекурсии мы проверяем, больше ли индекс или равен размеру, и если это так, мы печатаем текущий tempList и возвращаемся.
Временная сложность будет O(2^n)
Сложность пространства будет O(n).
Если вы узнали что-то новое из этой статьи, нажмите кнопку «Подписаться»
если (!уже)🚗