题目:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
看到这道题的第一反应就是写一个中序遍历然后返回需要查找的节点的下一个节点返回,但是这样的查找效率不够高,而且没有用到题目给出的包含指向父节点的指针这个特性。实际上,只需要根据中序遍历的特点就可以写出,因为中序遍历中,如果遍历的是左子节点,下一个就应该是根节点(父节点),而如果遍历的是根节点,则下一个就应该是右子树的最左边的子节点,如果遍历的是右子节点,那么下一个就应该找它的父节点的父节点直至没有父节点。刚开始我是这么考虑的,看了剑指以后才发现事实远没有我想象中的那么简单,真是很难全面考虑呢QAQ
剑指用来分情况讨论是根据一个节点是否有子节点来进行讨论的:
情况一:一个节点有右子树,那么它的下一个就是它的右子树的最左边的节点,即它的右子节点沿着左指针一直找到NULL为止
情况二:
一个节点没有右子树:
2.1 如果这个节点是它的父节点的左子节点,那么它的下一个节点就是它的父节点
2.2 如果这个节点是它的父节点的右子节点,就要沿着它的父节点一直往上找,直到找到一个有资格成为它的父节点的左子节点的节点为止,此时这个左子节点的父节点就是下一个遍历的节点;而如果找到顶了都找不到这样的节点,那么就说明它是遍历的最后一项了。
写代码还算比较容易,毕竟只是普通的if-else而已,但是在处理情况二的时候还是有点晕,看了讨论区的大佬代码才发现2.1和2.2在写代码的时候可以合并,因为都是往父节点去寻找,直到找到一个可以作为左子节点的父节点为止。另外想吐槽一下为什么指向父节点的指针叫做next:)代码如下:
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if (pNode == NULL) {
return NULL;
}
if (pNode->right != NULL) {
TreeLinkNode* right = pNode->right;
while (right->left != NULL) {
right = right->left;
}
return right;
}
else {
while (pNode->next != NULL) {
TreeLinkNode* parent = pNode->next;
if (parent->left == pNode) {
return parent;
}
pNode = pNode->next;
}
return NULL;
}
}
};