深度优先搜索非递归实现

127 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

题目分析

给你二叉树的根节点 root ,返回它节点值的访问顺序。 示例:

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy81MzgyNDQ4LTIxZmZhYjJkNjU4Y2JjODIucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXB8aW1hZ2VWaWV3Mi8yL3cvNjYwL2Zvcm1hdC93ZWJw.png
//树和节点的定义
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

前序遍历

栈操作示例

20200917185509394.png

思路分析

  1. 前序遍历中节点访问顺序是:根节点--左孩子--右孩子
  2. 此示例中,当根节点A不为空时,访问当前值记录到结果数组中,再依次访问根节点的左孩子B和右孩子C
  3. 在访问根节点的左孩子B时,将左孩子也作为根节点,将此节点值记录到结果数组中,再依次访问此节点B的左孩子和右孩子,依次类推
  • 例如此题中先将1记录到res结果数组,再访问1的左孩子2和右孩子3
  • 此时访问左孩子2,此时2作为根节点,将2记录到结果数组中,再访问2的左孩子4和右孩子null
  • 此时访问左孩子4,此时4作为根节点,将4记录到结果数组中,再访问4的左孩子null和右孩子6
  • 由于左孩子为空,所以此时访问右孩子6,将6记录到结果数组中,再将6作为根节点,访问6的左孩子null和右孩子7
  • 到节点7时,将7记录到结果数组中,但是7的左孩子和右孩子都是null,所以1节点的左子树访问结束,此时访问右子树,节点1的右孩子是3,此时将3记录到结果数组中,再访问3的左孩子和右孩子,直至结束

代码实现

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        //当根节点为空时,返回空
        if (root==nullptr) return {};
        //深度遍历需定义栈,便于返回上一节点
        //为什么使用栈呢???因为栈是后进先出,当访问节点依次回退时,也是先回退到后访问的节点
        stack<TreeNode*> stk;
        //定义vector容器,依次记录访问的节点的值
        vector<int> res;
        //首先将根节点入栈
        stk.emplace(root);
        //当栈不为空时,对栈里面的元素进行访问
        while(!stk.empty()){
            //定义一个节点类型的指针,指向栈顶,记录下栈顶元素
            TreeNode* node=stk.top();
            //将栈顶元素出栈
            stk.pop();
            //访问栈顶元素的左右子节点和根节点,由于入栈是先入的后访问,所以先序遍历入栈顺序是右子节点、左子节点、根节点
            if(node->right) stk.emplace(node->right);
            if(node->left) stk.emplace(node->left);
            res.emplace_back(node->val);
        }
        return res;
    }
};

中序遍历

栈操作示例

思路分析

  1. 中序遍历中节点访问顺序是:左孩子--根节点--右孩子
  2. 此示例中,当根节点A不为空时,访问该节点的左孩子B,当B的左孩子为空时,输出B,此时B便作为根节点,再访问B的右孩子c
  3. 当c节点不为空时,再访问c的左孩子,直至左孩子为空,依次重复以上操作
  • 此题中先访问1,1的左孩子2不空
  • 访问左孩子2,2的左孩子4不空
  • 访问左孩子4,此时4的左孩子为空,则将4作为根节点输出到结果数组,再访问4的右孩子6
  • 6不空,将6作为根节点访问其左孩子null,此时将6加入结果数组
  • 访问6的右孩子7,右孩子7不空,但是7的左孩子空,此时将7作为根节点输出到结果数组中,因为7的右孩子也是空,所以此时节点2的左子树访问完毕,此时再将2作为根节点加入结果数组
  • 2没有右孩子,2的父节点--1的左子树访问完毕,将根节点1加入结果数组,再依次访问节点1的右子树,依次类推

代码实现

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> sta;
        vector<int> res;
        if(root==nullptr) return {};
        //先将node指向根节点,此时栈还是空
        TreeNode* node=root;
        //当栈不为空或者第一个根节点不为空时
        while(!sta.empty()||node!=nullptr){
        while(node){
            //当前节点不为空时,将节点入栈
            sta.push(node);
            //访问当前节点的左孩子,直到左孩子为空时,跳出循环,左孩子访问结束,返回上一层父节点,访问根节点,在依次对右子树进行左孩子访问,重复此操作
            node=node->left;
        }
        //通过抛出栈顶元素向上访问
        node=sta.top();
        sta.pop();
        res.push_back(node->val);
        //节点指向根节点的右孩子,依次进行以上循环(访问左孩子,根节点,右孩子)
        node=node->right;
        }
        return res;
    }
    };

后序遍历

栈操作示例

20200917185541365.png

思路分析

思路同上

代码实现

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> sta;
        vector<int> res;
        TreeNode* node=root;
        //记录访问的上一个节点
        TreeNode* prev=nullptr;
        if(node==nullptr) return  {};
        while(!sta.empty()||node!=nullptr){
            //先依次访问左孩子
            while(node){
                sta.push(node);
                node=node->left;
            }
            //此时node是空,往上访问
            node=sta.top();
            sta.pop();
            //此时访问右子树,有两种情况,右孩子为空和右不为空,此时是右孩子不为空,并且没有被访问过
            if(node -> right && node -> right != prev){
                sta.emplace(node);
                node = node -> right;
            }
            //
            else{
                res.emplace_back(node -> val);
                prev = node;
                node = nullptr;
            }
        }
        return  res;
    }
};

重点

清楚栈操作