08 二叉树的下一个节点
题目描述:
给定一个二叉树和其中的一个节点,请找出中序遍历顺序的下一个节点并且返回。注意,树中的节点不仅包含左右子节点(指向左右子节点的指针),同时包含指向父节点的指针。
测试用例:
1)普通二叉树(完全二叉树、不完全二叉树)
2)特殊二叉树(所有节点都没有右/左子节点的二叉树,只有一个节点的二叉树,空树:根节点指针为nullptr)
3)不同位置的节点的下一个节点(下一个节点为当前节点的右子节点、右子树的最左节点、父节点、跨层的父节点;当前节点没有下一个节点)
解题思路:
建议:在解题时,画出具体的二叉树结构图、通过具体的例子找出中序遍历下一个节点的规律,设计可行的方法。
分三种情况讨论:
1)如果一个节点有右子树,那么它的下一个节点就是它的右子树的最左节点。即从右子节点出发一直沿着左子节点的指针寻找。如图中b(h)与a(f)。
2)该节点没有右子树,且该节点是其父节点的左节点,下一个节点是它的父节点。
3)该节点没有右子树,且该节点是其父节点的右节点时:可以沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左子节点的节点。如果这样的节点存在,那么这个节点的父节点就是要找的下一个节点
4)初始化要找的下一个节点时,设为空指针。没有找到时,直接返回该空指针。
代码:
1)
/* struct TreeLinkNode { int val; struct TreeLinkNode *left; //此处的struct对调用有影响么?没有 struct TreeLinkNode *right; struct TreeLinkNode *next; TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) { } }; */ class Solution { public: //pNode: 给定的节点 不要修改其值 TreeLinkNode* GetNext(TreeLinkNode* pNode) { if( pNode == NULL ) //nullptr return NULL; //TreeLinkNode* nextNode = NULL ; TreeLinkNode* nextNode = nullptr ; //TreeLinkNode* nextNode = new TreeLinkNode(); if (pNode->right != NULL){ //error : 右子树非空,右子树的root节点则是指定节点pNode中序的下一个节点。 //nextNode = pNode->right; //右子树非空,右子树的最左节点则是指定节点pNode中序的下一个节点。 TreeLinkNode* rightTree = pNode->right ; while(rightTree->left!=NULL){ rightTree = rightTree->left ; } //把找到的左节点赋给返回值 nextNode = rightTree; }else if (pNode->next!= nullptr){ //父节点非空--根节点没有父节点 //右子树为空时,根据父节点判断指定节点pNode是左子树还是右子树 if(pNode == pNode->next->left){ //是左节点,则在中序遍历中父节点是该节点的下一个节点 nextNode = pNode->next ; }else{ //是右节点 //父节点是左节点,取父节点的父节点。父节点是右节点,对父节点的父节点重新判断。。。 //一直找到父节点是左节点的节点。 TreeLinkNode* parantNode = pNode->next ; TreeLinkNode* currentNode = pNode ; while(parantNode!=nullptr && currentNode == parantNode->right){ currentNode = parantNode; parantNode = parantNode->next ; } nextNode = currentNode->next ; } } return nextNode; } };
注意:
「1」注意中英文字符
「2」line22-28,当右子树非空时,下一个节点应该时右子树的最左节点,而不是根节点。
「3」line32一定要判断父节点非空才可以使用父节点 ->next,因为并不是所有的节点都有父节点,如树的根节点。
「4」在程序运行中,不要修改输入参数pNode,创建新的变量。如 currentNode 、parantNode 等。
2)思路同上面是一致的,只是在第三种情况时,代码更简洁。
/* 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 == nullptr){ return nullptr; } TreeLinkNode* next = nullptr; //先右子节点的左子节点遍历 if(pNode->right != nullptr){ TreeLinkNode* rightNode = pNode->right; while(rightNode->left != nullptr){ rightNode = rightNode->left; } next = rightNode; } //向父结点遍历 else if(pNode->next != nullptr){ //一定要判断父节点是否非空 TreeLinkNode* parentNode = pNode->next; TreeLinkNode* currentNode = pNode; //当前节点是左节点时,不进入while循环。右节点进入循环 //一定要判断父节点是否非空 while(parentNode != nullptr && currentNode == parentNode->right){ currentNode = parentNode; parentNode = parentNode->next; } next = parentNode; } return next; } };
基础知识:
[1] NULL是0,nullptr是空指针void
https://blog.csdn.net/jays_/article/details/82586699
[2] 对变量的命名,要有意义。如父节点 parentNode,不要随便写pp、pc这种名字。