描述
这是一篇面对初级coder的题解。
知识点:树 递归 DFS
难度:一星
题解
题目:操作给定的二叉树,将其变换为源二叉树的镜像。
比如: 源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5
考察树的基础知识与递归的思路,深度优先搜索。
方法一:递归求解
思路:递归
本质上是左右子树的交换,通过对左右子树的交换,可以完成二叉树的镜像
机制一:交换
需要进行左右子树的交换
最通用的思路如上图所示,用一个空指针temp完成交换
机制二:递归
左右节点交换后,继续递归左右子树
一个清晰的版本
class Solution { public: TreeNode* Mirror(TreeNode* pRoot) { if(pRoot) //判断边界条件,是否为空树 空树递归结束 { /*交换*/ TreeNode* temp;//定义一个缓冲指针 temp=pRoot->left;//缓冲左树 pRoot->left=pRoot->right;//左树等于右树 pRoot->right=temp;//右树等于左树(缓冲) /*递归*/ pRoot->left=Mirror(pRoot->left);//递归左树 pRoot->right=Mirror(pRoot->right);//递归右树 } return pRoot; } };
递归与交换整合
class Solution { public: TreeNode* Mirror(TreeNode* pRoot) { if(pRoot) //判断边界条件,是否为空树 空树递归结束 { TreeNode* temp;//定义一个缓冲指针 temp=Mirror(pRoot->left);//缓冲并镜像左树 pRoot->left=Mirror(pRoot->right);//左树等于右树 pRoot->right=temp;//右树等于左树(缓冲) } return pRoot; } };
用栈实现递归
本质上与函数递归一样,只是手动维护了一个调用栈
#include <stack> class Solution { public: TreeNode* Mirror(TreeNode* root) { if(!root) return root; //判断边界条件 stack<TreeNode*> stack;//定义递归栈 TreeNode* temp;//定义一个缓冲指针 stack.push(root);//压入头节点 while (!stack.empty()) { TreeNode* node = stack.top(); //头结点出栈 stack.pop(); //将左右节点入栈 if (node->left) stack.push(node->left); if (node->right) stack.push(node->right); //交换左右节点 temp = node->left; node->left = node->right; node->right = temp; } return root; } };
复杂度分析:
时间复杂度 O(N) : 其中 NN 为二叉树的节点数量,建立二叉树镜像需要遍历树的所有节点,占用O(N) 时间。
空间复杂度 O(N): 最差情况下(当二叉树退化为链表),递归时系统需使用 O(N) 大小的栈空间。
运行时间8ms 占用内存400KB
总结
递归的解法在树的解题中有广泛应用:
如下动图展示了深度优先搜索的递归算法:
解题模板
下面给出二叉树对称性递归的解题模板
1、递归结束条件:特殊情况的判断
如果是单树问题,一般来说只要进行以下判断:
下面给出二叉树对称性递归的解题模板
1、递归结束条件:特殊情况的判断
如果是单树问题,一般来说只要进行以下判断:
if(!root) return true/false; if(!root->left) return true/false/递归函数; if(!root->right) return true/false/递归函数;如果是双树问题(根节点分别为p,q),一般来说进行以下判断:
if(!p && !q) return true/false; if(!p || !q) return true/false;
当然也不完全是这些情况,比如有的需要加上节点值的判断,需要具体问题需要具体分析
2、返回值
通常对称性递归的返回值是多个条件的复合判断语句
可能是以下几种条件判断的组合:
节点非空的判断
节点值比较判断
(单树)调用根节点左右子树的递归函数进行递归判断
(双树)调用两棵树的左右子树的递归函数进行判断