算法第12天|继续学习二叉树:翻转二叉树、对称二叉树、二叉树最大深度、二叉树的最小深度
今日总结:
递归法的三段式应用
1、确定递归的返回参数、输入参数
2、确定递归的停止条件
3、确定单次递归的递归逻辑与返回值
对称二叉树需要重新思考
1、迭代法中是如何使用队列进行判断对称的
2、递归法中是传入什么参数,什么逻辑判断对称的
二叉树的最小深度需要重新思考:
所谓的深度是指根节点到叶子节点,如果某个节点只有左子节点或者右子节点,不是最短的路径,只有左右子节点都没有的时候才是底。
翻转二叉树
题目链接:226. 翻转二叉树 - 力扣(LeetCode)
整体思路:
翻转二叉树,本质上可以看作是对左右子树的翻转,所以更加细的讲:将每个节点的左右子节点进行翻转就实现了翻转二叉树。
对于深度优先搜索:
前序遍历(中前后)(递归、迭代)
后序遍历(前后中)(递归、迭代)
对于广度优先搜索:
层序遍历(递归、迭代)
都是可以使用的,在要将当前位置节点记录的时候转换左右节点就实现了翻转二叉树。
前序遍历递归翻转二叉树代码
/*** 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:
//前序遍历void digui(TreeNode* &root){//判断停止条件if(root==nullptr)return;swap(root->left,root->right);digui(root->left);digui(root->right);}TreeNode* invertTree(TreeNode* root) {//深度优先、前序遍历翻转digui(root);return root;}
};
前序遍历迭代翻转二叉树代码
/*** 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) {深度优先、前序遍历迭代法stack<TreeNode*>stk;//根节点入栈if(root==nullptr)return root;stk.push(root);while(!stk.empty()){//出栈TreeNode*cur = stk.top();stk.pop();swap(cur->left,cur->right);//右左节点入栈if(cur->right!=nullptr)stk.push(cur->right);if(cur->left!=nullptr)stk.push(cur->left);}return root;}
};
层序遍历递归翻转二叉树代码
/*** 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>>res;void digui(TreeNode* &root,int depth){//停止条件if(root==nullptr)return ;//判断是不是第一次进入这一行if(res.size()==depth)res.push_back(vector<int>());res[depth].push_back(root->val);//交换左右子节点swap(root->left,root->right);if(root->left!=nullptr) digui(root->left,depth+1);if(root->right!=nullptr) digui(root->right,depth+1);//递归左,递归右}TreeNode* invertTree(TreeNode* root) {//使用广度优先,层序遍历,递归法//定义一个存储节点的vector数组,因为要跟depth比较digui(root,0);return root; }
};
层序遍历迭代翻转二叉树代码
/*** 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) {//使用广度优先、层序遍历迭代法//定义队列queue <TreeNode*>que;if(root==nullptr)return root;que.push(root);//开始while判断while(!que.empty()){//记录队列的长度,因为队列一直变换int size = que.size();for(int i=0;i<size;i++){//弹出队头TreeNode* cur = que.front();que.pop();swap(cur->left,cur->right);if(cur->left!=nullptr) que.push(cur->left);if(cur->right!=nullptr)que.push(cur->right);}}return root;}
};
对称二叉树
题目链接:101. 对称二叉树 - 力扣(LeetCode)
整体思路:
因为是寻找对称,是左边与右边的关系,可以看作是两棵子树的关系,可以通过对左右两棵子树的对应位置进行判断:使用递归、迭代(两个对应节点一组入栈或队)
递归法:
1、确定返回参数、传入的参数
这个题是判断是不是对称,返回参数是bool;
这个题是判断左右子树是不是对称,传入参数是两个节点
bool digui(TreeNode* left,TreeNode* right)
2、确定停止条件
因为是判断是不是对称,当出现不对称或者遍历完了就返回
左边空,右边不空,返回false
左边不空,右边空,返回false
左边空,右边空,返回true
左边不空,右边不空,左边值不等于右边值,返回false
if(left==nullptr&&right!=nullptr) return false;
else if(left!=nullptr&&right==nullptr) return false;
else if(left!=nullptr&&right!=nullptr&&left->val!=right->val)return false;
else if(left==nullptr&&right ==nullptr)return true;
3、确定单次递归的逻辑
获取下一次递归返回的bool值进行判断当前层次返回值
(1)将左节点的左节点与右节点的右节点继续递归
(2)将左节点的右节点与右节点的左节点继续递归
(3)两者的返回值都是true,当前层的返回值才能是true;
递归法代码
// /**
// * 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 digui(TreeNode* left,TreeNode* right){//设置停止条件//如果左空右不空、左不空右空、左右都不空但是值不同、左右都空if(left==nullptr&&right!=nullptr) return false;else if(left!=nullptr&&right==nullptr) return false;else if(left!=nullptr&&right!=nullptr&&left->val!=right->val)return false;else if(left==nullptr&&right ==nullptr)return true;//只剩下左右都不空左右值相同//左右两个节点都有自己的左右节点,所以是左节点的左子节点与右节点的右子节点;左节点的右子节点与右节点的左子节点进行判断bool A = digui(left->left,right->right);bool B = digui(left->right, right->left);bool C =false;return C= A&&B;}bool isSymmetric(TreeNode* root) {//轴对称、首先判断左子节点与右子节点,之后判断的是左子节点的左子节点与右子节点的右子节点//使用一个递归形式,将整个二叉树分为左子树与右子树if(root==nullptr)return true;return digui(root->left, root->right);
迭代法:
迭代法类似于层序遍历,但是与层序遍历不同;
(1)设置一个队列,用于存储左右子树的对应节点,
(2)每次取出对应的两个节点,用于判断当前两个节点是不是相同(对称)
(3)之后将两个节点的对应子节点入队,直到队空
迭代法代码
/*** 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 isSymmetric(TreeNode* root) {//使用类似层序遍历,队列//将一层的节点输入进队列,两个两个的拿出,然后将其子节点输入到队列,直到队列为空queue <TreeNode*> que;if(root==nullptr)return true;//将根节点的左右子节点输入到队列que.push(root->left);que.push(root->right);while(!que.empty()){//进行判断两个节点的关系,出队TreeNode* cur1 = que.front();que.pop();TreeNode* cur2 =que.front();que.pop();if(cur1==nullptr&&cur2!=nullptr)return false;else if(cur1!=nullptr&&cur2==nullptr)return false;else if(cur1==nullptr&&cur2==nullptr) continue;//不能直接返回true,因为跟递归不同,返回true不是返回上一层,而是直接返回答案,应该用continue直到队列是空的else if(cur1!=nullptr&&cur2!=nullptr&&cur1->val!=cur2->val)return false;//此时只有两个节点不空、节点一样//队列中加入两个节点的对应位置的子节点que.push(cur1->left);que.push(cur2->right);que.push(cur1->right);que.push(cur2->left);}return true;}
};
二叉树的最大深度
整体思路
二叉树的深度,就是根节点到叶子节点的最远距离,可以使用递归的形式,从最下边开始计算节点的高度,直到根节点;也可以使用迭代法层序遍历直接判断有多少层
递归法
1、确定递归返回参数、输入参数
递归的返回参数应该是当前节点的高度,输入参数应该是当前节点
2、确定递归停止条件
当节点与到nullptr表示找到了叶子节点的下一个子节点,所以此层的高度应该是0
3、确定单层递归的递归逻辑
一个节点有左右子节点,当前节点返回:获取左右子节点的最大高度后+1
递归法代码
/*** 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://1、确定递归返回参数、输入参数//返回参数应该是高度,输入的参数应该是节点int getdepth(TreeNode*root){//2、确定停止条件//当前节点是nullptr的时候就返回,返回的是高度0,假设叶子节点是高度1if(root==nullptr)return 0;//3、确定单层循环的循环逻辑//获取左右子节点的高度,返回高的那个int depth_left = getdepth(root->left);int depth_right =getdepth(root->right);return 1+max(depth_left,depth_right);}int maxDepth(TreeNode* root) {//使用递归的方法,进行寻找depthint depth = getdepth(root);return depth;}
};
迭代法:
层序遍历迭代法模板
/*** 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:int maxDepth(TreeNode* root) {//定义一个深度int depth=0;//定义一个队列queue <TreeNode*>que;//根节点if(root==nullptr)return 0;que.push(root);while(!que.empty()){//获取队列大小int size = que.size();for(int i=0;i<size;i++){//临时节点,出队TreeNode* cur = que.front();que.pop();if(cur->left!=nullptr) que.push(cur->left);if(cur->right!=nullptr) que.push(cur->right);}depth++;}return depth;}
};
二叉树的最小深度
题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)
整体思路:
与二叉树的最大深度类似,但是不能直接修改为min,因为所谓的最小深度,是从叶子节点到根节点,如果一个节点只有一个子节点,不属于叶子节点:
此时的最小深度应该需要是从拥有的子节点的那条路径进行往下,所以需要在最大深度的代码上添加限制条件:出现一个子节点的时候要如何?
递归法:
/*** 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://1、确定递归的返回、输入int getdepth(TreeNode* root){//2、确定递归的停止if(root==nullptr)return 0;//最后的高度为0//3、确定单层的逻辑及返回值//如果当前节点的左右子节点不全,不能直接获取最小的depth,,而是让另一个不为nullptr的节点继续遍历,获取他的高度,只有叶子节点才能是底层的高度1int left_depth = getdepth(root->left);int right_depth = getdepth(root->right);if(root->left==nullptr&&root->right!=nullptr)return 1+right_depth;if(root->left!=nullptr&&root->right==nullptr)return 1+left_depth;int depth = 1+min(left_depth,right_depth);return depth;}int minDepth(TreeNode* root) {return getdepth(root); }
};
迭代法:
迭代法就是在层序遍历迭代法的模板上,添加限制条件:寻找最先遇到的叶节点(左右子节点都没有)
/*** 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:int minDepth(TreeNode* root) {//层序遍历迭代法://定义队列,深度queue<TreeNode*>que;int depth=0;//根节点入队if(root==nullptr)return 0;que.push(root);while(!que.empty()){//定义队列大小int size = que.size();depth++;for(int i=0;i<size;i++){TreeNode* cur = que.front();que.pop();//如果当前节点没有子节点,就可以直接返回深度了if(cur->left==nullptr&&cur->right==nullptr)return depth;if(cur->left!=nullptr) que.push(cur->left);if(cur->right!=nullptr) que.push(cur->right);}}return depth;}
};// /**
// * 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:
// //1、确定递归的返回、输入
// int getdepth(TreeNode* root)
// {
// //2、确定递归的停止
// if(root==nullptr)return 0;//最后的高度为0
// //3、确定单层的逻辑及返回值
// //如果当前节点的左右子节点不全,不能直接获取最小的depth,,而是让另一个不为nullptr的节点继续遍历,获取他的高度,只有叶子节点才能是底层的高度1
// int left_depth = getdepth(root->left);
// int right_depth = getdepth(root->right);
// if(root->left==nullptr&&root->right!=nullptr)
// return 1+right_depth;
// if(root->left!=nullptr&&root->right==nullptr)
// return 1+left_depth;// int depth = 1+min(left_depth,right_depth);
// return depth;
// }
// int minDepth(TreeNode* root) {
// return getdepth(root);
// }
// };