У меня есть сомнения в рекурсии, если я напишу код, как показано ниже
inorder(p){
if(p!=NULL){
inorder(p->link); //step 1
cout<<p->info<<" "; //step 2
inorder(p->link); //step 3
}
}
Здесь я сомневаюсь, что, когда выполняется шаг 1, управление возвращается к функции, а затем снова выполняется шаг 1, и снова элемент управления возвращается к функции до p, является NULL, если это процесс, то как управление будет переходите к шагу 2, который является "cout" и к шагу 3...
Я не могу закодировать код в своем мозгу...
Прежде чем "управление возвращается к одной и той же функции" на шаге 1, CPU делает важный шаг: он отмечает место в коде inorder
где ему нужно перезапустить выполнение, как только "второй уровень" inorder
вернется (это пятно сразу после шаг 1). Там "второй уровень" снова отмечает позицию возврата, прежде чем перейти на "третий уровень", "четвертый уровень" и т.д. В конце концов, уровень N
-th получает NULL
, поэтому он сразу возвращается. Затем уровень N-1
-th получит распечатку info
и inorder
во второй раз. Теперь место возврата отличается - оно сразу после шага 3. Как только уровень N
-th заканчивается, уровень N-1
-st также заканчивается, возвращаясь к уровню N-2
-nd, затем до N-3
-rd ] и т.д., пока не выйдет самый первый уровень.
Вот пример дерева:
A
/ \
B C
/ \
D E
Процесс обхода порядка выглядит следующим образом:
inorder(A) -- step 1, level 1
inorder(B) -- step 1, level 2
inorder(NULL) -- returns
cout << B -- step 2, level 2
inorder(NULL) -- returns
return -- level 2 returns
cout << A -- step 2, level 1
inorder(C) -- step 3, level 2
inorder(D) -- step 1, level 3
inorder(NULL) -- returns
cout << D -- step 2, level 3
inorder(NULL) -- returns
return -- level 3 returns
cout << C -- step 2, level 2
inorder(E) -- step 1, level 3
inorder(NULL) -- returns
cout << E -- step 2, level 3
inorder(NULL) -- returns
return -- level 3 returns
return -- level 2 returns
return -- level 1 returns
Каждый раз, когда вызывается функция inorder, данные (называемые кадром активации или фреймом) помещаются в структуру данных, называемую стеком вызовов. Каждый кадр отслеживает данные, локальные для функции, такие как параметры, переданные в функцию, указатель на кадр активации вызывающего функции и, что важно, адрес следующей строки кода, который должен быть выполнен в вызывающей функции. Так как вы рекурсивно вызываете inorder, вы рекурсивно добавляете кадры в этот стек, и каждый кадр содержит адрес следующей строки кода, который должен быть выполнен, когда соответствующая функция завершена, и управление возвращается вызывающему функции.
Другими словами, когда вы вызываете inorder из строки, указанной как шаг 1, а переданный параметр p равен NULL, когда функция выходит, вызывающая функция запускает выполнение в строке, указанной как шаг 2.
Дополнительную информацию см. на странице wikipedia http://en.wikipedia.org/wiki/Call_stack.
Понимание концепций, связанных со стеками вызовов, поможет понять, что происходит с рекурсией.
Когда выполняется первый вызов inorder (p-> link), рассмотрите его как контрольную точку. Он будет продолжать звонить до точки, в которой он достигнет NULL. Затем выполняется строка 2, а для второго вызова inorder (p-> link) выполняется то же самое. Таким образом, это образует дерево
inorder(p->link) -> coutinfo -> inorder(p->link) / ^ / ^ V / V / inorder()->cout->inorder() inorder()->cout->inorder() . ^ / \ . ^ / \ . | . . . | . . . | . | inorder(p->link) //(p->link is NULL) inorder(p->link) //(p->link is NULL)
Рассмотрите игру, было ли у вас 5 сообщений в разных местах вашего дома. Каждое сообщение приводит вас к следующему. Чтобы выиграть игру, вы должны найти все 5 сообщений и вернуть их игровому хосту. Тем не менее, вы не можете получить сообщения, когда найдете их... вы должны помнить свое местоположение и забрать их в противоположном порядке, который вы их нашли.
Вы бы подошли к первому предмету, запомните, где он находится, и следуйте подсказке; сохраняя ментальную заметку, где вам нужно вернуться позже, чтобы поднять ключ. Вы бы сделали то же самое со следующими 4 подсказками.
Когда вы найдете последний ключ, поэтому больше не существует, вы тогда начнете работать в обратном направлении, возвращаясь туда, где вы нашли каждую подсказку, извлекая их.
Поиск первой подсказки похож на ваш первый вызов "inorder()". Следуя подсказке ко второму подсказку, ваш вызов "inorder (p-> link)". Выбор подсказки после всех подсказок был найден, как когда вы возвращаетесь к "step2" в своем коде.
Предположим, что p
указывает на A
, p->link
(то есть A.link
) называется q и указывает на B
, а q->link
(то есть B.link
) равна NULL, назовите ее r.
p q r
----->A----->B----->0
Теперь мы вызываем inorder(p)
.
Я предполагаю, что ваш код правильный. Вы пытаетесь распечатать связанный список ниже:
inorder(p){
if(p!=NULL)
{
inorder(p->link); //step 1 to navigate to next link
cout<<p->info<<" "; //step 2 as main operation
inorder(p->link); //step 3 to navigate to next link
}
}
Перевести на английский
inorder(p)
{
if p points to a linked list
{
print the list p points to;
print content of p;
print the list p points to again;
}
if p points to a null linked list
{
do nothing;
}
}
Тогда связанный список 1 → 2 → 3 выведет ((3) 2 (3)) 1 ((3) 2 (3))
Как вы можете видеть, элемент управления переходит только к шагу 2, как только шаг 1 встречает нулевой список. После того, как информация распечатана, она затем переходит к шагу 3.
Следовательно,
Когда связанный список в узле "3",
шаг 1 встречает null и return;
шаг 2 распечатывает 3;
шаг 3 встречает null и return;
Когда связанный список в узле "2"
шаг 1 выполняет все, что он делает на узле "3"; //печать 3
шаг 2 распечатывает 2;
шаг 3 выполняет все, что он делает на узле "3"; //печать 3
когда связанный список в узле "1"
шаг 1 выполняет все, что он делает на узле "2"; //печать 3 2 3
шаг 2 распечатывает 1;
этап 3 выполняет все, что он делает на узле "2"; // печать 3 2 3
Первый раз, когда я столкнулся с рекурсией, была факториальная функция. Factorial является базовой функцией в математике, учитывая число N, оно возвращает число, равное произведению всех положительных целых чисел, меньших N.
Рассмотрим этот простой код в C++, который хорошо демонстрирует рекурсию.
int fact(int x)
{
if(x==1)
return 1;
x = x*fact(x-1);
return x;
}
Это вводит целое число, а затем вызывает себя после декрементации целого числа, пока целое число равно 1 и не вернет x. Обратите внимание, что строка 'return x;' не вызывается, пока линия не закончится.
вы должны узнать о call stack
если хотите понять, как работает рекурсия. Вот ссылка. Надеюсь, это поможет...
left
иright
? ..