面试算法——树的遍历大大大汇总

方法论:

  1. 看题五分钟,不会做,看解析;
  2. 先看中文站,再看国际站;
  3. 选择最优解析;
  4. 回头再来写

面试四步走:

  1. 和面试官,探讨题目限制条件;
  2. 说说可能解,选择最优解;
  3. 码字;
  4. 跑测试用例

关于树,需要了解:

  1. 树节点定义
  2. 遍历算法

推荐一个算法可视化的网页 ,VISUALGO-BST,刚看到的时候感觉很惊艳!!!

二叉树节点定义:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

94. 二叉树的中序遍历

可能解:

  1. 递归算法
  2. 迭代算法
  3. 利用栈模拟递归通用模板;
# 1. 递归算法
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        inorder(root, res);
        return res;    
    }

private:
    void inorder(TreeNode* root, vector<int>& res) {
        if (root == nullptr)    return;
        inorder(root->left, res);
        res.push_back(root->val);
        inorder(root->right, res);
    }
};
# 2. 迭代算法
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> sk;
        TreeNode* cur = root;
        while (cur != NULL || !sk.empty()){
            while (cur != NULL){
                sk.push(cur);
                cur = cur->left;
            }

            TreeNode* tmp = sk.top();
            sk.pop();
            res.push_back(tmp->val);
            cur = tmp->right;
        }
    return res;
    }
};
# 3. 利用栈模拟递归通用模板
class Solution {
public:
    // 写代码,注意顶层设计思想,先写框架,再细节
    // 工作中,业务代码同理
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> sk;
        if (root == nullptr)    return {};
        sk.push(root);

        while (!sk.empty()) {            
            TreeNode* tmp = sk.top();
            sk.pop();
            if (tmp != nullptr) {
                if (tmp->right) sk.push(tmp->right);
                sk.push(tmp);
                sk.push(nullptr);
                if (tmp->left)  sk.push(tmp->left);
            } else {
                res.push_back(sk.top()->val);
                sk.pop();
            }
        }
        return res;
    }
};

144. 二叉树的前序遍历

可能解:

  1. 递归方法
  2. 迭代法
  3. 通用模板
# 2. 迭代法
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> sk;
        sk.push(root);
        while (!sk.empty()){
            TreeNode* tmp = sk.top();
            sk.pop();
            if (tmp == NULL) continue;
            res.push_back(tmp->val);
            sk.push(tmp->right);
            sk.push(tmp->left);
        }
        return res;
    }
};
# 3. 通用模板法
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> sk;
        if(root == nullptr) return {};
        sk.push(root);
        while (!sk.empty()){
            TreeNode* tmp = sk.top();
            sk.pop();

            if (tmp != nullptr){
                // 压栈的时候与前序遍历的方法,逆序, 右--左--根--nullptr
                if (tmp->right) sk.push(tmp->right);
                if (tmp->left) sk.push(tmp->left);
                sk.push(tmp);
                sk.push(nullptr);
            } else {
                res.push_back(sk.top()->val);
                sk.pop();
            }
        }
        return res;
    }
};

145.二叉树的后序遍历

可能解法:

  1. 递归法
  2. 前序遍历,再进行反转:前序:root->left->right,后序:left->right->root,modify前序:root->right->left;
  3. 通用模板
# 2. modify前序遍历反转
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> sk;
        sk.push(root);
        while (!sk.empty()){
            TreeNode* tmp = sk.top();
            sk.pop();
            if (tmp == NULL) continue;
            res.push_back(tmp->val);
            sk.push(tmp->left);
            sk.push(tmp->right);
        }

        reverse(res.begin(), res.end());
        return res;
    }
};
# 3. 通用模板
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==NULL)  return {};
        vector<int> res;
        stack<TreeNode*> sk;
        
        sk.push(root);
        while (!sk.empty()){
            TreeNode* temp = sk.top();
            sk.pop();

            if (temp != NULL){
                sk.push(temp);
                sk.push( NULL );
                if (temp->right ) {sk.push(temp->right);}
                if (temp->left ) {sk.push(temp->left);}
            }
            else {
                res.push_back(sk.top()->val);
                sk.pop();
            }
        }
        return res;
    }
};

N叉树节点定义:

class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};

589.N叉树的前序遍历

可能解:

  1. 递归解法
  2. 迭代法
  3. 通用模板法
# 1.递归解法
class Solution {
public:
    vector<int> preorder(Node* root) {
        vector<int> res;
        pre(root, res);
        return res;      
    }

private:
    void pre(Node* root, vector<int>& res) {
        if (root == nullptr) return;
        res.push_back(root->val);
        for (auto c :  root->children) {
            pre(c, res);
        }
    }
};
# 2.迭代法
class Solution {
public:
    vector<int> preorder(Node* root) {
        vector<int> res;
        stack<Node*> sk;
        sk.push(root);

        while (!sk.empty()) {
            Node* tmp = sk.top();
            sk.pop();
            // 这一行代码如果不要,需要判断root不为nullptr
            if (tmp == nullptr) continue;
             
            res.push_back(tmp->val);
            for (int i = tmp->children.size() - 1; i >= 0; i-- ) {
                sk.push(tmp->children[i]);
            }
        }
        return res;
    }
};

590.N叉树的后序遍历

可能解:

  1. 递归解法
  2. 迭代法
  3. 通用模板法
# 1. 递归解法
class Solution {
public:
    vector<int> postorder(Node* root) {
        vector<int> res;
        post(root, res);
        return res;
    }

private:
    void post(Node* root, vector<int>& res) {
        if (root == nullptr)    return;
        for (auto c : root->children) {
            post(c, res);
        }
        res.push_back(root->val);
    }
};
# 2. 迭代法
class Solution {
public:
    vector<int> postorder(Node* root) {
        vector<int> res;
        stack<Node*> sk;
        sk.push(root);

        while (!sk.empty()) {
            Node* tmp = sk.top();
            sk.pop();
            if (tmp == nullptr) continue;
            for (auto c : tmp->children) {
                sk.push(c);
            }
            res.push_back(tmp->val);
        }
        reverse(res.begin(), res.end());
        return res;
    } 
};

429. N叉树的层序遍历

可能解:

  1. 利用队列queue实现层次遍历
class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        // 1. 需要
        if (root == nullptr) return vector<vector<int>>();
        vector<vector<int>> res;
        queue<Node*> qu;
        qu.push(root);

        while (!qu.empty()) {
            int size = qu.size();
            vector<int> curLevel;
            for (int i = 0; i < size; i++){
                Node* tmp = qu.front();
                qu.pop();
                curLevel.push_back(tmp->val);
                // 2. 这一段直接屏蔽了,qu中间的元素不为nullptr
                for (auto c : tmp->children) {
                    qu.push(c);
                }
            }
            res.push_back(curLevel);
        }
        return res;
    }
};

总结

  1. 关于树遍历的迭代写法,注意以下三种:前序:root --> left --> right, 中序:left–>root–>right,后序:left–>right–>root;
  2. 除了中序遍历,其他迭代方法,根节点入栈或队列,while遍历栈或队列是否为空,循环内判断top或front元素是否为nullptr,剩下的就是遍历顺序了
  3. 中序遍历不把root加入栈中,因为,我们需要先左子树,当节点左结束访问完后,再访问 右节点 的左子树,因此需要一个while上一层的变量。

参考

  1. 极客时间-算法训练营-覃超
  2. leetcode中文站
  3. leetcode国际站(将力扣中文链接,后面的-cn去掉,就是该题的国际站)
  4. Basic C++ iterative solution with detailed explanations. Super easy for beginners.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值