Morris 遍历
在前序遍历二叉树时,在访问完根节点左子树的的最右的叶节点后应该访问的是根节点的右子树,
而根节点的一个指针域指示它的右孩子,在普通的遍历无法做到这一关键点,所以我们在遍历树时,我们知道叶节点无左,右孩子,所以,将叶节点的右孩子指向这个跟节点即可。
为符合前序遍历的定义,当根节点的左孩子为空时,考虑p1向右移动(即{p1 = p1->right},将p1移动到根节点的右孩子)。
注意前序遍历应当符合先记录根节点的数据域。
何时需要断链
注意在非最右的叶节点也会出现右子为空的的情况,那我们需要考虑在访问左侧叶节点后,我们的遍历应该是哪移动?同样的也应该进行回溯至上一个层级后向右访问,这里的情况,我们可以理解它是通解里的特殊解,即根节点的左孩子的右子树为空。
因此在p1我们需要对p2的右节点进行检查,当p2的右节点出现回指向p1时,则说明建成了回溯链接后,p1通过该链接,已回溯至目标根节点,需要对右子树进行遍历(注意当左子树为空时,也向右移动,那么我们可以作简化处理)
code
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* ans = (int*)malloc(sizeof(int) * 2000);
*returnSize = 0;
struct TreeNode* p1 = root, * p2 = NULL;
while (p1 != NULL) {
p2 = p1->left;
if (p2) {
while (p2->right != NULL && p2->right != p1) {
p2 = p2->right;
}
if (p2->right == NULL) {
ans[(*returnSize)++] = p1->val;//1
p2->right = p1;
p1 = p1->left;
continue;
}else {
p2->right = NULL;
}
}
else {
ans[(*returnSize)++] = p1->val;
}
p1 = p1->right;
//中序遍历ans[(*returnSize)++] = p1->val;中序遍历需略去1处输出输出
}
return ans;
}
前序遍历与中序遍历类似,仅需把输出放置在循环后即可。考虑上图满二叉树,
根据后序遍历顺序为C,D, B,G,F,E,A
我们只对P1存在左子树和数据域考虑(因为左子树为空时,无数据)第一次回溯的p1到B,那么C为p2遍历,第二次回溯p1到A,D,B为p2遍历的倒序,G为第三次遍历,最后A,E,F并无p2访问,我们特解出即可。(注意p2经向右移动,处于其他位置,我们应该使用p赋值即为p1->left)
后A,E,F并无p2访问,我们特解出即可。(注意p2经向右移动,处于其他位置,我们应该使用p赋值即为p1->left)