LeetCode——树
递归
104. 二叉树的最大深度
题目描述:给定一个二叉树,找出其最大深度。
int maxDepth(struct TreeNode* root){
if(root==NULL) return 0;
int maxLeft=maxDepth(root->left)+1;
int maxRight=maxDepth(root->right)+1;
return maxLeft>maxRight?maxLeft:maxRight;
}
110. 平衡二叉树
题目描述:给定一个二叉树,判断它是否是高度平衡的二叉树。
bool isBalanced(struct TreeNode* root){
return maxDepth(root)!=-1;
}
int maxDepth(struct TreeNode *root){
if(root==NULL) return 0;
int maxLeft=maxDepth(root->left);
if(maxLeft==-1) return -1;
int maxRight=maxDepth(root->right);
if(maxRight==-1) return -1;
int x=abs(maxRight-maxLeft);
if(x>1) return -1;
return (maxLeft>maxRight?maxLeft:maxRight)+1;
}
543. 二叉树的直径
题目描述:给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
分析:直径为左子树最大高度+右子树最大高度,在递归过程中实时更新最大的直径即可
int diameterOfBinaryTree(struct TreeNode* root){
int maxLen=0;
maxDepth(root,&maxLen);
return maxLen;
}
int maxDepth(struct TreeNode* root,int *maxLen){
if(root==NULL) return 0;
int maxLeft=maxDepth(root->left,maxLen);
int maxRight=maxDepth(root->right,maxLen);
int tmpLen=maxLeft+maxRight;
if(tmpLen>*maxLen) *maxLen=tmpLen;
return (maxLeft>maxRight?maxLeft:maxRight)+1;
}
226. 翻转二叉树
题目描述:翻转一棵二叉树。
分析:后序遍历求解
struct TreeNode* invertTree(struct TreeNode* root){
if(root==NULL) return NULL;
struct TreeNode *l=invertTree(root->left);
struct TreeNode *r=invertTree(root->right);
root->left=r;
root->right=l;
return root;
}
617. 合并二叉树
题目描述:给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
分析:
利用前序遍历,用两个指针分别指向两棵树对应位置
递归终止条件:指针到达某一棵树的叶子结点,于是返回另一个结点指针
返回给上一层的信息:返回一棵处理好的根节点
本轮的任务:从宏观来看,本轮要连接下一层返回的两个根节点,组成一棵树,并将当前位置的结点数值相加
struct TreeNode* mergeTrees(struct TreeNode* t1, struct TreeNode* t2){
if(t1==NULL) return t2;
if(t2==NULL) return t1;
t1->val+=t2->val;
t1->left=mergeTrees(t1->left,t2->left);
t1->right=mergeTrees(t1->right,t2->right);
return t1;
}
112. 路径总和
题目描述:给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
bool hasPathSum(struct TreeNode* root, int sum){
if(root==NULL){
return false;
}
if(root->left==NULL&&root->right==NULL&&root->val==sum){//叶子结点,且其值为sum
return true;
}
return hasPathSum(root->left,sum-root->val)||hasPathSum(root->right,sum-root->val);//左右结点都没有路径才返回false
}
437. 路径总和 III
题目描述:给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
分析:
从宏观上看,对于树中的一个结点,总的路径数包含两部分:
- 经过该结点的路径数
- 不经过该该结点,即在其子树上的路径
int pathSumWithRoot(struct TreeNode* root,int sum){
if(root==NULL) return 0;
int ret=0;
if(sum==root->val) ret++;
ret+=pathSumWithRoot(root->left,sum-root->val)+pathSumWithRoot(root->right,sum-root->val);
return ret;
}
int pathSum(struct TreeNode* root, int sum){
if(root==NULL) return 0;
return pathSum(root->left,sum)+pathSum(root->right,sum)+pathSumWithRoot(root,sum);
}
572. 另一个树的子树
题目描述:给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
分析:
双递归求解,外层递归用于遍历s树的每个结点,内层递归用于判断以当前结点为根节点的子树是否与t树相同
内层递归终止条件:两个指针都为NULL,说明这条路径上对应结点值是相同的,返回true,其余情况都为false
bool isSameTree(struct TreeNode *s,struct TreeNode *t){
if(s==NULL&&t==NULL) return true;
if(s==NULL||t==NULL) return false;
if(s->val!=t->val) return false;
return isSameTree(s->left,t->left)&&isSameTree(s->right,t->right);
}
bool isSubtree(struct TreeNode* s, struct TreeNode* t){
if(s==NULL) return false;
return isSameTree(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);
}
101. 对称二叉树
题目描述:给定一个二叉树,检查它是否是镜像对称的。
分析:
首先,如果给定是空树,则必然对称
其次分析,如果有子树,则左子树的左结点等于右子树的右结点,左子树右结点等于右子树左结点
bool child(struct TreeNode* l,struct TreeNode* r){
if(l==NULL&&r==NULL) return true;
if(l==NULL||r==NULL) return false;
if(l->val!=r->val) return false;
return child(l->left,r->right)&&child(l->right,r->left);
}
bool isSymmetric(struct TreeNode* root){
if(root==NULL) return true;
return child(root->left,root->right);
}
111. 二叉树的最小深度
题目描述:给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
分析:
递归终止条件是root为NULL,此时树高为0。当左右子树都存在时,最小深度为左右子树树高最小值+1,如果其中一棵子树为空,则最小深度就要以令一棵子树的最小深度为准
int min(int a,int b){
return a<b?a:b;
}
int minDepth(struct 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;
}
404. 左叶子之和
题目描述:计算给定二叉树的所有左叶子之和。
int sumOfLeftLeaves(struct TreeNode* root){
if(root==NULL) return 0;
int res=0;
if(root->left!=NULL&&root->left->left==NULL&&root->left->right==NULL){
res=root->left->val;
}
return res+sumOfLeftLeaves(root->left)+sumOfLeftLeaves(root->right);
}
687. 最长同值路径
题目描述:给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
int counter(struct TreeNode* root,int* res){
if(root==NULL) return 0;
int l=counter(root->left,res);
int r=counter(root->right,res);
int l1=0,r1=0;
if(root->left!=NULL&&root->val==root->left->val) l1=l+1;
if(root->right!=NULL&&root->val==root->right->val) r1=r+1;
*res=(*res>(l1+r1)?*res:(l1+r1));
return l1>r1?l1:r1;
}
int longestUnivaluePath(struct TreeNode* root){
int res=0;
counter(root,&res);
return res;
}
337. 打家劫舍 III
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
分析:
对于当前结点来说,如果偷了这个结点,则不能偷它的左右结点,如果不偷,则可以偷左右结点
int rob(struct TreeNode* root){
if(root==NULL) return 0;
int val1=root->val;
if(root->left!=NULL) val1+=rob(root->left->left)+rob(root->left->right);
if(root->right!=NULL) val1+=rob(root->right->left)+rob(root->right->right);
int val2=rob(root->left)+rob(root->right);
return val1>val2?val1:val2;
}
后序遍历,从叶子递推到根
void postOrder(struct TreeNode* root){
if(root->left!=NULL) postOrder(root->left);
if(root->right!=NULL) postOrder(root->right);
int res1=0,res2=root->val;
if(root->left!=NULL){
res1+=root->left->val;
if(root->left->left!=NULL) res2+=root->left->left->val;
if(root->left->right!=NULL) res2+=root->left->right->val;
}
if(root->right!=NULL){
res1+=root->right->val;
if(root->right->left!=NULL) res2+=root->right->left->val;
if(root->right->right!=NULL) res2+=root->right->right->val;
}
root->val=res1>res2?res1:res2;
}
int rob(struct TreeNode* root){
if(root==NULL) return 0;
postOrder(root);
return root->val;
}
671. 二叉树中第二小的节点
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2
或 0
。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。
输入:
2
/ \
2 5
/ \
5 7
输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。
分析:可以转化为找左右子树中的最小值,如果左右子树最小值都大于根结点的值,则返回两者中较小的值,其他情况返回最大值
int myFind(struct TreeNode* root,int val){
if(root==NULL) return -1;
if(root->val>val) return root->val;
int l=myFind(root->left,val);
int r=myFind(root->right,val);
if(l>val&&r>val) return l<r?l:r;
return l>r?l:r;
}
int findSecondMinimumValue(struct TreeNode* root){
return myFind(root,root->val);
}
层次遍历
637. 二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> lst = new ArrayList<>();
if(root==null) return lst;
Queue<TreeNode> q=new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
double sum=0;
int cnt=q.size();
for(int i=0;i<cnt;i++){
TreeNode tmp=q.poll();
sum+=tmp.val;
if(tmp.left!=null) q.add(tmp.left);
if(tmp.right!=null) q.add(tmp.right);
}
lst.add(sum/cnt);
}
return lst;
}
}
513. 找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值。
BFS:
使用层序遍历,遍历顺序为自右向左,最后一个结点即为所求
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
root=q.poll();
if(root.right!=null) q.add(root.right);
if(root.left!=null) q.add(root.left);
}
return root.val;
}
}
DFS:
void dfs(struct TreeNode* root,int depth,int* maxDepth,int* ret){
if(root==NULL) return;
if(root->left==NULL&&root->right==NULL){
if(depth>=*maxDepth){
*maxDepth=depth;
*ret=root->val;
}
return;
}
dfs(root->right,depth+1,maxDepth,ret);
dfs(root->left,depth+1,maxDepth,ret);
}
int findBottomLeftValue(struct TreeNode* root){
int maxDepth=-1,ret;
dfs(root,0,&maxDepth,&ret);
return ret;
}
前中后序遍历
144. 二叉树的前序遍历
非递归步骤
- 开始根结点入栈
- 然后开始循环:栈顶结点出栈,依次将右结点和左结点入栈,直至栈空
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> st=new Stack<>();
List<Integer> lst=new ArrayList<>();
if(root==null) return lst;
st.push(root);
while(!st.isEmpty()){
TreeNode tmp=st.pop();
lst.add(tmp.val);
if(tmp.right!=null) st.push(tmp.right);//先右后左,保证先序遍历
if(tmp.left!=null) st.push(tmp.left);
}
return lst;
}
}
94. 二叉树的中序遍历
给定一个二叉树,返回它的中序 遍历。
颜色标记法(作为模板背诵)
- 使用颜色标记结点,未访问的结点为白色(用0表示),访问过的为灰色(用1表示)
- 如果遇到结点为白色,则将其标记为灰色,然后将右结点、自身、左结点依次入栈
- 如果遇到结点为灰色,则输出
class Solution {
class ColorNode{
TreeNode node;
int color;
public ColorNode(TreeNode node,int color){
this.node=node;
this.color=color;
}
}
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> lst=new ArrayList<>();
Stack<ColorNode> st=new Stack<>();
if(root==null) return lst;
st.push(new ColorNode(root,0));
while(!st.isEmpty()){
ColorNode tmp=st.pop();
TreeNode treeTmp=tmp.node;
if(tmp.color==0){
if(treeTmp.right!=null) st.push(new ColorNode(treeTmp.right,0));
st.push(new ColorNode(treeTmp,1));
if(treeTmp.left!=null) st.push(new ColorNode(treeTmp.left,0));
}else{
lst.add(treeTmp.val);
}
}
return lst;
}
}
145. 二叉树的后序遍历
给定一个二叉树,返回它的 后序 遍历。
class Solution {
class ColorNode{
int color;
TreeNode node;
public ColorNode(TreeNode node,int color){
this.node=node;
this.color=color;
}
}
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> lst=new ArrayList<>();
Stack<ColorNode> st=new Stack<>();
if(root==null) return lst;
st.push(new ColorNode(root,0));
while(!st.isEmpty()){
ColorNode tmp=st.pop();
if(tmp.color==0){
st.push(new ColorNode(tmp.node,1));
if(tmp.node.right!=null) st.push(new ColorNode(tmp.node.right,0));
if(tmp.node.left!=null) st.push(new ColorNode(tmp.node.left,0));
}else{
lst.add(tmp.node.val);
}
}
return lst;
}
}
BST
669. 修剪二叉搜索树
给定一个二叉搜索树,同时给定最小边界L 和最大边界R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
struct TreeNode* trimBST(struct TreeNode* root, int L, int R){
if(root==NULL) return NULL;
if(root->val>R) return trimBST(root->left,L,R);
if(root->val<L) return trimBST(root->right,L,R);
root->left=trimBST(root->left,L,R);
root->right=trimBST(root->right,L,R);
return root;
}
230. 二叉搜索树中第K小的元素
给定一个二叉搜索树,编写一个函数 kthSmallest
来查找其中第 k 个最小的元素。
算法1:中序遍历
BST的中序序列即为元素的升序
void inOrder(struct TreeNode* root,int k,int* cnt,int* ret){
if(root==NULL) return;
inOrder(root->left,k,cnt,ret);
(*cnt)++;
if(*cnt==k){
*ret=root->val;
return;
}
inOrder(root->right,k,cnt,ret);
}
int kthSmallest(struct TreeNode* root, int k){
int cnt=0,ret=0;
inOrder(root,k,&cnt,&ret);
return ret;
}
算法2:递归
统计左子树的结点数,如果恰好为k-1,则该结点即为所求,>k-1时,说明结点在左子树上,<k-1时,说明结点在右子树上
int count(struct TreeNode* root){
if(root==NULL) return 0;
return count(root->left)+count(root->right)+1;
}
int kthSmallest(struct TreeNode* root, int k){
int cnt=count(root->left);
if(cnt==k-1) return root->val;
if(cnt>k-1) return kthSmallest(root->left,k);
return kthSmallest(root->right,k-cnt-1);
}
538. 把二叉搜索树转换为累加树
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
输入: 原始二叉搜索树:
5
/ \
2 13
输出: 转换为累加树:
18
/ \
20 13
分析:要想找出所有大于本结点的值,应该采用"右中左"的顺序遍历,这样得到的序列为降序
void traver(struct TreeNode* root,int* sum){
if(root==NULL) return;
traver(root->right,sum);
*sum+=root->val;
root->val=*sum;
traver(root->left,sum);
}
struct TreeNode* convertBST(struct TreeNode* root){
int sum=0;
traver(root,&sum);
return root;
}
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if(root->val<p->val&&root->val<q->val) return lowestCommonAncestor(root->right,p,q);
if(root->val>p->val&&root->val>q->val) return lowestCommonAncestor(root->left,p,q);
return root;
}
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if(root==NULL) return NULL;
if(root==p||root==q) return root;
struct TreeNode* l=lowestCommonAncestor(root->left,p,q);
struct TreeNode* r=lowestCommonAncestor(root->right,p,q);
if(l!=NULL&&r!=NULL) return root;
if(l==NULL&&r==NULL) return NULL;
return l==NULL?r:l;
}