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

题目描述:给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

分析:

从宏观上看,对于树中的一个结点,总的路径数包含两部分:

  1. 经过该结点的路径数
  2. 不经过该该结点,即在其子树上的路径
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. 二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 20。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。

输入: 
    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. 二叉树的前序遍历

非递归步骤

  1. 开始根结点入栈
  2. 然后开始循环:栈顶结点出栈,依次将右结点和左结点入栈,直至栈空
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. 二叉树的中序遍历

给定一个二叉树,返回它的中序 遍历。

颜色标记法(作为模板背诵)

  1. 使用颜色标记结点,未访问的结点为白色(用0表示),访问过的为灰色(用1表示)
  2. 如果遇到结点为白色,则将其标记为灰色,然后将右结点、自身、左结点依次入栈
  3. 如果遇到结点为灰色,则输出
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;
}
posted @ 2020-04-16 18:46  咸鱼不闲咋整啊  阅读(279)  评论(0编辑  收藏  举报