Java 使用常量空间和O(n)运行时编写二叉搜索树的非递归遍历
这不是作业,这是面试问题。 这里的问题是算法应该是常量空间。 我对如何在没有堆栈的情况下实现这一点一无所知,我会用堆栈发布我所写的内容,但无论如何都不相关 以下是我尝试过的:我尝试进行一次预顺序遍历,到达了最左边的节点,但我被卡在那里了。我不知道如何在没有堆栈/父指针的情况下“递归”备份 任何帮助都将不胜感激Java 使用常量空间和O(n)运行时编写二叉搜索树的非递归遍历,java,binary-tree,traversal,tree-traversal,Java,Binary Tree,Traversal,Tree Traversal,这不是作业,这是面试问题。 这里的问题是算法应该是常量空间。 我对如何在没有堆栈的情况下实现这一点一无所知,我会用堆栈发布我所写的内容,但无论如何都不相关 以下是我尝试过的:我尝试进行一次预顺序遍历,到达了最左边的节点,但我被卡在那里了。我不知道如何在没有堆栈/父指针的情况下“递归”备份 任何帮助都将不胜感激 (我将其标记为Java,因为这是我可以轻松使用的,但它显然与语言无关。)它是一个二元搜索树,因此每个节点都可以通过一系列右/左决策来访问。将该序列描述为0/1,从最低有效位到最高有效位。函
(我将其标记为Java,因为这是我可以轻松使用的,但它显然与语言无关。)它是一个二元搜索树,因此每个节点都可以通过一系列右/左决策来访问。将该序列描述为0/1,从最低有效位到最高有效位。函数f(0)表示“通过右分支找到的节点,直到找到一个叶;f(1)表示向左一个,其余的向右;f(2)——也就是说,二进制010——表示向右一个,然后向左一个,然后向右一个,直到找到一个叶。从n=0开始迭代f(n),直到找到每个叶。效率不高(因为每次都必须从树的顶部开始)但内存和线性时间不变。如果使用的是基于向下指针的树,并且没有父指针或其他内存,则不可能在恒定空间中遍历
如果你的二叉树是在一个数组中,而不是在一个基于指针的对象结构中,这是可能的。但是你可以直接访问任何节点。这是一种欺骗;-)我没有完全想清楚,但我认为这是可能的,只要你愿意在这个过程中弄乱你的二叉树 每个节点都有2个指针,所以它可以用来表示一个双链表。假设您从根到根前进。Left=Current。Now Root。Left指针是无用的,所以将其指定为Current.Right并继续到Current.Left。当您到达最左边的子节点时,您将拥有一个链表,其中一些节点上挂着树。现在迭代ov呃,就是说,在你走的时候,对你遇到的每一棵树都重复这个过程 编辑:仔细考虑。以下是按顺序打印的算法:
void traverse (Node root) {
traverse (root.left, root);
}
void traverse (Node current, Node parent) {
while (current != null) {
if (parent != null) {
parent.left = current.right;
current.right = parent;
}
if (current.left != null) {
parent = current;
current = current.left;
} else {
print(current);
current = current.right;
parent = null;
}
}
}
这是一个搜索树,因此您可以随时获取下一个键/条目 您需要smth(我没有测试代码,但它非常简单)
问题的标题没有提到节点中缺少“父”指针。虽然BST不一定需要它,但许多二叉树实现都有父指针。 类节点{ 节点*左; 节点*右; 节点*父节点; 数据; }) 在这种情况下,在纸上成像一个树的图表,用铅笔围绕着树,从边缘的两侧上下画(向下时,你在边缘的左侧,向上时,你在边缘的右侧)。基本上,有4种状态:
遍历(节点*节点)
{
枚举方向{SW,NE,SE,NW};
方向=西南;
while(节点)
{
//首先,如果我在路上的话,输出节点数据:
如果(方向==SE或方向==SW){
流外数据;
}
开关(方向){
案例SW:
如果(节点->左){
//如果我们有一个左撇子,继续向左走
节点=节点->左;
}
else if(节点->右侧){
//我们没有左撇子,向右走
节点=节点->右侧;
方向=SE;
}
否则{
//没有孩子,上去。
方向=NE;
}
打破
案例SE:
如果(节点->左){
方向=西南;
节点=节点->左;
}
else if(节点->右侧){
节点=节点->右侧;
}
否则{
方向=NW;
}
打破
案例NE:
如果(节点->右侧){
//掉头回到右节点
节点=节点->右侧;
方向=SE;
}
否则{
节点=节点->父节点;
}
打破
案例NW:
节点=节点->父节点;
打破
}
}
}
Morris Inorder树遍历如何?它基于线程树的概念,它修改树,但在完成后将其还原
林奇:
不使用任何额外的空间。我们可以遍历二叉树而无需修改树本身(前提是节点具有父指针)。这可以在常量空间中完成。我发现了这个有用的链接
接受的答案需要进行以下更改,否则它将不会打印BST只有一个节点的树
if (current == NULL && root != NULL)
print(root);
这是iluxa的简短版本。 它以完全相同的顺序运行完全相同的节点操作和打印步骤,但以简化的方式[1]:
void traverse (Node n) {
while (n) {
Node next = n.left;
if (next) {
n.left = next.right;
next.right = n;
n = next;
} else {
print(n);
n = n.right;
}
}
}
[1]
此外,它甚至在树根节点没有剩余子节点的情况下也能工作。以上iluxa回答的一个小特例
if(current== null)
{
current = root;
parent = current.Right;
if(parent != null)
{
current.Right = parent.Left;
parent.Left = current;
}
}
如果没有堆栈或父指针,我也看不到这样做的方法。Mh,每个节点都有对其父节点的引用吗?@ChrisWue-nope,这很简单:)你确定他要求的是预顺序遍历而不是任何其他遍历吗?任何遍历都可以,我只是从预顺序遍历开始,因为这就是为什么我最清楚。你需要
log(n)
位来完成这项工作,所以我怀疑这是否符合条件
if (current == NULL && root != NULL)
print(root);
void traverse (Node n) {
while (n) {
Node next = n.left;
if (next) {
n.left = next.right;
next.right = n;
n = next;
} else {
print(n);
n = n.right;
}
}
}
if(current== null)
{
current = root;
parent = current.Right;
if(parent != null)
{
current.Right = parent.Left;
parent.Left = current;
}
}