당신은 노드가 터진 후, 오른쪽 아이가 여전히 통과해야한다는 사실이 누락
: 당신은 스택에 데이터 만 가지고 있다면
current = s.top();
s.pop();
cout << current->data << " ";
current = current->right;
을이 불가능하다. 루프 불변량은 스택이 우회되지 않은 오른쪽 자식이있는 노드를 정확하게 보유한다는 것입니다.
무슨 일이 일어나고 있는지 볼 수있는 또 다른 방법은 대수에 의해 반복적으로 재귀 순회를 변환하는 것입니다
traverse(node) {
if (node) {
traverse(node->left);
visit(node);
traverse(node->right);
}
}
먼저 반복에 꼬리 호출을 변환합니다. 우리는 함수의 시작 인수를 업데이트하고 goto
로 재귀 호출을 대체하여이 작업을 수행 :
traverse(node) {
start:
if (node) {
traverse(node->left);
visit(node);
node = node->right;
goto start;
}
}
goto
및 if
가 while
과 동일을, 그래서 우리는 지금까지
traverse(node) {
while (node) {
traverse(node->left);
visit(node);
node = node->right;
}
}
이
다른 재귀 호출을 대체하려면 컴파일러 런타임 환경의 호출 스택을 시뮬레이트해야합니다. 우리는 명시 적 스택을 사용하여이를 수행합니다.
traverse(node) {
start:
while (node) {
stack.push(node); // save the value of the argument.
node = node->left; // redefine it the same way the recursive call would have
goto start; // simulate the recursive call
// recursive call was here; it's gone now!
recursive_return: // branch here to simulate return from recursive call
visit(node);
node = node->right;
}
// simulate the recursive return: if stack has args, restore and go to return site
if (!stack.empty()) {
node = stack.pop(); // restore the saved parameter value
goto recursive_return;
}
}
이것은 추악하지만 재귀 코드의 반복 버전을 구현하는 방식입니다. (꼬리가 아닌 재귀 호출이 여러 개있는 경우에는 더 복잡합니다.) 그리고 코드와의 유사성을 확인할 수있을 것입니다.
우리는 심지어 더 많은 대수를 사용하여 추함을 없앨 수 있습니다. start
로 시작하는 실행
while (node) {
stack.push(node); // save the value of the argument.
node = node->left; // redefine it the same way the recursive call would have
}
에 해당하는 경우
start:
while (node) {
stack.push(node); // save the value of the argument.
node = node->left; // redefine it the same way the recursive call would have
goto start; // simulate the recursive call
이
우리는 또한 다음과 같은
if (!stack.empty()) {
node = stack.pop(); // restore the saved parameter value
visit(node);
node = node->right;
goto start;
}
와
if (!stack.empty()) {
node = stack.pop(); // restore the saved parameter value
goto recursive_return;
}
을 대체 할 수있다 : 첫째,이 코드를 볼 어렵지 않다
recursive_return:
다음의 세 명령을 if
본문에 복사했습니다.이와
는 방법이 recursive_return
라벨에 도착 남아 있지, 그래서 우리는 다음 두 문장과 함께 삭제할 수 있습니다 :
// Dead code! Delete me!
recursive_return:
visit(node);
node = node->right;
우리는 지금이 :
traverse(node) {
start:
while (node) {
stack.push(node); // save the value of the argument.
node = node->left; // redefine it the same way the recursive call would have
}
if (!stack.empty()) {
node = stack.pop(); // restore the saved parameter value
visit(node);
node = node->right;
goto start;
}
}
우리는 무한 루프로 교체하여 마지막 goto start
을 제거 할 수 있습니다.
이전 코드와 동일한 조건에서 반환됩니다. 스택이 비어 있습니다!
이 코드는 사용자가 제시 한 코드와 동일하다는 것을 증명할 것입니다. 일부 비교를 피하기 때문에 좀 더 효율적입니다. 우리는 포인터와 스택 요소에 대해 전혀 추론 할 필요가 없었습니다. 그것은 "그냥 일어났다."
아, 왜 데이터를 사용할 수 없는지 알 겠어. 그것은 매우 훌륭하고 분명한 포인트입니다. 감사! 스택이 격리 된 노드 또는 각 노드의 자식을 보유하고 있습니까? –
@Brandon "고립 된"과 같은 용어는 매우 의미가 없습니다. 스택은 검색이 도착했지만 아직 방문하지 않은 노드를 정확하게 보유합니다. 이것은 또한 이러한 스택 노드의 오른쪽 자식을 루트로하는 하위 트리도 가로지를 수 없음을 의미합니다. – Gene
나는 그것을 지금 본다. 나는 오른쪽에 대해서도 궁금해했다. 고마워요. 이것은 많은 도움이됩니다! 와우, 나는이 개념을 얼마나 이해하지 못했는지 알지 못했다. 좋은 하루 보내십시오 –