二叉树相关
二叉树的天然递归结构
104. 二叉树的最大深度
题目描述
题解
左子树的最大深度+右子树的最大深度+1
int maxDepth(TreeNode* root) {
if(root==NULL)
{
return 0;
}
return max(maxDepth(root->left),maxDepth(root->right))+1;
}
111. 二叉树的最小深度
题目描述
题解 注意终止条件必须是叶子节点 左右子树为空
注意左右子树不存在的情况 并且计算深度的起始点需要是叶子节点
int minDepth(TreeNode* root) {
if(root==NULL)return 0;
if(root->left==NULL)return minDepth(root->right)+1;
if(root->right==NULL)return minDepth(root->left)+1;
return min(minDepth(root->left),minDepth(root->right))+1;
}
题解2 简化代码
int minDepth(TreeNode* root) {
if(root==NULL)return 0;
if(root->left==NULL||root->right==NULL)
return max(minDepth(root->left),minDepth(root->right))+1;
return min(minDepth(root->left),minDepth(root->right))+1;
}
题解3
int minDepth(TreeNode* root) {
if (!root) return 0; //递归结束
int left = minDepth(root->left);
int right = minDepth(root->right);
if (!left || !right) return left + right + 1; //如果有一个空,则+1
return min(left, right) + 1; //否则最小值+1
}
简单的二叉树递归问题
226. 翻转二叉树
题目描述
题解1 先交换左右节点的位置再反转节点
TreeNode* invertTree(TreeNode* root) {
if(root==NULL)return NULL;
swap(root->left,root->right);
root->left=invertTree(root->left);
root->right=invertTree(root->right);
return root;
}
题解2 先反转左右节点 再交换位置
TreeNode* invertTree(TreeNode* root) {
if(root==NULL)return NULL;
invertTree(root->left);
invertTree(root->right);
swap(root->left,root->right);
return root;
}
100. 相同的树
题目描述
题解1 判断左右节点是否相同并且root值相同
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p==NULL)return q==NULL;
else if(q==NULL)return false;
else{
return (isSameTree(p->left,q->left)&&isSameTree(p->right,q->right)&&p->val==q->val);
}
}
题解2 化简代码
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p&&!q)return true;
if(!p||!q)return false;//有一个节点为空提前返回
return (isSameTree(p->left,q->left)&&isSameTree(p->right,q->right)&&p->val==q->val);
}
101. 对称二叉树
题目描述
题解1 左子树反转后和右子树相同
class Solution {
public:
TreeNode* reverse(TreeNode* root)//子树反转
{
if(root==NULL)return NULL;
swap(root->left,root->right);
reverse(root->left);
reverse(root->right);
return root;
}
bool issame(TreeNode* root1,TreeNode* root2)//判断是否相同
{
if(!root1&&!root2)return true;
if(!root1||!root2)return false;
return(issame(root1->left,root2->left)&&issame(root1->right,root2->right)&&root1->val==root2->val);
}
bool isSymmetric(TreeNode* root) {//反转后相同g
if(root==NULL) return true;
reverse(root->left);
return(issame(root->left,root->right));
}
};
题解2 右子树等于左子树 左子树等于右子树 根节点值相等
bool dfs(TreeNode* root1,TreeNode* root2)
{
//if(!root1&&!root2)return true;
if(!root1||!root2)return !root1&&!root2;
return root1->val!=root2->val?false:dfs(root1->left,root2->right)&&dfs(root1->right,root2->left);
}
bool isSymmetric(TreeNode* root) {//反转后相同g
//if(root==NULL) return true;
return root?dfs(root->right,root->left):true;
}
222. 完全二叉树的节点个数
题目描述
题解1 计算每个节点的个数(没有用的完全二叉树的性质)
int countNodes1(TreeNode* root) {
//if()return 0;
return root == NULL ? 0 : 1 + countNodes1(root->left) + countNodes1(root->right);
}
题解2 计算二叉树的层数
int countlevel(TreeNode* root)
{
int ret=0;
while(root)
{
ret++;
root=root->left;
}
return ret;
}
int countNodes2(TreeNode* root) {
if(root==NULL)return 0;
int llevel=countlevel(root->left);
int rlevel=countlevel(root->right);
if(llevel==rlevel)//如果有左子树和右子树 右子树的节点数+完全的左子树的节点
{
return countNodes2(root->right)+(1<<llevel);//位运算简化
}
return countNodes2(root->left)+countNodes2(root->right)+1;
}
题解3 二分查找
bool judge(TreeNode* node, int h){//计算高度
while(node){
h--;
node = node->left;
}
return h == 0;
}
int countNodes(TreeNode* root) {
if(NULL == root) return 0;
TreeNode* node = root;
int h = 0;//树的高度
while(node){
h++;
node = node->left;
}
node = root;
int sum = 1 << (h - 1);//满节点的数量
while((--h) > 0){
TreeNode* right = node->right;
if(judge(right, h))//判断右子树最大 如果和h相同
{
sum += 1 << (h - 1);//左子树是满的
node = node->right;
}else{//和h高度不同
node = node->left;//说明左子树不满
}
};
return sum;
}
110. 平衡二叉树
题目描述
题解1
int bfs(TreeNode* node)//-1表示不是平衡二叉树
{
if(node==NULL)return 0;
int lbfs=bfs(node->left);
if(lbfs==-1)return -1;//左右子树有一个不是 就提前终止
int rbfs=bfs(node->right);
if(rbfs==-1)return -1;
return abs(lbfs-rbfs)<2? max(lbfs,rbfs)+1:-1;//看高度差
}
bool isBalanced(TreeNode* root) {
return bfs(root)!=-1;
}
题解2
int countlevel(TreeNode* root)
{
if(root==NULL)return 0;
return max(countlevel(root->left),countlevel(root->right))+1;
}
bool isBalanced(TreeNode* root) {
if(root==NULL)return true;
if(!isBalanced(root->left)||!isBalanced(root->right))return false;
int llevel=countlevel(root->left);
int rlevel=countlevel(root->right);
return abs(llevel-rlevel)<2;
}
题解3 优化 提前返回
int countlevel(TreeNode* root)
{
if(root==NULL)return 0;
return max(countlevel(root->left),countlevel(root->right))+1;
}
bool isBalanced(TreeNode* root) {//优化 提前返回
if(root==NULL)return true;
if(!(isBalanced(root->left)&&isBalanced(root->right)))return false;
int llevel=countlevel(root->left);
int rlevel=countlevel(root->right);
return abs(llevel-rlevel)<2;
}
注意递归的终止条件
112. 路径总和
题目描述
题解 看左节点有没有sum-val的路径
bool hasPathSum(TreeNode* root, int sum) {
if(root==NULL)return sum==0;
return hasPathSum(root->left,sum-root->val)||hasPathSum(root->right,sum-root->val);
}
404. 左叶子之和
题目描述
题解
如果是有左子树是左子叶 当前节点左子树值 +递归右子树
否则 左子叶的数量+右子叶的数量
int sumOfLeftLeaves(TreeNode* root) {
if(root==NULL)return 0;
if(root->left&&root->left->left==NULL&&root->left->right==NULL)return sumOfLeftLeaves(root->right)+root->left->val;
return sumOfLeftLeaves(root->left)+sumOfLeftLeaves(root->right);
}
定义递归问题
257. 二叉树的所有路径
题目描述
题解1 终止条件 必须是叶子节点
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
if(root==NULL) return ret;
if(root->left==NULL&&root->right==NULL)//是叶子节点
{
ret.push_back(to_string(root->val));//结尾放当前节点
return ret;
}
vector<string> lret=binaryTreePaths(root->left);//左子叶的路径
for(int i=0;i<lret.size();i++)
{
ret.push_back(to_string(root->val)+"->"+lret[i]);
}
vector<string> rret=binaryTreePaths(root->right);//右子叶的路径
for(int i=0;i<rret.size();i++)
{
ret.push_back(to_string(root->val)+"->"+rret[i]);
}
return ret;
}
题解2
class Solution {
public:
void dfs(TreeNode* root,vector<string>& ret)
{
if(root==NULL)return;
if(root->left==NULL&&root->right==NULL)//是叶子节点
{
ret.back()+=to_string(root->val);
return;
}
if(root->left&&root->right)//都有
{
string tmp=ret.back();//加入当前节点 再遍历左子树
ret.back()+=to_string(root->val)+"->";
dfs(root->left,ret);
ret.push_back(tmp);//加上当前节点 遍历右子树
ret.back()+=to_string(root->val)+"->";
dfs(root->right,ret);
return;
}
if(root->left)
{
ret.back()+=to_string(root->val)+"->";
dfs(root->left,ret);
return;
}
ret.back()+=to_string(root->val)+"->";
dfs(root->right,ret);
return;
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
if(root==NULL)return ret;
ret.push_back(string());//把结果传进来
dfs(root,ret);
return ret;
}
};
题解3 使用栈
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
if(root==NULL)return ret;
stack<pair<TreeNode*,string>> s;//当前节点以及到当前节点的路径
pair<TreeNode*,string> tmp;//临时变量
s.push(make_pair(root,to_string(root->val)));
while(!s.empty())
{
tmp=s.top();
s.pop();
if(tmp.first->right)//有右子树
{
s.push(make_pair(tmp.first->right,tmp.second+"->"+to_string(tmp.first->right->val)));//添加子树元素
}
if(tmp.first->left)//有左子树
{
s.push(make_pair(tmp.first->left,tmp.second+"->"+to_string(tmp.first->left->val)));//添加子叶元素
}
if(!tmp.first->right&&!tmp.first->left)//是叶子节点
{
ret.push_back(tmp.second);//在返回值添加
}
}
return ret;
}
113. 路径总和 II
题目描述
方法1 递归
public:
void dfs(TreeNode* root,vector<vector<int>>&ret,vector<int>tmpret,int sum)
{
if(root==NULL)return;
if(!root->left&&!root->right&&sum==root->val)//是子叶节点并且值相等
{
tmpret.push_back(root->val);
ret.push_back(tmpret);//添加子路径
}
tmpret.push_back(root->val);//添加当前节点值
dfs(root->left,ret,tmpret,sum-root->val);
dfs(root->right,ret,tmpret,sum-root->val);
return;
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>>ret;
vector<int> tmpret;
dfs(root,ret,tmpret,sum);
return ret;
}
};
方法2 用栈模仿递归
vector<vector<int>> res;
vector<int> temp;//防止反复初始化数组
void dfs(TreeNode* root,int sum){
int resum=sum-root->val;//要找的值
temp.push_back(root->val);//假设它在
if(resum==0&&!root->left&&!root->right)
res.push_back(temp);//找到答案
if(root->left)
dfs(root->left,resum);
if(root->right)
dfs(root->right,resum);
temp.pop_back();//回溯 temp是全局变量
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
if(root)dfs(root,sum);
return res;
}
129. 求根到叶子节点数字之和
题目描述
题解
int sum=0;//返回值
int tmpsum=0;//临时的值
int sumNumbers(TreeNode* root) {
if(root==NULL) return 0;
int t=tmpsum;//之前的累加
tmpsum=tmpsum*10+root->val;//加上该节点
if(!root->left&&!root->right)sum+=tmpsum;//叶子节点就在返回值上加
sumNumbers(root->left);
sumNumbers(root->right);
tmpsum=t;
return sum;
}
复杂的二叉树递归问题
437. 路径总和 III
题目描述
题解 分为包含当前节点和不包含当前节点 两种递归情况
int pathSum(TreeNode* root, int sum) {
if(root==NULL)return NULL;
int ret=0;
ret+=findpath(root,sum);
ret+=pathSum(root->left,sum);
ret+=pathSum(root->right,sum);
return ret;
}
int findpath(TreeNode* root,int sum)//包含当前节点
{
if(root==NULL)return 0;
int ret=0;
if(root->val==sum)ret=1;
ret+=(findpath(root->left,sum-root->val)+findpath(root->right,sum-root->val));//可能为负数
return ret;
}
二分搜索树问题
530. 二叉搜索树的最小绝对差
题目描述
题解1 二叉树中序遍历 利用栈模拟实现
题解2 递归实现二叉树中序遍历 储存上一个节点
void getMinimum(TreeNode* root,int &pre,int& minval) {//储存上一个节点的值
if(root==NULL)
{
return ;
}
getMinimum(root->left,pre,minval);//传地址
if(pre>=0){minval=min(root->val-pre,minval);}
pre=root->val;
getMinimum(root->right,pre,minval);
return;
}
int getMinimumDifference(TreeNode* root) {
if(root==NULL)
{
return 0;
}
int pre=-1;
int minval=INT_MAX;
getMinimum(root,pre,minval);
return minval;
}
235. 二叉搜索树的最近公共祖先
题目描述
题解1 根据二叉搜索树的性质判断
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);
if(p->val<root->val&&q->val<root->val)//都大在左子树中找
return lowestCommonAncestor(root->left,p,q);
return root;//如果一大一小 或者有节点相等 那么当前节点就是公共祖先
}
98. 验证二叉搜索树
题目描述
题解1 利用中序遍历从小到大的性质
bool ValidBST(TreeNode* root,long long &min) {//上一个节点的值
if(root==NULL)return true;
if(!ValidBST(root->left,min))return false;
if(root->val<=min)return false;//当前节点的值大于等于上一个节点的值就不是搜索树
min=root->val;
return ValidBST(root->right,min);
}
bool isValidBST(TreeNode* root) {
if(root==NULL)return true;
long long min=LONG_MIN;
return ValidBST(root,min);
}
题解2 根据定义进行判断
bool ValidBST(TreeNode* root,long long min,long long max) {
if(root==NULL)return true;
if(root->val<=min||root->val>=max)
return false;
return ValidBST(root->left,min,root->val)&&ValidBST(root->right,root->val,max);//左边是搜索树右边也是搜索树
}
bool isValidBST(TreeNode* root) {
if(root==NULL)return true;
return ValidBST(root,LONG_MIN,LONG_MAX);
}
题解3 利用栈进行中序遍历
bool isValidBST(TreeNode* root) {
if(root==NULL)return true;
long long min=LONG_MIN;
stack<TreeNode*> s;
// s.push(root);
while(!s.empty()||root)
{
while(root)
{
s.push(root);
root=root->left;
}
root=s.top();
s.pop();
if(root->val<=min)return false;
min=root->val;
root=root->right;
}
return true;
}
450. 删除二叉搜索树中的节点
题目描述
思路1 找到右子树中的最小值替换当前节点
TreeNode* findmin(TreeNode* node)//右子树的最小值
{
while(node->left)
{
node=node->left;
}
return node;
}
TreeNode* deleteNode(TreeNode* node, int key) {
if(node==NULL)return NULL;
if(node->val>key)//在左子树中找
node->left=deleteNode(node->left,key);
else if(node->val<key)
node->right=deleteNode(node->right,key);
else//删除当前节点
{
if(!node->left&&!node->right) {delete node; return NULL;}
if(!node->right)//没有右子树 说明是最大值
{
TreeNode* tmp=node->left;
delete node;
return tmp;
}
else if(!node->left)//没有左子树 说明是最小值
{
TreeNode* tmp=node->right;
delete node;
return tmp;
}
else
{
TreeNode* tmp=findmin(node->right);//右子树的最小值
cout<<tmp->val;
swap(node->val,tmp->val);//交换
node->right=deleteNode(node->right,key);//递归删除
return node;
}
}
return node;
}
108. 将有序数组转换为二叉搜索树
题目描述
题解1 通过对数组的二分查找将根节点设置为中间值
TreeNode*sorted(vector<int>&nums,int l,int r)
{
if(l>r)return NULL;
//if(l==r) return new TreeNode(nums[l]);
int mid=l+(r-l)/2;
TreeNode* root=new TreeNode(nums[mid]);
root->left=sorted(nums,l,mid-1);
root->right=sorted(nums,mid+1,r);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return sorted(nums,0,nums.size()-1);
}
230. 二叉搜索树中第K小的元素
题目描述
题解1 统计左右子树节点个数
int numroot(TreeNode* root)
{
if(root==NULL)return 0;
return 1+numroot(root->left)+numroot(root->right);
}
int kthSmallest(TreeNode* root, int k) {
if(root==NULL)return 0;
int num=numroot(root->left);//左子树节点个数
if(num==k-1)return root->val;
if(num>=k) return kthSmallest(root->left,k);
else
return kthSmallest(root->right,k-num-1);
}
利用栈中序遍历 记录当前节点是第几个节点
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> s;
int ret;
if(root==NULL)return 0;
int i=0;
TreeNode* tmp;
while(!s.empty()||root)
{
while(root)
{
s.push(root);
root=root->left;
}
tmp=s.top();
s.pop();
i++;
if(i==k)break;
root=tmp->right;
}
return tmp->val;
}
思路3 递归中序遍历二叉树
void numroot(TreeNode* root,int k,int &pre)
{
if(root==NULL)return ;
numroot(root->left,k,pre);
i++;
if(i==k)
{pre=root->val;return;}
numroot(root->right,k,pre);
return ;
}
int kthSmallest(TreeNode* root,int k) {
if(root==NULL)return 0;
int pre=-1;
numroot(root,k,pre);//pre为返回值 找到的二叉树节点的值
return pre;
}
思路4 取消传出参数 之间返回
int kthSmallest(TreeNode* root,int k) {
if(root==NULL)return 0;
int ret=kthSmallest(root->left,k);
if (ret!=0)return ret;
i++;
if(i==k)
{return root->val;}
return kthSmallest(root->right,k);
}
236. 二叉树的最近公共祖先
题目描述
题解1 递归遍历二叉树
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==NULL)
return NULL;
if(root->val==p->val||root->val==q->val)
return root;
TreeNode* ltree=lowestCommonAncestor(root->left,p,q);//查看左子树中有没有p或q
//if(ltree)cout<<"lroot"<<ltree->val<<endl;
TreeNode* rtree=lowestCommonAncestor(root->right,p,q);//查看右子树中给有没有p或q
if(ltree==NULL)return rtree;//左子树中没有 就在右子树中
if(rtree==NULL)return ltree;//右子树中没有 就在左子树中
if(ltree&&rtree)return root;//如果都有 说明一边一个
return NULL;
}
题解2
class Solution {
public:
unordered_map<int,TreeNode*> father;//记每个节点的父节点
unordered_map<int,bool> visited;//从p到根节点的路径
void dfs(TreeNode* root)//记录每个节点的父节点
{
if (root==NULL)return;
if(root->left)
{
father[root->left->val]=root;
dfs(root->left);
}
if(root->right)
{
father[root->right->val]=root;
dfs(root->right);
}
return;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
father[root->val]=NULL;
dfs(root);
while(p)
{
visited[p->val]=true;//从p开始遍历 标记p所有的父节点
cout<<p->val<<endl;
p=father[p->val];
}
while(q)
{
if(visited[q->val])break;//找到父节点
q=father[q->val];
}
return q;
}