力扣:对于有关深度优先探索的二叉树题

简介: 力扣:对于有关深度优先探索的二叉树题

前言

数据结构中的二叉树,是由n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点。


这是一种典型的非线性结构,所以对于这种结构的遍历,通常有两种方式,即广度优先探索和深度优先探索,而本章主要讲的就是这个深度优先探索,听起来挺高大上,其实就是所谓的递归

递归思想

递归就是在运行的过程中调用自己。斐波那契数列就是典型的例子;即这样一个数列:1,1,2,3,5,8,13,21…从第三个数开始,每个数是前两个数之和。

那么我们想求第n项的值是多少就可以利用递归思想来解决问题。

int f(int n){
//结束条件
  if(n<=2){
  return 1;
  }
  //关系式
  return f(n-1)+f(n-2);
  }

通过判断终止条件,先不断传递函数,再通过return一个一个进行归还到最初的函数,得到最终结果。

所以要用递归,需要弄好三要素:

1、先判断能否用递归,即需不需要传递函数再通过返回的方式来解决问题。

2、如果可以,需要列出终止条件

3、最后找出递归关系式即可。


例题

NO.1

先说一个二叉树的遍历例题

前序遍历:按照访问根节点——左子树——右子树的方式遍历这棵树。在访问左子树或者右子树的时候,左子树或者右子树又可以充当根结点这个角色,有自己的左子树和右子树,所以通过递归思想来完成遍历。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
void preorder(struct TreeNode* root,int* res,int* resSize)
 {
  //终止条件
     if(root == NULL)
    {
        return ;
    }
    //递归关系式
    res[(*resSize)++]=root->val;
    preorder(root->left,res,resSize);
    preorder(root->right,res,resSize);
 }
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    int* res=(int*)malloc(sizeof(int)*200);
    *returnSize=0;
    preorder(root,res,returnSize);
    return res;
}

如果上面看得比较复杂(由于力扣接口返回问题),那么通过打印的方式来演示

void BinaryTreePreOrder(BinaryTree* root)
{
  //终止条件
    if(root==NULL)
    {
        return ;
    }
    printf("%c ",root->val);
    //递归关系式
    BinaryTreePreOrder(root->left);
    BinaryTreePreOrder(root->right);
}


这是二叉树的深度优先探索的遍历方式,通过这种思路,来解决下面一些问题。

NO.2

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int maxDepth(struct TreeNode* root){
   //终止条件
    if(root==NULL)
    {
        return 0;
    }
    //递归探身加对比长度,加上本身1;
    return fmax(maxDepth(root->right),maxDepth(root->left))+1;
}


解题思路

   对于递归思想,我们要从简单的看起,如图所示,我们一眼能看出深度为3,
我们先只考虑3这个根节点,可以先想一下终止条件是什么,即什么时候停止深
度计算。接着想左子树小于右子树,怎么只想要右子树而已。通过思考,我们
知道遇到空就停止,图所示左子树深度为1,右子树为2,那么可以通过比较来
长度来选择右子树,最后再加上根节点本身1即可。简单的考虑清楚了,一般来
说就解决%90的问题了,剩下的  就是一些特殊情况,没有考虑周到。


NO.3

解题思路

  对于该题目,一眼上去就是两边是镜像对称的,那么如果一指针在遍历,通
  过镜像也有一指针在遍历,两个指针遍历相同的话,不就轴对称了吗。
  所以我们可以创建两个指针来同时遍历。但又发现原函数不允许含有两个参
  bool isSymmetric(struct TreeNode* root){
return FisSymmetric(root->left,root->right);
}
那我们就自己重新创立一个函数来递归遍历。
当然需要注意:左边指针遍历时,右边指针遍历是相反对称的。


