二叉树:理论基础 递归遍历 迭代遍历 统一迭代

理论基础

  • 二叉树的种类
    • 满二叉树:只有度为0和2,且度为0的结点在同一层
    • 完全二叉树:只有最底层可能没填满,且该层结点集中在左侧
    • 二叉搜索树:有序树,左子树所有值 < 根节点值 < 右子树所有值
    • 平衡二叉搜索树:空树或者左右子树高度差绝对值不超过1,且左右子树都是平衡二叉树
      • map、set、multimap、multiset底层都是平衡二叉树
  • 二叉树的存储方式
    • 链式存储:指针
    • 顺序存储:数组,如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2
  • 遍历方式
    • 深度优先遍历(递归,迭代)
      • 前序:中左右
      • 中序:左中右
      • 后序:左右中
    • 广度优先遍历(迭代)
  • 二叉树的定义(链式存储)
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

题目:Leetcode144,145,94

  • 递归遍历

    • 学会递归算法步骤:
      1. 确定递归函数的参数和返回值
      2. 确定终止条件
      3. 确定单层递归的逻辑
    • 思路:当前节点为空时结束本层递归,然后根据遍历方法编写单层递归逻辑
    • 前序遍历
    • class Solution {
      public:
          void travalsal(TreeNode* cur, vector<int>& vec) {  //1.确定参数(当前节点、存储结果的数组),确定返回值(void)
              //2.确定终止条件:当前节点为空,则停止
              if(cur == NULL) return;
              //3.确定单层逻辑
              vec.push_back(cur->val);//中
              travalsal(cur->left, vec);//左
              travalsal(cur->right, vec);//右
          }
          vector<int> preorderTraversal(TreeNode* root) {
              vector<int> res;
              travalsal(root, res);
              return res;
          }
      };
    • 中序遍历
    • class Solution {
      public:
          void travalsal(TreeNode* cur, vector<int>& vec) {  //1.确定参数(当前节点、存储结果的数组),确定返回值(void)
              //2.确定终止条件:当前节点为空,则停止
              if(cur == NULL) return;
              //3.确定单层逻辑
              travalsal(cur->left, vec);//左
              vec.push_back(cur->val);//中
              travalsal(cur->right, vec);//右
          }
          vector<int> inorderTraversal(TreeNode* root) {
              vector<int> res;
              travalsal(root, res);
              return res;
          }
      };
    • 后序遍历
    • class Solution {
      public:
          void travalsal(TreeNode* cur, vector<int>& vec) {  //1.确定参数(当前节点、存储结果的数组),确定返回值(void)
              //2.确定终止条件:当前节点为空,则停止
              if(cur == NULL) return;
              //3.确定单层逻辑
              travalsal(cur->left, vec);//左
              travalsal(cur->right, vec);//右
              vec.push_back(cur->val);//中
          }
          vector<int> postorderTraversal(TreeNode* root) {
              vector<int> res;
              travalsal(root, res);
              return res;
          }
      };

  • 迭代遍历

    • 思路:
      • 对于前序遍历来说,每次先处理中间节点,先将根节点放入栈中,再将右孩子、左孩子入栈(栈的特性)。
      • 而中序遍历时,无法直接复用前序的逻辑,因为在迭代过程中有两个操作,分别是处理:将元素放进result数组中,访问:遍历节点。在前序遍历时,要访问的元素和要处理的元素顺序是一致的,都是中间节点;而中序遍历时,就出现了处理顺序和访问顺序是不一致的情况。因此,使用迭代法进行中序遍历时,需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
      • 后序遍历只需调整前序的代码顺序,变成中右左遍历,最后反转结果数组。
    • 前序遍历
    • class Solution {
      public:
          vector<int> preorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;//遍历节点的栈
              vector<int> res;//存放遍历结果
              if(root == nullptr) return res;
              st.push(root);
              while(!st.empty()) {
                  TreeNode* node = st.top();//中
                  st.pop();
                  res.push_back(node->val);
                  //由于栈先进后出的特性,所以处理时要先(右)后(左)
                  if(node->right) st.push(node->right);//右
                  if(node->left) st.push(node->left);//左
              }
              return res;
          }
      };
    • 中序遍历
    • class Solution {
      public:
          vector<int> inorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;
              vector<int> res;
              TreeNode* cur = root;
              while(cur || !st.empty()) {
                  if(cur) {
                      st.push(cur);
                      cur = cur->left;
                  } else {
                      cur = st.top();
                      st.pop();
                      res.push_back(cur->val);
                      cur = cur->right;
                  }
              }
              return res;
          }
      };
    • 后序遍历
    • class Solution {
      public:
          vector<int> postorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;
              vector<int> res;
              if(root == nullptr) return res;
              st.push(root);
              while(!st.empty()) {
                  //依据栈的特性,while结束时res的遍历结果是(中右左),只需再进行反转
                  TreeNode* node = st.top();//中
                  st.pop();
                  res.push_back(node->val);
                  if(node->left) st.push(node->left);//左
                  if(node->right) st.push(node->right);//右
              }
              //中右左,反转得到后序遍历(左右中)
              reverse(res.begin(), res.end());
              return res;
          }
      };

  • 统一迭代

    • 思路:前面的迭代方法无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况,那就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记(在要处理的节点放入栈之后,紧接着放入一个空指针作为标记)。
    • 前序遍历
    • class Solution {
      public:
          vector<int> preorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;
              vector<int> res;
              if(root == nullptr) return res;
              st.push(root);
              while(!st.empty()) {
                  TreeNode* node = st.top();
                  if(node) {
                      st.pop();
                      if(node->right) st.push(node->right);//右
                      if(node->left) st.push(node->left);//左
                      st.push(node);//中
                      st.push(nullptr);
                  } else {
                      st.pop();
                      node = st.top();
                      st.pop();
                      res.push_back(node->val);
                  }
              }
              return res;
          }
      };
    • 中序遍历
    • class Solution {
      public:
          vector<int> inorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;
              vector<int> res;
              if(root == nullptr) return res;
              st.push(root);
              while(!st.empty()) {
                  TreeNode* node = st.top();
                  if(node) { //若节点不为空
                      st.pop();//弹出,避免重复操作
                      //按照 右中左 的顺序将结点入栈
                      if(node->right) st.push(node->right);//右
                      st.push(node);//中
                      st.push(nullptr);//标记访问,待处理结点后面做标记
                      if(node->left) st.push(node->left);//左
                  } else { //若节点为空,将下一个节点放进结果集
                      st.pop();//弹出空节点
                      node = st.top();//获取要放进结果集的节点
                      st.pop();
                      res.push_back(node->val);
                  }
              }
              return res;
          }
      };
    • 后序遍历
    • class Solution {
      public:
          vector<int> postorderTraversal(TreeNode* root) {
              stack<TreeNode*> st;
              vector<int> res;
              if(root == nullptr) return res;
              st.push(root);
              while(!st.empty()) {
                  TreeNode* node = st.top();
                  if(node) {
                      st.pop();
                      st.push(node);//中
                      st.push(nullptr);
                      if(node->right) st.push(node->right);//右
                      if(node->left) st.push(node->left);//左
                  } else {
                      st.pop();
                      node = st.top();
                      st.pop();
                      res.push_back(node->val);
                  }
              }
              return res;
          }
      };

总结

迭代方法还需要多理解

参考链接

代码随想录:理论基础 递归遍历 迭代遍历 统一迭代

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值