предисловие
Перед проектом системы управления компанией необходимо сотрудничать с интерфейсомVUE
Для реализации динамической маршрутизации возвращаемая структура данных представляет собой древовидную структуру, но параллельные элементы данных хранятся в базе данных. В это время вам нужно собрать структуру данных в коде. Я давно не думал об этих проблемах, и потребовалось много времени, чтобы их решить. Это включает в себя реализацию рекурсивного алгоритма.Я взял выходные, чтобы глубоко подумать о дизайне рекурсивного алгоритма, и записал немного своего скромного мнения, чтобы сделать заметку.
Что такое рекурсия?
за递归
На самом деле, мы не незнакомы. Помнишь, когда я использовал его в старшей школе?数学归纳法
? ,递归
Эта концепция не является уникальной для информатики. Фактически数学归纳法
Это теоретическая основа рекурсии.
Начнем с простого каштана: найдите n Здесь мы используем рекурсию для решения.
int foo(int n) {
if(n != 1) {
return n*foo(n-1);
}
return 1;
}
Смысл приведенного выше кода: когдаn
не равно1
когда. Продолжай звонитьfoo(n-1)
и с входящимn
Умножить. Объясните здесь принцип реализации рекурсивного вызова: мы знаем, что вызов функции в компьютере реализуется с использованием стековой структуры данных, такой как входящиеn
за5
, первое исполнениеfoo
метод, при выполнении которогоfoo(n-1)
,методfoo
будет помещен в стек. Фактически рекурсивный метод ничем не отличается от обычного метода. Единственное отличие состоит в том, что рекурсивный вызов представляет собой собственный код. Здесь также можетfoo
Каждый вызов вызываетсяfoo1
,foo2
и Т. Д. Тогда легко понять, когдаfoo
метод выполняется дляn==1
При , стек хранится по очередиfoo1,foo2,foo3,foo4,foo5。
В настоящее времяfoo5
В верхней части стека извлеките стек.foo5
при исполненииn==1
, как видно из кода,n==1
, метод возвращает напрямую1
. тогдаfoo4
выскочил, входящийn
за2
, операция2*1
,Потомfoo3,foo2,foo1
Извлекайте стек по очереди, и операции следующие:3*2,4*6
когда последний методfoo1 return
расчет времени,5*24
, операция заканчивается.
Из этого процесса нетрудно увидеть, что рекурсия является непрерывным нисходящим процессом. Слои вызывают сами себя, а затем оценка является восходящим процессом. Начиная с возвращаемого значения выхода в конце рекурсии, оно оценивается слой за слоем.
Из этого примера также можно увидеть две важные особенности рекурсивных алгоритмов, например:
- продолжайте называть себя
- Есть условие завершения, иначе получится бесконечный цикл
Вопрос, над которым следует подумать, заключается в том, как определяется условие завершения и как оно изменяется. В приведенной выше программе нужно сделать-1
работать.
Когда следует рассмотреть возможность использования рекурсии?
когда определение рекурсивно
Это тот же тип, что и в приведенном выше примере для нахождения факториала.斐波那契数列
Определение является рекурсивным. Посмотрите прямо на код:
int Fib(int n) {
if(n==1 || n==2) {
return 1;
}else {
Fib(n-1)+Fib(n-2)
}
}
斐波那契数列
:1,1,2,3,5 ...
Когда сама структура данных рекурсивна
Самый распространенный пример — определение связанного списка (здесь речь идет о связанном списке без головного узла):
class Node {
private String data; //节点数据域
private Node next; //指向下一个节点
}
Когда вам нужно суммировать поля данных связанного списка, вы можете использовать рекурсивную реализацию:
int SUM(Node node){
if(node == null) {
return 0;
}else {
return node.getData()+SUM(node.getNext());
}
}
Когда проблему нужно решать рекурсивно
Реализация алгоритма Ханойской башни подробно обсуждается здесь.
Описание задачи о ханойской башне: есть три башни с именами X, Y и Z. На основании башни X расположены диски разного диаметра, которые пронумерованы от меньшего к большему: 1, 2, 3...n.Теперь требуется переместить диски на основании башни X на основание башни Z, в один и тот же порядок сложен. При перемещении необходимо соблюдать правила: 1. За один раз можно перемещать только одну тарелку. 2. Тарелку можно поставить на любую башню. 3. Большую тарелку нельзя ставить поверх меньшей.
Ханойские башни — типичная задача рекурсивного решения. Анализировать декомпозицию задачи по описанию непросто, лучше попытаться проанализировать решение, когда масштаб проблемы невелик.
Когда на башне X только одна тарелка:
X==>Z
Когда на башне X две тарелки:
X1==>Y,X2==>Z
Y1==>Z
Когда на башне X три тарелки:
X1==>Z,X2==>Y
Z==>Y,X==>Z
Y1==>X,Y2==>Z
X==>Z
Рекурсивный алгоритм дан первым, этот алгоритм распечатает шаги перемещения.
void Hanoi(int n,String X,String Y,String Z) {
if(n == 1) {
System.out.println("将第"+n+"个盘片从"+X+"移到"+Z); //X==>Z
}else {
Hanoi(n-1,X,Z,Y);
System.out.println("将第"+n+"个盘片从"+X+"移到"+Z); //X==>Z
Hanoi(n-1,Y,X,Z);
}
}
Основное требование к Ханойской башне — поместить самый большой внизу, поэтому, когдаX
Когда количество тарелок на башне больше 1, мы сначала ставимX
на башне,1至n-1
блюдо наY
, поэтому мы можем игнорироватьX
нижняя тарелка, положитьY
в видеZ
,Z
в видеY
, поэтому проблема возвращается, когдаX
имеютn-1
тарелки, как перейти кZ
ситуация. запомни это времяZ
даY
. Теперь мы сделалиX
начальство1至n-1
диск помещаетсяY
начальство. не забудьтеX
Есть также одна из самых больших тарелок, которые мы упустили из виду.
Хорошо, теперь поставьX
Поставьте самую большую тарелкуZ
,В настоящее времяX
без тарелок,Y
имеют1至n-1
тарелки, пока игнорируемZ
самая большая тарелка на . будетY
в видеX
,X
в видеY
. Проблема вернуласьX
Тамn-1
Как блюдо перемещается наZ
ситуация.
Посмотрите еще раз на приведенный выше код. когда
n==1
, напрямуюX
перейти кZ
, в противном случаеY
в видеZ
,Z
в видеY
,будет1至n-1
блюдо перемещается вY
начальство. Выньте это в это времяX
Поставьте самую большую тарелкуZ
. поставь это сноваX
когдаY
,Y
в видеX
, продолжайте рекурсивно. до того какn==1
, ход удался! В самом деле, нетрудно обнаружить, что при исполнении до второгоSystem.out.println("将第"+n+"个盘片从"+X+"移到"+Z);
Время. завершилX
Самая большая тарелка переехала вZ
Цель. Количество этажей в Ханойской башне уменьшено на один. Проблема возвращается к исходной ситуации, за исключением того, что тарелка находится вY
на, неX
начальство. Но это не мешает звонитьHanoi
метод. На самом деле суть проблемы в том, чтоX,Y,Z
Три башни одинаковы без каких-либо различий. После еще одного цикла операции просто установите нижний диск.Z
, на этот раз масштаб проблемы уменьшен на единицу.
Как разработать рекурсивный алгоритм?
найти условие окончания рекурсии
Одна из рекурсивных функций, описанных выше, гласит, что рекурсия должна иметь условие завершения. Например, факториал, последовательность Фибоначчи и задача о Ханойской башне, которые представляют масштаб проблемы при рекурсивном вызове.n
продолжайте уменьшаться до тех пор, пока не будет достигнуто условие окончания рекурсииn==1
. На самом деле рекурсивное условие не обязательно таково, что оценочное значение конечного условия достигается за счет уменьшения.Например, при построении древовидной структуры вы можете судить о значении элемента данных, оцениваяisLeaf
Полеtrue
чтобы решить, выходить ли из рекурсии. Одно можно сказать наверняка: условие рекурсивного выхода должно быть скрыто в параметрах, передаваемых методу при первом вызове, и в любом состоянии, к которому можно получить доступ во время выполнения метода.
найти тело цикла
Это относительно сложная проблема.Для подобных задач с математическим определением телом цикла часто являются слова, которые определяют проблему, например, определение последовательности Фибоначчи:当0<n<3时,Fib=1,当n>2时,Fib(n)=Fib(n-1)+Fib(n-2)
. При разработке алгоритма для рекурсивной структуры данных тело цикла представляет собой инструкцию, которая обращается к рекурсивной структуре. Например, при обходе связанного списка:
void foo(node) {
if(node != null) {
foo(node.getNext());
}else {
return;
}
}
Однако для таких задач, как Ханойская башня, нет интуитивного способа непосредственно увидеть характеристики рекурсии. Личный опыт заключается в выполнении расчетов на меньших масштабах задачи. Откройте для себя рекурсивные функции.
резюме
Рекурсивное программирование само по себе относительно сложно понять и освоить. Его нельзя использовать умело с помощью простого теоретического обучения. Только постоянное решение проблем может привести к совершенству.