代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
 bool FisSymmetric(struct TreeNode* L,struct TreeNode* R)
 {
     //终止成功条件
     if(L==NULL&&R==NULL)
     {
         return true;
     }
     //判断条件(都不为空)
     if(L!=NULL&&R!=NULL)
     {
         //失败条件
         if(L->val!=R->val)
         {
            return false;
         }
         else
         {
             return FisSymmetric(L->left,R->right)&&FisSymmetric(L->right,R->left);
         }
     }
     //判断条件(一边为空)
     return false;
 }
bool isSymmetric(struct TreeNode* root){
return FisSymmetric(root->left,root->right);
}


通过代码可以看出,当判断出null或者镜像的两个数不相等时,其实就成功or失败了。所以我们就将这个结果传递给最初函数即可。直接return+递归函数即可。

return FisSymmetric(L->left,R->right)&&FisSymmetric(L->right,R->left);


NO.4

解题思路

  这道题一看好像与上一道题一样,但要考虑清楚,这是翻转,不能使用两个
  指针来解决(可能就只有我一开始这样做吧,嘻嘻),通过仔细观察,这道题就
  是通过左右子树交换完成的,


 对于越多层来说,就是想通过最近根节点进行交换,再通过更大的根节点
 来交换,所以我们可以通过递归方法进行交换。


代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
*/
struct TreeNode* invertTree(struct TreeNode* root){
   //停止条件
    if(root==NULL)
    {
        return NULL;
    }
    //递归左子树和右子树,实行交换
     struct TreeNode* left=invertTree(root->left);
     struct TreeNode* right=invertTree(root->right);
     root->left=right;
     root->right=left;
   //返回
  return root;

NO.5

解题思路

  如果你对于前面几道题都理解透彻的话,那么这道题是非常容易的。根据题目
  很容易知道,叶子节点就是终点,也就是结束的最后一个,那么我们只需要判
  断每条路径之和是否与targenum匹配即可。当然,由于是判断子节点,所以
  需要先判断根节点是否为空。


代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool hasPathSum(struct TreeNode* root, int targetSum){
   //特殊条件
    if(root==NULL)
    {
        return false;
    }
    //终止条件
   if(root->left==NULL&&root->right==NULL)
   {
       return targetSum==root->val;
   }
   //递归探深
   return hasPathSum(root->left,targetSum-root->val)||hasPathSum(root->right,targetSum-root->val);
}


由于这里每个都要进行匹配,所以用或者来进行递归。

总结

总之,递归要想明白,要传递什么,return回什么,这是最关键的。终止条件要放在函数的前头,确认好关系式。在头脑中要有递归的思想类型图(就是开头的第二张照片),而增强递归的理解,最好的方式就是多刷题,了解不同类型的递归方式。

最后,这些都是我做题过程中的理解,如果你有不同见解思路,欢迎下方评论,谢谢大家的观看。

相关文章
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
63 6
|
2月前
【LeetCode 31】104.二叉树的最大深度
【LeetCode 31】104.二叉树的最大深度
26 2
|
2月前
【LeetCode 29】226.反转二叉树
【LeetCode 29】226.反转二叉树
20 2
|
2月前
【LeetCode 28】102.二叉树的层序遍历
【LeetCode 28】102.二叉树的层序遍历
18 2
|
2月前
【LeetCode 43】236.二叉树的最近公共祖先
【LeetCode 43】236.二叉树的最近公共祖先
21 0
|
2月前
【LeetCode 38】617.合并二叉树
【LeetCode 38】617.合并二叉树
15 0
|
2月前
【LeetCode 37】106.从中序与后序遍历构造二叉树
【LeetCode 37】106.从中序与后序遍历构造二叉树
23 0
|
2月前
【LeetCode 34】257.二叉树的所有路径
【LeetCode 34】257.二叉树的所有路径
22 0
|
2月前
【LeetCode 32】111.二叉树的最小深度
【LeetCode 32】111.二叉树的最小深度
19 0
|
4月前
|
存储 算法
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
文章深入探讨了二叉树的层序遍历方法,并展示了如何通过队列实现层序遍历的算法逻辑,同时指出掌握层序遍历技巧可以帮助解决LeetCode上的多道相关题目。
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题