Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Data Structures and Algorithms in C++ 2e (На ру...docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.37 Mб
Скачать

3.5. Рекурсия 143

Рекурсия хвоста

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

Мы можем использовать структуру данных стека, обсужденную в Разделе 5.1, чтобы преобразовать повторение - sive алгоритм в нерекурсивный алгоритм, но есть некоторые случаи, когда мы можем сделать это преобразование более легко и эффективно. Определенно, мы можем легко подставить - vert алгоритмы та рекурсия хвоста использования. Алгоритм использует рекурсию хвоста, если это использует линейную рекурсию, и алгоритм сделал рекурсивный звонок как свое самое последнее действие. Например, алгоритм Кодового Фрагмента 3,39 рекурсии хвоста использования, чтобы полностью изменить элементы множества.

Это недостаточно, что последнее заявление в определении функции включает рекурсивный вызов, как бы то ни было. Для функции, чтобы использовать рекурсию хвоста, рекурсивный вызов должен быть абсолютно последней вещью, которую функция делает (если мы не находимся в основном случае, конечно). Например, алгоритм Кодового Фрагмента 3.38 не использует рекурсию хвоста, даже при том, что ее последнее заявление включает рекурсивный вызов. Этот рекурсивный вызов не фактически последняя вещь, которую делает функция. После того, как это получает стоимость

возвращенный из рекурсивного вызова, это добавляет эту стоимость к [n- 1] и возвращает эту сумму.

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

Когда алгоритм использует рекурсию хвоста, мы можем преобразовать рекурсивный алгоритм

в нерекурсивный, повторяя через рекурсивные вызовы, а не требование - луг их явно. Мы иллюстрируем этот тип преобразования, пересматривая prob-lem изменения элементов множества. В Кодовом Фрагменте 3.40, мы даем нерекурсивный алгоритм, который выполняет эту задачу, повторяя через рекурсивные вызовы алгоритма Кодового Фрагмента 3.39. Мы первоначально называем этот алгоритм как

IterativeReverseArray (A, 0, n- 1).

Алгоритм IterativeReverseArray (A, я, j):

Вход: множество A и неотрицательная Продукция индексов i и j целого числа: аннулирование элементов в старте в индексе i и окончании в j, в то время как я <j делаю

Обменяйтесь [я] и [j]

я¬ i+1

j¬ j-1

возвратиться

Кодовый Фрагмент 3.40: Изменение элементов множества, используя повторение.

144 Глава 3. Множества, связанные списки и рекурсия

3.5.2 Двойная рекурсия

Когда алгоритм сделал два рекурсивных звонка, мы говорим, что он использует двойную рекурсию. Эти требования могут, например, использоваться, чтобы решить две подобных половины некоторой проблемы, как мы сделали в Разделе 3.5 для привлечения английского правителя. Как другое применение двойной рекурсии, давайте пересмотрим проблему подведения итогов n элементов A множества целого числа. В этом случае мы можем суммировать элементы в: (i) рекурсивно подведение итогов элементов в первой половине A; (ii) рекурсивно подведение итогов элементов во второй половине A; и (iii) добавление этих двух ценностей вместе. Мы даем детали в алгоритме Кодового Фрагмента 3.41, который мы первоначально называем как BinarySum (A, 0, n). Алгоритм BinarySum (A, я, n):

Вход: множество A и целые числа i и Продукция n: сумма n целых чисел в старте в индексе i, если n = 1 тогда

возвратитесь [я]

возвратите BinarySum (A, я,n/2 ⌉) + BinarySum (A, я +n/2,n/2 ⌋)

Кодовый Фрагмент 3.41: Подведение итогов элементов во множестве, используя двойную рекурсию.

Чтобы проанализировать Алгоритм BinarySum, мы рассматриваем, для простоты, случай где

n - власть два. Общий случай произвольного n рассматривают в Упражнении R-4.5. Рисунок 3.20 показывает след рекурсии выполнения функции BinarySum (0, 8). Мы маркируем каждую коробку ценностями параметров i и n, которые представляют начало - индекс луга и длина последовательности элементов, которые будут суммированы, соответственно. Нет - tice, что стрелки в следе идут от маркированной коробки (я, n) к другой маркированной коробке (я, n/2) или (я + n/2, n/2). Таким образом, ценность параметра n разделена на два в каждом, повторяются - sive требование. Таким образом глубина рекурсии, то есть, максимального количества случаев функции, которые активны в то же время, равняется 1 + log2 n. Таким образом Набор из двух предметов Алгоритма - Сумма использует сумму дополнительного пространства, примерно пропорционального этой стоимости. Это - большое улучшение по пространству, необходимому функции LinearSum Кодового Frag-ment 3.38. Продолжительность Алгоритма, BinarySum все еще примерно пропорционален n, однако, так как каждую коробку посещают в постоянное время, ступая через наш

алгоритм и есть 2n- 1 коробка.

Рисунок 3.20: след Рекурсии для выполнения BinarySum (0, 8).