0
点赞
收藏
分享

微信扫一扫

剑指---树篇(C++)

minute_5 2022-01-22 阅读 34

文章目录

树篇

第一题: 二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。

数据范围:节点的数量满足 0≤n≤100 ,节点上的值满足 0≤val≤100
进阶:空间复杂度 O(1) ,时间复杂度 O(n)

假如输入的用例为{1,2,3,4,5,#,6,#,#,7},那么如下图:
在这里插入图片描述


题目思路: 传入某节点,调用该方法,返回的应该是以传入节点为根节点的树的深度,而树的深度,肯定和左右子树深度有关,所以进入这个方法后,就包含了左右子树的深度(而要得到左右子树的深度,肯定又是以左右子节点为根节点,再次调用该方法深度获取的,因此此时进行递归),并且还有由一个左右深度比较的过程,然后取较大值,这个较大值就是该节点左右子树深度较深的值,以该值+1作为返回值,就是该节点的深度。

看下图:
在这里插入图片描述


代码部分:

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot) {
    if (pRoot==NULL){
        return 0;
    }
        else{
        //声明a、b两个变量分别记录得到的左右子树深度。
            int a=TreeDepth(pRoot->left);
            int b=TreeDepth(pRoot->right);
            //进行比较,较大值+1作为返回值
            return a>b?(a+1):(b+1);
        }
    }
};

第二题: 二叉树的镜像

描述
操作给定的二叉树,将其变换为源二叉树的镜像。
数据范围:二叉树的节点数0≤n≤1000 , 二叉树每个节点的值0≤val≤1000
要求: 空间复杂度 O(n) 。本题也有原地操作,即空间复杂度 O(1) 的解法,时间复杂度 O(n).
在这里插入图片描述


题目思路: 如下图所示,我们需要做的是,从上自下依次更换每一层的左右子树的顺序。至此,可以采用递归的方法。在这里插入图片描述


代码部分:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pRoot TreeNode类 
     * @return TreeNode类
     */
    TreeNode* Mirror(TreeNode* pRoot) {
        if(pRoot==NULL){
            return pRoot;
        }
        //交换左右子树的信息
        TreeNode* temp=pRoot->left;
        pRoot->left=pRoot->right;
        pRoot->right=temp;
        //然后继续对左右子树的子树进行交换,在此直接递归即可
        Mirror(pRoot->left);
        Mirror(pRoot->right);
        //最后返回根节点的信息
        return pRoot;
    }
};

第三题: 从上往下打印二叉树

描述
不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入{8,6,10,#,#,2,1},如以下图中的示例二叉树,则依次打印8,6,10,2,1(空节点不打印,跳过),请你将打印的结果存放到一个数组里面,返回。
数据范围:
0<=节点总数<=1000
-1000<=节点值<=1000
在这里插入图片描述
题目思路:
题目给出一颗二叉树,我们需要按照从上到下,从左到右的顺序遍历节点。也就是从上到下一层一层的遍历。
对于BFS(广度优先算法),我们可以利用一个队列来存储我们需要访问的节点。当我们访问一个节点的时候,将这个节点的值放入要输出的数组里面,然后这节点出队。然后我们先判断左边的节点是否为空,若不为空,那么将这个节点指针入队,对右边的节点的判断也是一样。一直执行下去直到最后队列为空。另外记得特判根节点为空的情况
补充!
广度优先搜索算法(Breadth-First-Search,缩写为 BFS),是一种利用队列实现的搜索算法。简单来说,其搜索过程和 “湖面丢进一块石头激起层层涟漪” 类似。

深度优先搜索算法(Depth-First-Search,缩写为 DFS),是一种利用递归实现的搜索算法。简单来说,其搜索过程和 “不撞南墙不回头” 类似。

BFS 的重点在于队列,而 DFS 的重点在于递归。这是它们的本质区别。


代码部分:
也可以结合画图进一步理解整个搜索过程

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> ret;  //存放结果的数组
        if (!root) return ret;
        queue<TreeNode*> q;  //定义一个队列
        q.push(root);        //将根节点传入队列
        while (!q.empty()) {
            TreeNode *node = q.front();  //将根节点的值赋给*node
            ret.push_back(node->val);
            q.pop();  //弹出根节点
            
            if (node->left) q.push(node->left);  //如果左子树不为空,将左子树入队
            if (node->right) q.push(node->right);//如果右子树不为空,将右子树入队
        }
        return ret;
    }
};


第四题: 对称的二叉树

描述
给定一棵二叉树,判断其是否是自身的镜像(即:是否对称)
例如: 下面这棵二叉树是对称的

在这里插入图片描述
题目思路:
当且仅当两棵子树符合如下要求时,满足 “对称” 要求:

  • 两棵子树根节点值相同;

两颗子树的左右子树分别对称,包括:

  • a 树的左子树与 b 树的右子树相应位置的值相等
  • a 树的右子树与 b 树的左子树相应位置的值相等

具体的,我们可以设计一个递归函数 check ,传入待检测的两颗子树的头结点 a 和 b(对于本题都传入 root 即可),在单次查询中有如下显而易见的判断子树是否 “对称” 的 Base Case:

  • a 和 b 均为空节点:符合 “对称” 要求;
  • a 和 b 其中一个节点为空,不符合 “对称” 要求;
  • a 和 b 值不相等,不符合 “对称” 要求;

