我在接受亚马逊采访时得到了这个问题。 我被要求执行树的深度优先遍历,而不使用递归或堆栈。我可以为每个节点使用父指针,作为结构的一部分,但除此之外别无其他。(例如,“访问”变量“或任何东西)。 请建议我一个算法。
答案 0 :(得分:6)
父指针实际上就是你所需要的。诀窍是在遍历树时使用树。
丑陋的伪代码:
cur = treeroot;
while (1) { // Get to bottom of tree
if (cur.hasLeft) {
cur = left;
} else if (cur.hasRight) {
cur = right;
} else {
break;
}
// cur is now the bottom node
while (1) {
doStuff(cur); // output cur, perform op on it, whatever
if (!cur.hasParent) { // Done with traversal
break;
}
prev = cur; // So we can tell which way we came up to the parent.
cur = cur.parent;
if (cur.hasLeft && cur.left == prev) { // Delete left child; it's done
cur.hasLeft = false;
} else if (cur.hasRight && cur.right == prev) { // Delete right child; it's done
// Note: "else" not desirable if a node should not be able to have the same child in two spots
cur.hasRight = false;
}
if (cur.hasLeft) { // Go all the way to the bottom of the left child
cur = cur.left;
while (1) {
if (cur.hasLeft) {
cur = cur.left;
} else if (cur.hasRight) {
cur = cur.right;
} else {
break;
}
}
} else if (cur.hasRight) { // Go all the way to the bottom of the right child
cur = cur.right;
while (1) {
if (cur.hasLeft) {
cur = cur.left;
} else if (cur.hasRight) {
cur = cur.right;
} else {
break;
}
}
}
}
答案 1 :(得分:1)
对于'hacky'解决方案,您可以使用指针通常是4字节对齐的事实(即最后两位为0),并将这两位用作访问标记。
答案 2 :(得分:1)
这是我对二叉树的提议。语言是C#。它是一个包含整数的binarTree类的私有方法
private Node DFS(int value)
{
Node current = this.root;
if(current.data == value) return current;
while(true)
{
//go down-left as far as possible
while(current.leftChild != null)
{
current = current.leftChild;
if(current.data == value) return current;
}
//leftChild is null, but maybe I can go right from here
while(current.leftChild == null && current.rightChild != null)
{
current = current.rightChild;
if(current.data == value) return current;
}
if(current.leftChild == null && current.rightChild == null)
{
// Ok, I got to a leaf. Now I have to get back to the last "crossroads"
// I went down-left from, but there was also down-right option
while(current.parent != null &&
(current == current.parent.rightChild ||
current.parent.rightChild == null))
{
current = current.parent;
}
if(current.parent == null) return null;
// Ok If I'm here, that means I found the crossroads mentioned before
// I'll go down-right once and then I should try down-left again
current = current.parent.rightChild;
if(current.data == value) return current;
}
}
}
如果它不是二叉树,那么事情当然会变得更复杂,但逻辑是相似的。到每个级别的第一个可能的孩子的叶子。一旦你到达一片叶子,你就会上去。每当你抬头看看父母时,你都会检查你应该来自的孩子是否是父母列表中的最后一个。如果没有,你带下一个孩子再次下来。如果是,您上去查看以下父母。如果你回到根目录,你搜索了整棵树。
修改强>
好的搜索和遍历是不同的事情,我的不好。以下是一些经过修改的遍历代码
序:
public void preorderTraversal()
{
Node current = this.root;
Console.WriteLine(" item: {0} ", current.data);
while(true)
{
while(current.leftChild != null)
{
current = current.leftChild;
Console.WriteLine(" item: {0} ", current.data);
}
while(current.leftChild == null && current.rightChild != null)
{
current = current.rightChild;
Console.WriteLine(" item: {0} ", current.data);
}
if(current.leftChild == null && current.rightChild == null)
{
while(current.parent != null &&
(current == current.parent.rightChild ||
current.parent.rightChild == null))
{
current = current.parent;
}
if(current.parent == null)
{
return;
}
else
{
current = current.parent.rightChild;
Console.WriteLine(" item: {0} ", current.data);
}
}
}
}
序:
public void inorderTraversal()
{
Node current = this.root;
while(true)
{
while(current.leftChild != null)
{
current = current.leftChild;
}
Console.WriteLine(" item: {0} ", current.data);
while(current.leftChild == null && current.rightChild != null)
{
current = current.rightChild;
Console.WriteLine(" item: {0} ", current.data);
}
if(current.leftChild == null && current.rightChild == null)
{
while(current.parent != null &&
(current == current.parent.rightChild ||
current.parent.rightChild == null))
{
current = current.parent;
if(current.rightChild == null)
{
Console.WriteLine(" item: {0} ", current.data);
}
}
if(current.parent == null)
{
return;
}
else
{
Console.WriteLine(" item: {0} ", current.parent.data);
current = current.parent.rightChild;
}
}
}
}
后序:
public void postorderTraversal()
{
Node current = this.root;
while(true)
{
while(true)
{
if(current.leftChild != null)
{
current = current.leftChild;
}
else if(current.rightChild != null)
{
current = current.rightChild;
}
else
{
break;
}
}
while(current.parent != null &&
(current == current.parent.rightChild ||
current.parent.rightChild == null))
{
Console.WriteLine(" item: {0} ", current.data);
current = current.parent;
}
Console.WriteLine(" item: {0} ", current.data);
if(current.parent == null)
{
return;
}
else
{
current = current.parent.rightChild;
}
}
}
答案 3 :(得分:0)
如果您有父指针,那么您可以在没有堆栈的情况下展开树。放松期间唯一的另一个问题是“我是否需要拜访另一个孩子?”如果您刚从左侧孩子或右侧孩子返回(或概括为N个孩子),可以通过比较指针值来解决这个问题。
修改强>
伪代码(未经测试):
p_last = NULL;
p = p_head;
descend = true;
while (NULL != p)
{
p_tmp = p;
if (descend)
{
// ... Node processing here...
if (0 == p->num_children)
{
// No children, so unwind
p = p_parent;
descend = false;
}
else
{
// Visit first child
p = p->child[0];
}
}
else
{
// Find the child we just visited
for (i = 0; i < p->num_children; i++)
{
if (p_last == p->child[i])
{
break;
}
}
if (i == num_children-1)
{
// Processed last child, so unwind
p = p_parent;
}
else
{
// Visit next child
p = p->p_child[i+1];
descend = true;
}
}
p_last = p_tmp;
}
答案 4 :(得分:0)
这显示了我是如何用C语言进行的。它演示了预订和后序遍历,并且针对每个节点的0..N子进行了推广。
struct node {
struct node *parent;
struct node *child[NCHILD];
};
void dfs(struct node *head, void (*visit_preorder)(struct node *n), void (*visit_postorder)(struct node *n))
{
struct node *current = head;
struct node *prev = head;
while (current)
{
int i;
/* preorder traversal */
if (prev == current->parent)
visit_preorder(current);
/* Find first child to visit */
for (i = NCHILD; i > 0; i--)
{
if (prev == current->child[i - 1])
break;
}
while (i < NCHILD && current->child[i] == NULL)
i++;
prev = current;
if (i < NCHILD)
{
/* Descend to current->child[i] */
current = current->child[i];
}
else
{
/* Postorder traversal */
visit_postorder(current);
/* Ascend back to parent */
current = current->parent;
}
}
}