Day13 二叉树:107.二叉树的层序遍历 226.翻转二叉树 101.对称二叉树

120 阅读6分钟

107.二叉树的层序遍历

难度指数:😀😐😕

二叉树的层序遍历,相当于图论中的广度优先搜索(BFS), 而二叉树的递归遍历,相当于图论中的深度优先搜索(DFS)。

图论中的深搜和广搜可以应用在不同的数据结构中,

在二叉树本身的这个结构中,无法做到层序遍历的,需要借助队列, 借助一个队列来保存每一层里面遍历过的元素。

下面看如何通过队列来层序遍历二叉树: (图论中的广度优先搜索也是依赖队列来实现。)

动画:

13.01.gif

代码思路:

不能一上来就把root加入到队列,需要先判断root是否为空。如果root不为空,将第一个节点加入到队列。

 queue<TreeNode*> que;
 ​
 if (root != NULL) que.push(root);  //根节点不为空,就将第一个节点加入到队列
 ​
 //遍历的过程
 while (!que.empty()) {  //终止条件:队列里没有元素  (说明二叉树里没有元素可以添加到队列里,程序终止)
     size = que.size();  //size记录的就是当前层的节点个数  (作用是控制队列里面弹出的节点数量,不控制就不知道队列里应该弹出多少节点)
     vector<int> vec;  //放一层的元素  (如:第二层的元素4、7 放到一维数组里)
     while (size--) {  //遍历当前层的元素     (当前层只有一个元素)
         node = que.front();  //node获取到队列里的第一个元素
         que.pop();  //弹出元素
         vec.push_back(node->val);  //用一个数组把这个结果记录一下    (相当于这个6放到一维数组里)
         
         if (node->left) {
             que.push(node->left);
         }
         if (node->right) {
             que.push(node->right);
         }
     }
     result.push_back(vec);
 }
 return result;
 ​

一维数组在 while循环 结束之后还要放到二维数组里。

🐳最终结果要用一个二维数组result来保存,即最终的结果集

此时,一层一层节点的遍历已经遍历完了,我们通过size来控制本层的元素,

AC代码: (核心代码模式)

 /**
  * 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<vector<int>> levelOrder(TreeNode* root) {
         queue<TreeNode*> que;
         if (root != NULL) {  //若根节点不为空
             que.push(root);  //push进队列
         }
         vector<vector<int>> result;  //二维数组,存最终的结果集
         //遍历的过程
         while (!que.empty()) {  //终止条件:队列里没有元素  (说明二叉树里没有元素可以添加到队列,程序终止)
             int size = que.size();
             vector<int> vec;
             while(size--) {
                 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);
         }
         return result;
     }
 };

226.翻转二叉树

难度指数:😀🙂

其实就是两两交换节点的左右孩子,

⚠️注意:交换的是指针,而不是数值!

13.02.png

大部分人在写这道题时,有很多点是没想清楚的,

比如:这道题应该用哪种遍历方式,有两大类遍历方式:1️⃣递归; 2️⃣非递归

递归里面还包含前、中、后序遍历,你要用哪种顺序来遍历?

递归法:

想清楚用哪种遍历方式?

用前序、后序遍历是最合适的;用中序遍历代码有点绕。

写递归一定会要用到递归三部曲

动画:

13.03.gif

代码思路:

 1️⃣确定递归函数的参数和返回值
 //题目要求返回翻转之后新的二叉树的根节点
 TreeNode* invertTree(root) {
 
 }
 2️⃣确定终止条件
 //碰到空节点,终止遍历
 if (root == NULL) return root;
 3️⃣确定单层递归的逻辑
 //交换root的左孩子和右孩子
 swap(root->left, root->right);  //中
 invertTree(root->left);  //左
 invertTree(root->right);  //右

⚠️注意:这里root可能会给你带来歧义,这里的root是力扣上给好的定义,

在本题中,root是指遍历的每一个节点,你操作的变量不一定总是二叉树的根节点。 (这个变量最好不要命名为root)

前序、后序遍历写法:

AC代码: (核心代码模式)

 /**
  * 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:
     TreeNode* invertTree(TreeNode* root) {
         if (root == nullptr) {
             return root;
         }
         swap(root->left, root->right);  //中
         invertTree(root->left);  //左
         invertTree(root->right);  //右
         return root;
     }
 };

Q:swap() 可不可以放在最下面,即 左右中 (后序遍历)?

A:可以的。但是左中右(中序遍历)就不行!

按照上面这个套路写中序遍历是不行的!

会有以下问题:原先二叉树的左子树被处理了两次,变回原样;导致了右子树没有被处理。

中序遍历写法(不推荐):

需要改动,但代码比较绕!

AC代码: (核心代码模式)

 /**
  * 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:
     TreeNode* invertTree(TreeNode* root) {
         if (root == nullptr) {
             return root;
         }
         invertTree(root->left);  //左
         swap(root->left, root->right);  //中
         invertTree(root->left);  //右
         return root;
     }
 };

非递归法(迭代)以及层序遍历依然可以把这道题通过。

未完待续……

101.对称二叉树

难度指数:😀😐

我们在判断一棵二叉树是不是对称二叉树的时候,

本质上是判断根节点的左子树和右子树是否可以相互翻转

比较的是两个子树的里侧外侧的元素是否相等。如图所示:

13.04.png

很多人都不清楚自己用的什么遍历顺序,一顿操作猛如虎,通过了就不管了。

二叉树的题目,确定遍历顺序很重要!决定了对这道题的理解深度。

递归法:

想清楚用哪种遍历方式?

🦄这道题只能选择后序遍历(左右中),因为只有后序遍历才能把底部孩子的信息返回给上一层。

的处理过程中,就将孩子的处理信息向上一层返回了,

代码思路:

传入的左、右节点,如果:

  • 左节点为空,右节点不为空,那么两个二叉树外侧或者内测,比较的两个节点不相同,不相同就一定不可以翻转,return false;
  • 左节点不为空,右节点为空,也应该return false;
  • 左右都为空,可以翻转,因为至少是对称的;
  • 左右都不为空,但是值不相等,return false;
  • 左右都不为空,且值相等,应该继续往下遍历,看看它的左右孩子是否也是相等的(向下一层遍历的过程,涉及到递归三部曲的3️⃣)。
 1️⃣确定递归函数的参数以及返回值
 bool compare(TreeNode* left, right)
 {
     if (left == NULL && right != NULL) {
         return false;
     }
     else if (left != NULL && right == NULL) {
         return false;
     }
     else if (left == NULL && right == NULL) {
         return true;
     }
     else if (left->val != right->val) {
         return false;
     }
 }
 2️⃣确定终止条件
 ​
 3️⃣确定单层递归的逻辑

AC代码: (核心代码模式)

/**
 * 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:
    bool compare(TreeNode* left, TreeNode* right) {
        if (left == NULL && right != NULL) {
            return false;
        }
        else if (left != NULL && right == NULL) {
            return false;
        }
        else if (left == NULL && right == NULL) {
            return true;
        }
        else if (left->val != right->val) {
            return false;
        }
        
        //此时:左右节点不为空,且数值相同的情况
        //需要进行递归,做下一层的判断
        bool outside = compare(left->left, right->right);
        bool inside = compare(left->right, right->left);
        bool isSame = outside && inside;

        return isSame;
    }

    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return false;
        return compare(root->left, root->right);
    }
};