二叉树遍历

1、前序遍历

递归

void PreOrder(TreeNode* root) {
    if (root == nullptr) {
        return;
    }
    int val = root->val; // 访问
    PreOrder(root->left);
    PreOrder(root->right);
}

非递归

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        stack<TreeNode*> st;
        TreeNode *p = root;
        while(!st.empty() || p != nullptr) {
            while(p != nullptr) {
                ret.push_back(p->val);
                st.push(p);
                p = p->left;
            }
            p = st.top();
            st.pop();
            p = p->right;
        }
        return ret;
    }
};

用一个栈保存经过的节点,再出栈往右边走。

2、中序遍历

非递归

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        stack<TreeNode*> st;
        TreeNode* p = root;
        while(!st.empty() || p != nullptr) {
            while(p != nullptr) {
                st.push(p);
                p = p->left;
            }
            p = st.top();
            st.pop();
            ret.push_back(p->val);
            p = p->right;
        }
        return ret;
    }
};

3、后序遍历

非递归

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        TreeNode* p = root;
        TreeNode* pre = nullptr;
        stack<TreeNode*> st;
        while(!st.empty() || p != nullptr) {
            while(p != nullptr) {
                st.emplace(p);
                p = p->left;
            }
            p = st.top();
            st.pop();
            if (p->right == nullptr || p->right == pre) {
                ret.emplace_back(p->val);
                pre = p;
                p = nullptr;
            } else {
                st.emplace(p);
                p = p->right;
            }
        }
        return ret;
    }
};

非递归算法看起来其实都差不多,都是利用栈,先找到最左边的节点,把路上的节点不断保存(放入栈内),到了叶子节点就出栈,然后往右边走一步。

前序在路上就访问节点

中序在出栈后访问节点(左边已经访问过了)

后序有点不一样,要一个pre标记右边是否被访问过,如果访问过,就访问当前节点,并改变pre=p,p=nullptr(p已经出栈并且已经访问完,需要重置)。如果没访问过,当前节点入栈,向右边走。后面的循环出栈时再遇到这个节点就能根据pre判断。

4、层次遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        if (root == nullptr) {
            return ret;
        }
        deque<TreeNode*> a;
        a.push_back(root);
        while(a.size() != 0) {
            vector<int> tmp;
            int sz = a.size();
            for (int i = 0; i < sz; ++i) {
                TreeNode *q = a.front();
                a.pop_front();
                tmp.emplace_back(q->val);
                if (q->left != nullptr) {
                    a.push_back(q->left);
                }
                if (q->right != nullptr) {
                    a.push_back(q->right);
                }
            }
            ret.emplace_back(tmp);
        }
        return ret;
    }
};

主要使用一个队列,每次循环把每层的节点依次入栈,创建一个临时数组tmp用来保存一层的节点。

锯齿形层次遍历:力扣

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        deque<TreeNode*> dq;
        if (root == nullptr) {
            return ret;
        }
        dq.push_back(root);
        int cs = 1;
        while(dq.size() != 0) {
            deque<int> tmp;
            int sz = dq.size();
            for (int i = 0; i < sz; ++i) {
                TreeNode* q = dq.front();
                dq.pop_front();
                if (cs % 2 == 1) {
                    tmp.push_back(q->val);
                } else {
                    tmp.push_front(q->val);
                }
                if (q->left != nullptr) {
                    dq.push_back(q->left);
                }
                if (q->right != nullptr) {
                    dq.push_back(q->right);
                }
            }
            cs = cs + 1;
            ret.push_back(vector<int>{tmp.begin(), tmp.end()});
        }
        return ret;
    }
};

判断层数然后正着放还是倒着放即可。

5、Morris

前序遍历

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        TreeNode* pre = nullptr;
        TreeNode* p = root;
        while(p != nullptr) {
            if (p->left != nullptr) {
                pre = p->left;
                while(pre->right != nullptr && pre->right != p) {
                    pre = pre->right;
                }

                if (pre->right == nullptr) {
                    ret.push_back(p->val);
                    pre->right = p;
                    p = p->left;
                } else {
                    pre->right = nullptr;
                    p = p->right;
                }
            } else {
                ret.push_back(p->val);
                p = p->right;
            }
        }
        return ret;
    }
};

中序遍历

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        TreeNode* pre = nullptr;
        TreeNode* p = root;
        while(p != nullptr) {
            if (p->left != nullptr) {
                pre = p->left;
                while(pre->right != nullptr && pre->right != p) {
                    pre = pre->right;
                }
                if (pre->right == nullptr) {
                    pre->right = p;
                    p = p->left;
                } else {
                    ret.push_back(p->val);
                    pre->right = nullptr;
                    p = p->right;
                }
            } else {
                ret.push_back(p->val);
                p = p->right;
            }
        }
        return ret;
    }
};

后序遍历

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) {
            return ret;
        }
        TreeNode* p = root;
        TreeNode* pre = nullptr;
        while(p != nullptr) {
            if (p->right != nullptr) {
                pre = p->right;
                while(pre->left != nullptr && pre->left != p) {
                    pre = pre->left;
                }
                if (pre->left == nullptr) {
                    ret.push_back(p->val);
                    pre->left = p;
                    p = p->right;
                } else {
                    pre->left = nullptr;
                    p = p->left;
                }
            } else {
                ret.push_back(p->val);
                p = p->left;
            }
        }
        reverse(ret.begin(), ret.end());
        return ret;
    }
};

Morris就是找到左子树的最右节点并把最右节点和当前节点连接起来,这样就做到了遍历时保存的作用。

具体就是每次循环先找到左子树中的最右节点,根据其右孩子为nullptr或者p,判断是否已经连接,如果是前序遍历,未连接时直接进行访问,并让p向左走,pre连接p,如果是中序遍历,已连接时(代表左边已经访问过)直接进行访问,并重置pre,p向右走。如果不存在左子树,那均直接访问,并向右走。

后序遍历因为是左、右、中的顺序,可以倒过来中、右、左,把前序遍历修改最后再翻转答案即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值