其他情况,我们则要分别检查 a 和 b 的左右节点是否 “对称” ,即递归调用 check(a.left, b.right) 和 check(a.right, b.left)。


代码部分:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot) {
    return check(pRoot,pRoot);//进入check函数从根节点进行检测
    }
    bool check(TreeNode* a,TreeNode* b){  //一左一右
        if((a==NULL)&&(b==NULL))return true;   //如果两者都为空,那么是镜像的
        if((a==NULL)||(b==NULL))return false;  //一个为空一个不为空,非镜像
        if((a->val)!=(b->val))return false;  //两者值不相等,非镜像
        return check(a->left,b->right)&&check(a->right,b->left);  //继续递归检查左右子树
    }
};

第五题: 二叉树中和为某一值的路径(一)

描述
给定一个二叉树root和一个值 sum ,判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n在这里插入图片描述
题目思路:

定义递归函数功能: 判断是否当前结点到root为止的路径和为sum

  • 每次递归都减掉经过的结点的值
  • 当前结点是叶子结点,并且sum刚好为0,表明该路径和刚好为sum
  • 返回时,保证能递归每个结点,且只需要有一条路径满足即可

图示:在这里插入图片描述


代码部分:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    bool hasPathSum(TreeNode* root, int sum) {
      if (root == NULL)
            return false;
        // 每次递归都减掉经过的结点的值
        sum -= root->val;
        // 当前结点是叶子结点,并且sum刚好为0,表明该路径和刚好为sum
        if (sum == 0 && root->left == NULL && root->right == NULL)
            return true;
        // 保证能递归每个结点,且只需要有一条路径满足即可
        // 本质上是DFS深度优先遍历,只是由递归实现
        return hasPathSum(root->left, sum) || hasPathSum(root->right, sum);
    }
};

第六题: 判断是不是平衡二叉树

描述
输入一棵节点数为 n 的二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1 (即等于1也可以),并且左右两个子树都是一棵平衡二叉树。
样例解释:在这里插入图片描述
题目思路:
解法一:自顶向下(前序遍历)
一棵二叉树为「平衡二叉树」的条件为:该树为空树,或者其左右子树的高度差最大为1。因此,判断一棵二叉树是否平衡需要求其子树高度,并比较左右子树高度差。

因此此题的解题步骤如下:

  • 设计「求二叉树高度」的函数getHeight(root),作用是求以root为根结点的二叉树高度;
  • 遍历原二叉树,对其每个结点调用getHeight(root)函数,若存在某左右子树的高度差大于等于2,则是不平衡的;否则是平衡二叉树。
    求取二叉树高度的思路如图所示。在这里插入图片描述

图中,红色箭头方向为递归方向,绿色箭头方向为回溯方向。

  • 当结点为空时,其高度为0;
  • 当结点无左孩子、右孩子时,其高度为1;
  • 否则,该结点的高度为max(左子树高度,右子树高度) + 1。(加1是因为要加上该结点本身)

代码部分:

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* root) {
        if (!root)  //为空,则是平衡二叉树
            return true;
        // 分别求左子树和右子树高度
        int leftHeight = getHeight(root->left), rightHeight = getHeight(root->right);
        // 若子树高度之差大于1,返回false
        if (abs(leftHeight - rightHeight) > 1) 
            return false;
        // 递归地判断左右子树是否平衡
        return IsBalanced_Solution(root->left) && IsBalanced_Solution(root->right);
    }
    // 求子树高度的函数
    int getHeight(TreeNode* root) {
        if (!root) 
            return 0;
        if (!root->left && !root->right) 
            return 1; // 叶子结点
        return 1 + max(getHeight(root->left), getHeight(root->right));
    }
};



解法二:自底向上(后序遍历)
解法一在「计算二叉树高度」时遍历了树的结点,在「判断是否平衡」时又遍历了一次树的结点,因此产生了重复计算。
说具体点就是:从1开始判断,用getHeight函数求深度时,要遍历2、4、5,而在判断以2为根的树是否为平衡二叉树时也要遍历4、5。因此,这种方法存在着许多重复的计算。
针对解法一的优化是采用「自底向上」的解题思路:

  • 从最底的叶子结点开始,计算该结点的高度。若该结点不平衡,则直接返回-1,不用继续计算其他结点高度,否则返回其高度;
  • 若自底向上的过程中一直都是平衡的,则最终的树是平衡的。此方法每个结点(最坏时)仅会遍历一次,不会有重复计算。在这里插入图片描述

代码部分:

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if (!pRoot) 
            return true; 
        return getHeight(pRoot) >= 0; // 若结果不是-1,则是平衡的
    }
    int getHeight(TreeNode* root) {
        if (!root) 
            return 0;   //递归到叶子节点时子树为空,用于回溯
        // 左子树高度
        int left = getHeight(root->left); 
        if (left == -1) 
            return -1; // 若左子树高度为-1,则不平衡
        int right = getHeight(root->right); // 右子树高度
        if (right == -1 || abs(left - right) >= 2) 
            return -1; // 若右子树高度为-1,或左右子树高度之差大于1,则不平衡
        return 1 + max(left, right); // 返回该结点高度
    }
};
举报

相关推荐

0 条评论