leetcode(94) 二叉树的中序遍历——莫里斯遍历(非传统递归方法)

题目描述
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1

2
/
3

输出: [1,3,2]

思路

二叉树的遍历用递归方法解十分简单。但是计算机是非常不愿意做递归运算的,因为有时候他占用空间是非常恐怖的,数字大了之后会非正常退出。
这里提一下先序遍历的非递归解法:使用栈

  • a. 遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;
  • b. 当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;
  • c. 当所有节点访问完即最后访问的树节点为空且栈空时,停止。
void PreOrderTraversal(BinTree * BT)
{
    BinTree * T = BT;
    stack<BinTree> S ;    //创建并初始化堆栈S
    while(T || ! IsEmpty(S))
    {
        while(T)        //一直向左并将沿途节点访问(打印)后压入堆栈 
        {
            printf("%d\n", T->Data);
            S.push(S, T);
            T = T->Left;
        }
        if (! IsEmpty(S))
        {
            T = S.top();    //节点弹出堆栈
            S.pop();
            T = T->Right;  //转向右子树
        }
    }
}
 
//更简单的非递归前序遍历
void preorderTraversalNew(TreeNode *root, vector<int> &path)
{
    stack< pair<TreeNode *, bool> > s;
    s.push(make_pair(root, false));
    bool visited;
    while(!s.empty())
    {
        root = s.top().first;
        visited = s.top().second;
        s.pop();
        if(root == NULL)
            continue;
        if(visited)
        {
            path.push_back(root->val);
        }
        else
        {
        //甚至不同的遍历只需要改动这里的顺序就可以
            s.push(make_pair(root->right, false));
            s.push(make_pair(root->left, false));
            s.push(make_pair(root, true));
        }
    }
}

中序遍历的非递归解法:莫里斯遍历
本方法中,我们使用一种新的数据结构:线索二叉树。方法如下:

Step 1: 将当前节点current初始化为根节点
Step 2: While current不为空,

若current没有左子节点
   a. 将current添加到输出
   b. 进入右子树,亦即, current = current.right
否则
   a. 在current的左子树中,令current成为最右侧节点的右子节点
   b. 进入左子树,亦即,current = current.left

举例而言:

      1
    /   \
   2     3
  / \   /
 4   5 6

首先,1 是根节点,所以将 current 初始化为 1。1 有左子节点 2,current 的左子树是

     2
    / \
   4   5

在此左子树中最右侧的节点是 5,于是将 current(1) 作为 5 的右子节点。令 current = cuurent.left (current = 2)。 现在二叉树的形状为:

     2
    / \
   4   5
        \
         1
          \
           3
          /
         6

对于 current(2),其左子节点为4,我们可以继续上述过程

    4
     \
      2
       \
        5
         \
          1
           \
            3
           /
          6

由于 4 没有左子节点,添加 4 为输出,接着依次添加 2, 5, 1, 3 。节点 3 有左子节点 6,故重复以上过程。 最终的结果是 [4,2,5,1,6,3]。


/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        TreeNode * curr = root;
        while(curr != NULL){
            if(curr->left == NULL){
                ret.push_back(curr->val);
                curr = curr-> right;
            }
            else{
                TreeNode * pre = curr -> left;
                while(pre->right != NULL){
                    pre = pre -> right;
                }
                pre -> right = curr;
                TreeNode * tmp = curr;
                curr = curr -> left;
                tmp -> left = NULL;
            }
        }
        return ret;
    }
};

后序遍历的非递归:
对于一个节点而言,要实现访问顺序为左儿子-右儿子-根节点,可以利用后进先出的栈,在节点不为空的前提下,依次将根节点,右儿子,左儿子压栈。
故我们需要按照根节点-右儿子-左儿子的顺序遍历树,而我们已经知道先序遍历的顺序是根节点-左儿子-右儿子,故只需将先序遍历的左右调换并把访问方式打印改为压入另一个栈即可。最后一起打印栈中的元素。

/**
    * 后序遍历 非递归
    * 双栈法
    * @param root
    */
   public static void postOrder2(Node root) {
       Stack<Node> stack = new Stack<Node>();
       Stack<Node> output = new Stack<Node>();
       Node node = root;
       while (node != null || !stack.isEmpty()) {
           if (node != null) {
               stack.push(node);
               output.push(node);
               node = node.rightNode;
           } else {
               node = stack.pop();
               node = node.leftNode;
           }
       }

       while (output.size() > 0) {
           Node n = output.pop();
           System.out.print(n.data + "\t");
       }
   }
 
//更简单的非递归后序遍历
void postorderTraversalNew(TreeNode *root, vector<int> &path)
{
    stack< pair<TreeNode *, bool> > s;
    s.push(make_pair(root, false));
    bool visited;
    while(!s.empty())
    {
        root = s.top().first;
        visited = s.top().second;
        s.pop();
        if(root == NULL)
            continue;
        if(visited)
        {
            path.push_back(root->val);
        }
        else
        {
            s.push(make_pair(root, true));
            s.push(make_pair(root->right, false));
            s.push(make_pair(root->left, false));
        }
    }
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值