二叉树遍历的三种方法
递归 | 简单 | 时间O(n) | 空间O(n) |
非递归+栈 | 中等 | 时间O(n) | 空间O(n) |
非递归、不用栈 | 中等 | 时间O(n) | 空间O(1) |
伪代码实现--近C++代码
方法一:递归
1 Inorder-Tree-Walk(x) 2 if(x != NULL) 3 Inorder-Tree-Walk(x->left) 4 print x->key 5 Inorder-Tree-Walk(x->right)
方法二:非递归+栈
1 Inorder-Tree-Walk(x) 2 stack<Node*> s //定义装二叉树节点指针的栈容器 3 while(x != NULL || !s.empty()) 4 if(x != NULL) 5 s.push(x) 6 x = x->left 7 else if(!s.empty()) 8 x = s.top() 9 s.pop() 10 print x->key 11 x = x->right
方法三:非递归,不用栈(Morris 中序遍历[1])
思想:利用线索二叉树的思想,无右子树的节点的空闲的右指针,指向此节点中序遍历的后继,以此记录在此节点之后要访问的点。要使得无右子树的节点指向后继,不直接计算其后继,而是迭代地计算所经过的节点的前驱(左子树的最右节点)。
1 Inorder-Tree-Walk(x) 2 Node *cur = x, *prev; 3 while(cur != NULL) 4 if(cur->left != NULL) //如果左子树不为空,不应打印当前节点,需在当前节点的前驱节点记录下此节点(此节点记录在其前驱的右指针,其前驱右指针原本一定为空),方便下次找到这个节点。 5 //试图找到当前节点的中序序列前驱,前驱在左子树的最右点 6 prev = cur->left //先使prev为左子树根节点 7 while(prev->right != NULL && prev->right != cur) 8 prev = prev->right 9 //第一次找到左子树最右点时,最右点的右指针肯定为空,但是此时将标记其右指针指向该节点的后继(cur 节点),那么第二次到cur时,将再次执行找到其前驱。如果在寻找其前驱的过程中发现:有一个节点指向cur。说明这是第二次访问这个节点(cur的前驱已经打印输出,该cur输出了),这也是为什么找前驱的while里需要判断“prev->right != cur”。 10 if(prev->right == NULL) //第一次找到cur的前驱prev 11 prev->right = cur //标记其右指针指向cur,因为cur还没有输出,在输出prev后,输出cur 12 else // 第二次找到cur的前驱,此时prev->right == cur。第二次到达cur节点,一定是从其前驱的右指针过来的,说明其前驱已经输出打印完毕,该打印cur了 13 print cur->key 14 cur = cur->right //往右走,其左子树和当前节点已经遍历完成 15 else //cur->left == NULL, 如果左子树为空,打印输出当前节点 16 print cur->key 17 cur = cur->right //转向右节点,继续循环 参考资料:[1] http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html Accessed at 2015/4/11 [2] 《算法导论》中文版 原书第三版 机械工业出版社
时间: 2024-10-07 03:46:00