二叉树的遍历(迭代法)

题目类型:Midium

Posted by 谢玄xx on March 28, 2022

问题一:二叉树的前序遍历

解题思路

  • 递归法有手就行,本次来试一下迭代法。

  • 迭代法都需要用到栈结构。利用栈先入后出的特点依次将元素遍历。

代码如下

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if(!root) return res;
        stack<TreeNode*> st;
        st.push(root);//一定记得先把这个根节点压入栈
        while(!st.empty())
        {
            //先把栈内元素存储在一个临时变量temp里,再把它出栈
            TreeNode* temp = st.top();
            st.pop();
            //接下来操作temp就可以得到对应的节点值
            res.push_back(temp->val);
            //先右后左,因为栈先入后出
            if(temp->right)
            {
                st.push(temp->right);
            }
            if(temp->left)
            {
                st.push(temp->left);
            }


        }
        return res;//出来就是中左右,前序遍历完成

    }
};

问题二:二叉树的后序遍历

解题思路

代码如下

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if(!root) return res;
        stack<TreeNode*> st;
        st.push(root);//一定记得先把这个根节点压入栈
        while(!st.empty())
        {
            //先把栈内元素存储在一个变量里,再把它出栈
            TreeNode* temp = st.top();
            st.pop();
            res.push_back(temp->val);
            if(temp->left)
            {
                st.push(temp->left);
            }
            if(temp->right)
            {
                st.push(temp->right);
            }


        }
        //出栈后是中右左。想要是后序遍历(左右中)直接reverse即可。
        reverse(res.begin(), res.end());//注意reverse格式
        return res;
    }
};

问题三:二叉树的中序遍历

解题思路

  • 为什么中序遍历不同呢?因为前序后序都是操作根节点,遍历根节点;而中序遍历是操作根节点,遍历最左子树。如何才能遇到根节点的时不动它呢?答案是先push不pop。详见代码。

代码如下

解法1

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

问题四:二叉树的层序遍历

解题思路

  • 下述思路来自算法集结号。新建一个node结构体用于存放一个节点和当前所在层数。

代码如下

struct node{
    TreeNode *p;
    int depth;
};
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(!root) return res;
        int last = 0;
        vector<int> current;
        queue<node> que; //队列类型为node,存放当前层节点,及时传给current
        que.push((node){root, 0});
        while(!que.empty())
        {
            node temp = que.front();
            que.pop();
            if(temp.depth != last) //如果不在同一层,赶快把current拿出来
            {
                res.push_back(current);
                current.clear();
                last = temp.depth;
            }
            current.push_back(temp.p->val);
            if(temp.p->left)
            {
                que.push((node){temp.p->left, temp.depth + 1});
            }
            if(temp.p->right)
            {
                que.push((node){temp.p->right, temp.depth + 1});
            }

        }
        res.push_back(current);
        return res;

    }
};

解法2

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector <vector <int>> ret;
        if (!root) {
            return ret;
        }

        queue <TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int currentLevelSize = q.size();
            ret.push_back(vector <int> ());
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front(); q.pop();
                ret.back().push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        
        return ret;
    }
};

解法3——来自《代码随想录》卡尔的解法


vector<vector<int>> levelOrder(TreeNode* root) 
{
    queue<TreeNode*> que;
    if (root != NULL) que.push(root);
    vector<vector<int>> result;
    while (!que.empty()) 
    {
        int size = que.size();
        vector<int> vec;
    // 这里一定要使用固定大小的size,不要使用que.size(),因为que.size()是不断变化的
        for (int i = 0; i < size; i++) 
        {
            TreeNode* node = que.front();
            que.pop();
            vec.push_back(node->val);
            if (node->left) que.push(node->left);
            if (node->right) que.push(node->right);
        }
        result.push_back(vec);
    }

补齐LC中缺失的部分——建立二叉树与打印二叉树

代码如下:

TreeNode* build(vector<int>& nums) {
    if (nums.empty() || nums[0] == -1) return nullptr;
    
    // 将数字转换成 TreeNode
    vector<TreeNode*> nodes;
    for (int i = 0; i < nums.size(); i++) {
        TreeNode* cur = nullptr;
        if (nums[i] != -1) cur = new TreeNode(nums[i]);
        nodes.push_back(cur);
    }
    
    // 父子结点关联,i 的左右孩子下标为 2 * i + 1 和 2 * i + 2
    // 由于输入约定是一个满二叉树,所以只要保证最后一个右孩子不越界
    for (int i = 0; 2 * i + 2 < nums.size(); i++) {
        if (!nodes[i]) continue;
        nodes[i]->left = nodes[2 * i + 1];
        nodes[i]->right = nodes[2 * i + 2];
    }
    
    return nodes[0];
}

int main() {
    // 输入序列格式:
    //  1. 满二叉树
    //  2. 按照层序遍历的顺序
    //  3. 空结点用 -1 表示
    vector<int> nums = {4,2,5,1,3,-1,6};
    
    TreeNode* root = build(nums);
    vector<int> ans = inorderTraversal(root);//这个地方调用了上述我们写的函数,前中后层序遍历我们替换掉即可。
    for (auto val: ans) {
        cout << val << " ";
    }
    
    return 0;
}

问题五:二叉树的锯齿形层序遍历

解题思路

  • 所谓锯齿形层序遍历就是多了一个栈st用来存储奇数层节点,其他情况和层序遍历基本相同,只不过要认真一些。

  • 奇数行放到栈中,偶数行放到vector中。最后一行需要单独处理,使用一个判断语句考虑奇偶,再push即可。

代码如下

struct node
{
    TreeNode* p;
    int depth;
};
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(!root) return res;
        vector<int> current;    //存储当前行 (偶数)
        stack<int> st;          //也是存储当前行 (奇数行)
        queue<node> que;        //BFS快乐队列
        que.push((node){root, 0});      //把起点放入队列
        
        int last = 0;           //上一个节点的层深
        while(!que.empty())
        {
            node temp = que.front();
            que.pop();
            while(last != temp.depth)       //判断是不是新的一层。
            {
                while(!st.empty())
                {
                    current.push_back(st.top());    //如果是新的一层,就先判断栈(偶数)是否为空。如果非空,说明此时遍历奇数行,直接把栈中元素放入current
                                                    //如果st内无元素,那它就一定都在current里了。
                    st.pop();
                }
                //此时该行节点都已存储在current中了。
                res.push_back(current);
                current.clear();
                last = temp.depth;
            }
            //至此,完成将上一行节点插入res动作。
            if(temp.depth % 2 != 0)
            {
                st.push(temp.p->val);       //奇数层放到栈中,再由上面的st.empty()判断语句放入current
            }
            else
            {
                current.push_back(temp.p->val);     //偶数层直接放到current
            }
            //判断奇数层还是偶数层
            //下述判断和层序遍历同理
            if(temp.p->left)
            {
                que.push((node){temp.p->left, temp.depth + 1});
            }
            if(temp.p->right)
            {
                que.push((node){temp.p->right, temp.depth + 1});
            }            
        }
        //判断最后一层的奇偶性。先检测栈
        while(!st.empty())
        {
            current.push_back(st.top());
            st.pop();
        }
        res.push_back(current);     //栈为空,就必定都在current里。
        return res;

    }
};