如果您在每个孩子中都有指向父母的链接,这是可能的。当你遇到一个孩子时,访问左子树。当回来检查你是否是你父母的左孩子。如果是这样,请访问右子树。否则,一直往上走,直到你成为左孩子,或者直到你碰到树根为止。
在此示例中,堆栈的大小保持不变,因此不会消耗额外的内存。当然,正如 Mehrdad 所指出的,到父节点的链接可以被认为是 O(n) 空间,但这更像是树的属性,而不是算法的属性。
如果您不关心遍历树的顺序,您可以将整数映射分配给根为 1 的节点,根的子节点为 2 和 3,子节点为 4 , 5, 6, 7 等等。然后通过递增计数器并通过其数值访问该节点来循环遍历树的每一行。您可以跟踪可能的最高子元素并在计数器通过时停止循环。在时间上,这是一个效率极低的算法,但我认为它需要 O(1) 空间。
(我借鉴了堆编号的想法。如果你有节点 N,你可以在 2N 和 2N+1 找到孩子。你可以从这个数字倒推找到孩子的父母。)
这是该算法在 C 中的一个示例。请注意,除了创建树之外没有任何 malloc,并且没有递归函数,这意味着堆栈占用恒定空间:
#include <stdio.h>
#include <stdlib.h>
typedef struct tree
{
int value;
struct tree *left, *right;
} tree;
tree *maketree(int value, tree *left, tree *right)
{
tree *ret = malloc(sizeof(tree));
ret->value = value;
ret->left = left;
ret->right = right;
return ret;
}
int nextstep(int current, int desired)
{
while (desired > 2*current+1)
desired /= 2;
return desired % 2;
}
tree *seek(tree *root, int desired)
{
int currentid; currentid = 1;
while (currentid != desired)
{
if (nextstep(currentid, desired))
if (root->right)
{
currentid = 2*currentid+1;
root = root->right;
}
else
return NULL;
else
if (root->left)
{
currentid = 2*currentid;
root = root->left;
}
else
return NULL;
}
return root;
}
void traverse(tree *root)
{
int counter; counter = 1; /* main loop counter */
/* next = maximum id of next child; if we pass this, we're done */
int next; next = 1;
tree *current;
while (next >= counter)
{
current = seek(root, counter);
if (current)
{
if (current->left || current->right)
next = 2*counter+1;
/* printing to show we've been here */
printf("%i\n", current->value);
}
counter++;
}
}
int main()
{
tree *root1 =
maketree(1, maketree(2, maketree(3, NULL, NULL),
maketree(4, NULL, NULL)),
maketree(5, maketree(6, NULL, NULL),
maketree(7, NULL, NULL)));
tree *root2 =
maketree(1, maketree(2, maketree(3,
maketree(4, NULL, NULL), NULL), NULL), NULL);
tree *root3 =
maketree(1, NULL, maketree(2, NULL, maketree(3, NULL,
maketree(4, NULL, NULL))));
printf("doing root1:\n");
traverse(root1);
printf("\ndoing root2:\n");
traverse(root2);
printf("\ndoing root3:\n");
traverse(root3);
}
我为代码质量道歉 - 这主要是一个概念证明。此外,该算法的运行时间并不理想,因为它做了很多工作来弥补无法维护任何状态信息的问题。从好的方面来说,这确实符合 O(1) 空间算法的要求,可以以任何顺序访问树的元素,而无需子链接到父链接或修改树的结构。 p>