Понимание того, как работает рекурсионные работы. В этом уроке мы собираемся посмотреть, как сгладить массив с использованием итерации. После этого я напишу рекурсивное решение, чтобы сравнить это тоже.
Сила рекурсии – это стек. Каждый раз, когда функция вызывает саму собой, все переменные повторно инициализируются. Если вы хотите поделиться состоянием между вызовами функций, вы можете пропустить их вдоль каждого вызова функции явно, либо можно использовать закрытие для инкапсуляции структур данных, которые вы хотите получить доступ к всем вызовам. Есть плюсы и минусы к каждому здесь.
Подход, который мы здесь будем использовать, заключается в том, чтобы имитировать инкапсуляцию, то есть наш штатный стек не должен проходить вдоль результирующего массива выходных данных к каждой итерации.
Вот обзор нашего подхода:
Мы собираемся использовать первый итеративный метод глубины, чтобы сгладить массив. Идея состоит в том, чтобы добавить каждый элемент без массива в массив результатов. Когда массив обнаружен, мы выполняем некоторое бухгалтерское обслуживание, чтобы помнить, где мы остановились, то начните анализировать этот подпара. Повторить.
Сохраните массив результатов и стек состояния для отслеживания двух состояний,
- массив для анализа и
анализировать
- Расположение в этом массиве, чтобы начать анализ
Начало
Мы захотим начать с настройки начального состояния стека, называемого куча
- Массив будет базовым массивом
- Индекс будет Zeroth Index
Так что все в нашем во время цикла составляют из этих состояний, Начать
типа Номер
и анализировать
типа массив <неизвестно>
Теперь, пока у нас есть штаты в нашем стеке куча
- Итайте этот массив, пока не обнаружена подпара; В противном случае добавьте значение на массив результатов
- Когда обнаружена подразряд
- Помните наше текущее состояние
- Настройте следующее состояние, подкаребь и ноль индекса
Как только мы больше не указываем, верните результат.
// O(N) function flattenArrIter( arr ) { // edge case, array is empty if(!arr.length) return arr // output array let result = [] // track states, start with first item in // given array let stack = [{ start:0, analyze: arr }] // while we have states in the stack while( stack.length ) { // destructure the states let { start, analyze } = stack.pop() let index = start // iterate until an array is discovered or we run out of items in the array while( index < analyze.length && !Array.isArray( analyze[index] ) ) { result.push( analyze[index] ) index++ } // if we have items in the array, we know that we must have found // an embedded sub-array if( index < analyze.length ) { // remember to start with the next item in the array after // analyzing the sub array stack.push({ start:index + 1, analyze }) // start analyzing the sub array stack.push({ start:0, analyze: analyze[index] }) } } // no more items in the stack return result }
Теперь давайте посмотрим на рекурсивный способ сделать это. Во-первых, мы будем использовать инкапсуляцию.
function flattenArrWithEncapsulation( arr ) { // encapsulation, our helper will have access // to result array across function calls let result = [] helper( arr ) return result function helper( analyze ) { for( let index = 0; index < analyze.length; index++ ) { let item = analyze[index] if( Array.isArray(item) ) { helper( item ) } else { result.push( item ) } } } }
Эта функция чистая, потому что, пока она внутренне мутирует, это детерминировано и ничего не мутирует ничего.
Кроме того, его важно понять, что, хотя сама функция чистая, внутренняя помощник
Функция не чистая. Это связано с этой функцией и мутаты значения вне сама по себе. В этом случае Результат
массив мутирован.
Это хорошая функция для изучения при сравнении рекурсии к итерации, по крайней мере, для этого конкретного случая. Обратите внимание, как каждый рекурсивный звонок будет порождать новые значения для индекс
и анализировать
Как и итеративный метод. Вот как мы “используем рекурсивный стек”!
Примечание:
Потому что мы можем использовать для
Синтаксис здесь, мы можем повторно написать это более элегантным способом
function flattenArrWithEncapsulation( arr ) { // encapsulation, our helper will have access // to result array across function calls let result = [] helper( arr ) return result function helper( analyze ) { for( let item of analyze ) { if( Array.isArray(item) ) { helper( item ) } else { result.push( item ) } } } }
Эта маска, как рекурсия отслеживает индекс в этом стеке. Я думал, чтобы включить его здесь, чтобы обеспечить дополнительную точку сравнения.
Если вам интересно окапсуляция и мутация, давайте рассмотрим, как мы могли бы переписать, используя инкапсуляцию
function flattenArrNoEncapsulationNotPure( arr, result = [] ) { for( let item of arr ) { if( Array.isArray(item) ) { flattenArrNoEncapsulationNotPure( item, result ) } else { result.push( item ) } } return result }
Аккуратный! И все же эта функция не чистая! Это мутирует результат!
Давайте посмотрим, как мы можем написать это без капсулирования и нет мутации
// Don't use this, very inefficient! ~ greater than O(N^3) time complexity! function flattenArrNoMutationNoEncapsulation( arr, index = 0, result = [] ) { // base case if( arr.length === index) { return result } if( Array.isArray( arr[index] ) ) { return result.concat( flattenArrNoMutationNoEncapsulation( arr[index], 0, result) ) } else { return result.concat( arr[index] ).concat( flattenArrNoMutationNoEncapsulation( arr, index + 1, result) ) } }
Хорошо, я не рекомендую использовать это, потому что он намного медленнее, чем здесь все другие функции. На самом деле, это не будет работать в O (n)
время! Это потому, что каждый Concat
Вызов беги в линейное время, в отличие от толчок
что постоянное время O (1)
операция.
Я верю, что сложность времени растет в квадрате пирамидального времени
https://en.wikipedia.org/wiki/Square_pyramidal_number
В этой статье мы смотрели, как вручную хранить состояние штата для эмуляции рекурсивных решений с использованием итерации. Эти виды физических упражнений помогают понять, что такое «рекурсивный стек». Я надеюсь, что это было полезным введением в насколько итерация и рекурсия относится друг к другу.
Оригинал: “https://dev.to/babak/understanding-recursion-using-iteration-or-vice-versa-3l5o”