对于二叉树的遍历,最简单的有递归遍历,另外有比较常见的用栈来实现非递归遍历。今天讲的题目是一个特殊的二叉树,且不允许改变二叉树,实现 O(1) 空间复杂度的前序遍历,二叉树如下:
这个二叉树比较特殊,多了一些蓝色的指针,指向父结点。所以我们可以不用栈来实现回溯了,可以做到空间复杂度为 O(1)。
这是我碰到的一个面试题,当时面试官给我的提示是:从哪里来到哪里去。 我琢磨了一下,想到了其实要知道一个结点是否该回溯了,不需要知道左子树右子树所有结点是否已遍历完,只需要知道左子结点和右子结点是否已遍历完就可以了。
既然空间复杂度为 O(1),那就只能额外申请固定个数的变量(和结点个数无关),在这里只需要申请一个 Node* pre 变量就可以了,思路:
(1) 当 pre 结点是父结点,或空(根结点),代表该继续往下遍历,此时应打印当前结点,并将 pre 设置为当前结点,当前结点设置为左子结点(存在的话),否则是右子结点,若没有孩子,则应该回溯。
(2) 当 pre 结点是左子结点,此时应该继续访问右子树,应该讲 pre 结点设置为当前结点,当前结点设置为右孩子(存在的话)
(3) 当 pre 结点是右孩子,应该回溯,将 pre 设置为当前结点,当前结点设置为父结点。
当时有了这个思路之后,就自己在脑子里过了一遍二叉树的遍历,遍历的结束点为根结点,条件为左右子树都遍历完,且无父结点。
我的代码如下,感觉 while 循环的结束条件不太简洁,但一时又没想到其他的写法。希望各位读者有好的方法的话麻烦留言告知,互相学习!
void PrintTree(Node* pRoot)
{
if (!pRoot)
{
return;
}
Node* p = pRoot;
Node* pre = nullptr;
while (!((p->pLeft && !p->pRight && pre == p->pLeft && p->pParent == nullptr) || (p->pRight && pre == p->pRight && p->pParent == nullptr)))
{
if (pre == p->pParent || pre == nullptr)
{
cout << p->value << std::endl;
if (p->pLeft)
{
pre = p;
p = p->pLeft;
}
else if (p->pRight)
{
pre = p;
p = p->pRight;
}
else
{
pre = p;
p = p->pParent;
}
}
else if (pre == p->pLeft && p->pRight)
{
pre = p;
p = p->pRight;
}
else
{
pre = p;
p = p->pParent;
}
}
}
由于本人水平有限,代码中可能有错误,若发现错误请帮忙指出,但思路应该是没问题的。