代码随想录算法训练营第14天|【二叉树01】递归遍历+迭代遍历+ 统一迭代


前言

满二叉树和完全二叉树:

C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn

二叉树可以链式存储,也可以顺序存储。

那么链式存储方式就用指针, 顺序存储的方式就是用数组。


一、递归遍历

题目:
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
思路:

  1. 确定递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

代码:

/**
 * 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) {}
 * };
 */
class Solution {
public:
    void traversal(TreeNode* cur, vector<int>& vec){
        if (cur == NULL) return;
        vec.push_back(cur->val);// 中
        traversal(cur->left, vec);// 左
        traversal(cur->right, vec);// 右
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

二、迭代法遍历

Preorder Traversal

思路:
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。

为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
Initialize a stack, and push the root node in the stack.
As long as the stack is not empty, repeat the following steps:

  1. Pop the top element of the stack, and add its value to the result vector.
  2. If the current node has a right child, push it into the stack(because a stack is a LIFO data structure, we first push the right child, so the left child will be processed first.)
  3. If the current node has a left child. push it into the stack.
    In this way, we can traverse the binary tree in preorder without recursion.

代码:

/**
 * 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) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while(!st.empty()) {
            TreeNode* node = st.top();// visit the root
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);// Pushing right child into stack
            if (node->left) st.push(node->left);// Pushing left child into stack
        }
        return result;
    }
};

Inorder Traversal

思路:

在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
这是因为前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!

  1. 初始化一个空栈和一个指向树根的指针cur。

  2. 进入一个循环,只要cur不为NULL或者栈不为空就继续循环。这确保了所有节点都被处理,并且如果树不是完全二叉树,函数也不会过早地终止。

  3. 如果cur不为NULL,则将其压入栈中,并将cur设置为其左孩子。这个过程是在尝试遍历到当前子树的最左侧。

  4. 如果cur为NULL,则取出栈顶元素(这是最近没有左子树或其左子树已经被遍历过的节点),将其值添加到结果向量中,并将cur设置为其右孩子。

  5. 最后,返回结果向量。

Code:

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

统一迭代法

思路:
无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

代码:
中序遍历:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();//  将该节点弹出,避免重复操作
                if (node->right) st.push(node->right);// 添加右节点
                st.push(node);// 中
                st.push(NULL);// 空节点
                if (node->left) st.push(node->left);// 左
            } else {
                st.pop();// 删去空节点
                node = st.top();// 
                st.pop();
                result.push_back(node->val);// 输出
            }
        }
        return result;
    }
}; 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值