二叉树算法题

本文深入探讨了二叉树的多种经典算法,包括深度计算、翻转、判断相同树、对称性检测、遍历、层序遍历、最近公共祖先及二叉树与双向链表转化等。通过实例解析,提供了清晰的代码实现和解题思路。

1.二叉树深度

leetcode

int max(int a ,int b)
{
    return a > b ? a : b;
}

int maxDepth(struct TreeNode* root)
{
   if(root == NULL)
   return 0;
   int ret = max(maxDepth(root->left),maxDepth(root->right));
   return ret + 1;
}

解题思想:二叉树深度 == max(左子树深度,右子树深度)+1;


2.翻转二叉树

leetcode

class Solution {
public:
    TreeNode* invertTree(TreeNode* root)
    {
       if(root == NULL)
       return NULL;
       TreeNode* tmp = root->left;
       root->left = root->right;
       root->right = tmp;

       invertTree(root->left);
       invertTree(root->right);

       return root;
    }
};

解题思路:不断交换左右子树,直到为空


3.相同的树

leetcode

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) 
    {
       if(q == NULL && p == NULL)
       return true;
       if(q == NULL || p == NULL)
       return false;
       if(q->val != p->val)
       return false;

       return isSameTree(q->left,p->left) && isSameTree(q->right,p->right);
    }
};

解题思路:问题拆分
两棵树不同的情况
1.结构不同
即p == NULL || q == NULL;
2.结点值不同
即p->val != q->val;
然后递归子树即可;


4.对称二叉树

leetcode

class Solution {
public:
    bool child(TreeNode* q,TreeNode* p)
    {
        if(q == nullptr && p == nullptr)
        return true;
        if(q == nullptr || p == nullptr)
        return false;
        if(q->val != p->val)
        return false;
        return child(q->right,p->left) && child(q->left,p->right);
    }
    bool isSymmetric(TreeNode* root) 
    {
       if(root == nullptr)
       return true;
       return child(root->left,root->right);
    }
};

解题思路
化为子问题解决,最后递归求出左右子树是否对称;

5、二叉树的遍历

遍历

6.二叉树的层序遍历

leetcode

先不考虑按层输出,写一个二叉树的层序遍历;

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        vector<vector<int>> vv;
       queue<TreeNode*> q;
       if(root)
       q.push(root);
       while(!q.empty())
       {
           TreeNode* front = q.front();
           q.pop();
           cout << front->val;
           if(front->left)
           q.push(front->left);
           if(front->right)
           q.push(front->right);

       } 
       return vv;
    }
};

在此基础上,加入分层输出模块即可

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        vector<vector<int>> vv;
       queue<TreeNode*> q;
       if(root)
       q.push(root);
       while(!q.empty())
       {
           int queue_size = q.size();
           vector<int> v;
           for(int i = 0; i < queue_size ;++i)
           {
              TreeNode* front = q.front();
              q.pop();
              v.push_back(front->val);
              if(front->left)
              q.push(front->left);
              if(front->right)
              q.push(front->right);
           }
           vv.push_back(v);
       } 
       return vv;  
    }
};

解题思路
可以用一个循环控制每层输出的节点数量,并插入数组,而循环的控制变量如何选取?

观察发现,队列的元素个数是不断增加的,上一次循环增加的节点数,恰好是第二次循环需要输出的元素个数,再将元素插入数组即可;

7.二叉树的层序遍历II

leetcode

解题思路:在前一题的基础上加上逆置即可,使用库函数reverse;

8.二叉树的最近公共祖

leetcode

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)return NULL;        
        if(root == p||root == q)return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left && right)return root;
        return left ? left : right; // 只有一个非空则返回该指针,两个都为空则返回空指针
    }
};

解题思路

1.如果俩个节点分别处于左右子树,则当前根节点就是最近公共祖先

2.如果当处于左子树,则返回递归到左子树的值,右子树亦然;

8.2搜索树的最近祖先

leetcode

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)return NULL;
        if(p->val > root->val && q->val > root->val) 
            return lowestCommonAncestor(root->right, p, q);
        else if(p->val < root->val && q->val < root->val) 
            return lowestCommonAncestor(root->left, p, q);
        else return root;
    }
};

解题思路:搜索树左子树都比自己小,右子树都比自己大;

利用该性质,可以分三类:

1.一个值比自己大,一个比自己小,则该节点就是最近祖先;

2.都比自己小,递归到左子树,比自己大,递归到右子树;

9.二叉树与双向链表

leetcode

class Solution {
private:
	Node* head;
	Node* pre;
	//cur中序遍历
	void inorder(Node* cur) {
		if (cur == nullptr)return;
		inorder(cur->left);
		//转换
		//当pre不为空时 需要将cur->left = pre, pre->right = cur , 再将pre指向cur
		if (pre != nullptr) 
        {
			pre->right = cur;
			cur->left = pre;
            pre = cur;
		}
		else 
        {
		//当pre为nullptr 时 表示cur->val 为最小即为head头结点 只需要令head = cur,再将pre指向cur
			head = cur;
            pre = cur;
		}
		inorder(cur->right);
	}
public:
	Node* treeToDoublyList(Node* root) {
		if (root == nullptr)return root;
		inorder(root);
		//处理头尾
		head->left = pre;
		pre->right = head;
		return head;
	}
};

解题思路

将搜索树转化为排序的双向链表

  1. 通过排序可以想到搜索树的特性,即中序为有序;

  2. 双向则为可以想到左右指向改为链表的前后指针;

综上:核心代码为

cur->left = pre;//
if(pre)
    pre->right = cur;
pre = cur;

10.重建二叉树

leetcode

class Solution {
public:
     TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder ,int& prei ,int inbegin , int inend)
     {
        if(inbegin > inend)//传入空节点
        return NULL;

        TreeNode* root = new TreeNode(preorder[prei]);//根节点处于前序第一个位置
        int rooti = inbegin;
         //寻找中序根节点的位置
        while(rooti < inend)
        {
            if(inorder[rooti] == preorder[prei])
            break;
            else
            ++rooti;
        }
        if(inbegin <= rooti-1)//递归重建左树
        root->left = _buildTree(preorder,inorder,++prei,inbegin,rooti-1);
        else
        root->left = NULL;
        if(inend >= rooti+1)//递归重建右树
        root->right = _buildTree(preorder,inorder,++prei,rooti+1,inend);
        else
        root->right = NULL;
      return root;
     }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
       int prei = 0;
       int inbegin = 0 , inend = inorder.size() - 1;

       return _buildTree(preorder,inorder,prei,inbegin,inend);
    }
};

解题思路

  1. 二叉树的中序和前序可以重建一颗二叉树;
  2. 前序可以确定根节点的位置;
  3. 中序找到根节点后可以分为左右子树的递归重建;
<think>好的,我现在需要处理用户的问,他想查找与labuladong相关的二叉树算法汇总或学习资料。首先,我要确定用户的需求是什么。用户可能是在准备面试,特别是算法部分,而labuladong的算法小抄在LeetCode社区中比较有名,所以用户可能希望找到相关的目集合或者学习路径。 接下来,我需要回忆一下labuladong的算法文章中涉及二叉树的部分。通常,这类资料会覆盖二叉树的基本操作,比如遍历(前序、中序、后序、层次)、递归与迭代的实现、构造二叉树、最近公共祖先、路径问等。此外,可能还包括二叉搜索树(BST)的相关目,比如验证BST、BST的插入删除、以及一些高级应用。 然后,我需要参考用户提供的引用内容。引用[1]提到了将有序数组转换为平衡二叉搜索树,这涉及到分治法和递归,属于二叉树的构造问。引用[2]讨论了如何构建多叉树并寻找最近公共祖先,这可能与二叉树的LCA问相关。这些引用内容帮助我确认用户之前可能接触过二叉树构造和公共祖先的问,现在希望有更系统的习汇总。 接下来,我需要确保回答符合系统级指令。特别是要正确使用LaTeX格式,行内公式用$...$,独立公式用$$...$$。同时,生成的相关问需要与主相关,并且结构清晰,分点回答。 然后,我应该组织回答内容。首先介绍labuladong的算法资料,然后分核心考点列举常见的二叉树类型,并对应到LeetCode目。每个类型给出例,并简要说明解思路,可能的话引用用户提供的例子中的方法,比如分治法、递归、双指针等。 同时,需要生成相关问,这些问应该涵盖用户可能进一步询问的方向,比如具体目的解法、学习路径、算法优化等。 最后,检查是否符合格式要求,没有使用Markdown,正确使用引用标识,并确保LaTeX语法正确。例如,变量和公式用$包裹,独立公式单独成段。 可能还需要考虑用户是否有其他需求,比如是否有特定的目类型没有涵盖到,是否需要更多的资源推荐,或者更详细的解步骤。但根据用户当前的问,主要是汇总习,所以重点放在分类和例的列举上。 总结步骤: 1. 介绍labuladong的算法资料,特别是二叉树部分。 2. 分点列出核心考点,每个考点给出对应的LeetCode目和解关键。 3. 结合用户提供的引用中的方法,如分治法、双指针寻找公共祖先。 4. 生成相关问,引导用户深入探讨每个考点或方法。</think>关于labuladong的二叉树算法汇总,其核心内容主要围绕二叉树遍历框架、递归思维和典型型展开。以下是结合LeetCode高频型的分类总结: ### 一、核心考点分类 #### 1. 二叉树遍历与递归框架 - **前/中/后序遍历**:$递归 \Rightarrow 迭代$的转换 - 例二叉树的中序遍历(LeetCode 94)[^1] - 关键:`栈+指针`的迭代实现 - **层次遍历**:队列实现分层处理 - 例二叉树的右视图(LeetCode 199) - 关键:`BFS+记录每层最后一个节点` #### 2. 二叉树构造问 - **前序+中序构造二叉树**(LeetCode 105) - 关键:确定根节点后递归划分区间[^1] - 代码框架: ```python def buildTree(preorder, inorder): if not preorder: return None root_val = preorder[0] index = inorder.index(root_val) root.left = buildTree(preorder[1:1+index], inorder[:index]) root.right = buildTree(preorder[1+index:], inorder[index+1:]) ``` #### 3. 二叉搜索树(BST)特性应用 - **验证BST**(LeetCode 98) - 关键:$lower < val < upper$的递归传递 - **BST转累加树**(LeetCode 538) - 关键:`逆中序遍历+累加和` #### 4. 二叉树路径问 - **最大路径和**(LeetCode 124) - 关键:后序遍历中计算$max(left+right+root.val)$ - **路径总和**(LeetCode 112) - 关键:`DFS+目标值递减` #### 5. 最近公共祖先(LCA) - **普通二叉树的LCA**(LeetCode 236) - 关键:递归返回`p/q存在的标记`[^2] - **BST的LCA**(LeetCode 235) - 关键:利用BST大小关系剪枝 ### 二、进阶技巧 1. **分解子问思维**:把整棵树的问拆分为左右子树的问(如翻转二叉树) 2. **虚拟头节点技巧**:处理链表化二叉树(LeetCode 114) 3. **回溯与剪枝**:路径问中记录访问路径(如LeetCode 257)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值