leetcode二叉树刷题总结

LeetCode Hot100 刷题笔记(4)—— 二叉树、图论 一、二叉树:二叉树的中序遍历,二叉树的最大深度,翻转二叉树,对称二叉树,二叉树的直径,二叉树的层序遍历,将有序数组转换为二叉搜索树,验证二叉搜索树,二叉搜索树中第 K 小的元素,二叉树的右视图,二叉树展开为链表,从前序与中序遍历序列构造二叉树,路径总和 III,二叉树的最近公共祖先,二叉树中的最大路径和。 二、图论:岛屿数量,腐烂的橘子,课程表,实现 Trie (前缀树)。 阅读详情

文章目录

一. 基础理论

1. 刷题大纲

在这里插入图片描述

2. 二叉树的定义

public class TreeNode {
    int val;
  	TreeNode left;
  	TreeNode right;
  	TreeNode() {}
  	TreeNode(int val) { this.val = val; }
  	TreeNode(int val, TreeNode left, TreeNode right) {
    		this.val = val;
    		this.left = left;
    		this.right = right;
  	}
}

3. 二叉树的分类

满二叉树

如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k,有2^k-1个节点的二叉树。

完全二叉树

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。

二叉搜索树

有序树、左小右大

平衡二叉搜索树

是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

4. 二叉树的遍历方式

个人体会
二叉树的题目就是选择正确的遍历方式,再加上题目的要求,以及某些特殊二叉树的性质,去解题。

4.1 深度优先遍历

  • 前序遍历
  • 中序遍历
  • 后序遍历

4.2 广度优先遍历

层序遍历
相关题目 推荐
102.二叉树的层序遍历
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度

二. 二叉树的遍历方式

1. 递归法

终止条件: null return
传递参数: root result
单层逻辑: 按一定顺序处理 遇到处理节点 add

T144. 前序遍历

中左右 经常有回溯的味道

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root,result);
        return result;
    }
    public void preorder(TreeNode root,List<Integer> result){
        if (root == null){return;}
        result.add(root.val);
        preorder(root.left,result);
        preorder(root.right,result);
    }

}

T145. 后序遍历

左右中

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer> ();
        postorder(root,result);
        return result;
    }
    public void postorder(TreeNode root,List<Integer> result){
        if (root == null){return;}
        postorder(root.left,result);
        postorder(root.right,result);
        result.add(root.val);

    }   
}

T94. 中序遍历

左中右

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer> ();
        inorder(root,result);
        return result;
    }
    public void inorder(TreeNode root,List<Integer> result){
        if (root == null){return;}
        inorder(root.left,result);
        result.add(root.val);
        inorder(root.right,result);
    }
}

2. 迭代法

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
因此可以利用栈实现二叉树的遍历。
总结
弄清楚入栈和出栈顺序,动画参考添加链接描述
前序和后序逻辑一致,后序需要再做一个反转操作。
中序遍历,访问和处理不一致(深刻理解这句话), 引入指针进行判断
若分析不清楚 可画图帮助分析

T144. 前序遍历

遍历顺序: 中左右
入栈: 中右左
出栈: 中左右

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<>();//创建一个栈
        if (root == null){return result;}
        stack.push(root);//把根节点入栈
        while (!stack.isEmpty() ){//只要栈不为空
            TreeNode temp = stack.pop();
            result.add(temp.val);//取出栈的最后一个元素 
            if (temp.right != null){
                stack.push(temp.right);//右
            }
            if (temp.left != null){
                stack.push(temp.left);//左
            }
        }
        return result;
    }
}

T145. 后序遍历

遍历顺序: 左右中
在前序基础上左,入栈 中左右 ,出栈为 中右左,反转为 左右中

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){return result;}
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode temp = stack.pop();
            result.add(temp.val);
            if (temp.left != null){
                stack.push(temp.left);
            }
            if( temp.right != null){
                stack.push(temp.right);
            }
        }
        Collections.reverse(result);
        return result;
    } 
}

T94. 中序遍历

遍历顺序:左中右
但是先处理的是中节点,先访问的是左节点,造成处理顺序和访问顺序是不一致的。
root就是中节点,想办法解决访问和处理不一致的问题,故引入指针cur,进行判断,只要左空了,那就处理中,再访问右

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer> ();
        Stack<TreeNode> stack = new Stack<>();
        if (root==null){return result;}
        TreeNode cur = root;
        //分别对左右子树是否为空进行判断
        //先把左树放进栈里,若左空了就弹出,开始处理中之后,访问右
        while (cur != null || !stack.isEmpty()){
            if (cur != null){
                stack.push(cur);
                cur = cur.left;//访问左
            }
            else{//左子树空了就把stack弹出
                cur = stack.pop();
                result.add(cur.val);//处理中
                cur = cur.right;//访问右
            }
        }
        return result;
    }
}

3. 统一迭代法

前面的迭代法,写法风格不一致,为了解决该问题,提出以下方法
将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记
取出栈的最后一个元素,遇到null,则先删除null,再删除最后一个元素,并将其值放入result,不是null,则根据遍历顺序放入null

T94. 中序遍历

//遍历顺序:左中右
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer> ();
        Stack<TreeNode> stack = new Stack<>();
        if (root!=null){
        stack.push(root);}
        while (!stack.isEmpty()){
            TreeNode node = stack.peek();
            if (node != null){
                stack.pop();
                if(node.right!=null){stack.push(node.right);}//右
                stack.push(node);
                stack.push(null);//未处理的加null标记
                if(node.left != null) {stack.push(node.left);}//左
            }
            else{
                stack.pop();//删除空白后处理
                node = stack.peek();
                stack.pop();
                result.add(node.val);
            }
        }
        return result;
    }
}

4. 层序遍历

在这里插入图片描述

4.1 迭代法

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑
两个while循环 一个对整个队列判断,另外一个判断每一层,以len记录每一层的元素个数,temp变量记录每一层的结果,入队顺序为左右

class Solution {
    //迭代法
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (root == null){return result;}
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        while( !que.isEmpty()){
            List<Integer> temp = new ArrayList<Integer>();//每一层的变量
            int len = que.size();//每一层的个数
            //每一层的逻辑
            while (len > 0){
                TreeNode node = que.poll();
                temp.add(node.val);
                if(node.left != null){que.offer(node.left);}
                if(node.right != null){que.offer(node.right);}
                len--;
            }
            //每一层装完后加到result
            result.add(temp);
        }
        return result;
    }
}

4.2 递归法

单层逻辑: 记录深度,添加结果值
访问顺序:左右

class Solution {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        dfsOrder(root,0);
        return result;
    }
    public void dfsOrder(TreeNode root,Integer deep){
        if (root == null){return;}//递归终止条件
        //单层逻辑
        deep++; //遍历一次 深度加一
        if (result.size()<deep){
            List<Integer> temp = new ArrayList<Integer>();
            result.add(temp);
        }//给结果加上层次
        result.get(deep-1).add(root.val);
        dfsOrder(root.left,deep);
        dfsOrder(root.right,deep);
    }
}

三、二叉树的属性

T101. 对称二叉树 (后序遍历)

在这里插入图片描述

  1. 传入左右节点,返回布尔值
  2. 终止条件:根据左右节点的值 分类返回true或者false
  3. 单层逻辑:处理外侧、内侧(注意处理的节点)
    本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compareHelp(root.left,root.right);
    }
    public boolean compareHelp(TreeNode leftNode,TreeNode rightNode){
        if (leftNode == null && rightNode != null){return false;}
        if (leftNode != null && rightNode == null){return false;}
        if (leftNode == null && rightNode == null){return true;}
        if (leftNode.val != rightNode.val){return false;}

        boolean compareLeft = compareHelp(leftNode.left,rightNode.right);
        boolean compareRight = compareHelp(leftNode.right,rightNode.left);
        return compareLeft&&compareRight;
    }
}

相关题目

100.相同的树
572.另一个树的子树

T104. 二叉树的最大深度(后序遍历、层序)

在这里插入图片描述
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)
而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。

  • 本人更喜欢用层序遍历,因为有多少层就是深度
class Solution {//层序遍历
    public int maxDepth(TreeNode root) {
        if( root == null) {return 0;}
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        int depth = 0;
        while (!que.isEmpty()){
            int length = que.size();
            while (length>0){
                TreeNode node = que.poll();
                if(node.left != null) {que.offer(node.left);}
                if (node.right != null) {que.offer(node.right);}
                length--;
            }
            depth++;
        }
        return depth;
    }
}
  • 前序遍历
    有回溯的味道
 //前序遍历
 //从上到下 
class Solution {
    int res;
    public int maxDepth(TreeNode root) {
        res = 0;
        if(root == null ){return 0;}
        getDepth(root,1);//深度从1开始
        return res;
    }
    public void getDepth(TreeNode root,int depth){
        res = Math.max(depth,res);//处理中节点
        if(root.left==null&&root.right==null){
            return; //终止条件
        }
        if(root.left!=null){
            depth++;
            getDepth(root.left,depth);
            depth--;
        }
        if(root.right!=null){
            depth++;
            getDepth(root.right,depth);
            depth--;
        }  
        return;
    }
}

相关题目

559. n叉树的最大深度
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/
    //层序遍历 多少层即深度
class Solution {
    public int maxDepth(Node root) {
        if(root == null) {return 0;}   
        int depth = 0;//总共有多少层
        Queue<Node> que = new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()){
            int length = que.size();//每一层的大小
            depth++;
            while(length>0){
                Node temp = que.poll();
                for(int i = 0;i<temp.children.size();i++){
                    if(temp.children.get(i)!=null){
                        que.offer(temp.children.get(i));
                    }
                }
                length--;
            }
        } 
        return depth;
    }
}

T111. 二叉树的最小深度(后序遍历)

在这里插入图片描述
求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。
在这里插入图片描述

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null){return 0;}
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null){return rightDepth+1;} //与最大深度不同之处
        if (root.right == null) {return leftDepth+1;}
        return Math.min(leftDepth,rightDepth)+1;
    }
}
//对比最大深度
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {return 0;}
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth,rightDepth)+1;
    }
}

T222. 完全二叉树的节点个数(后序、层序、完全二叉树的性质)

在这里插入图片描述

  • 方法一: 后序遍历
class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) {return 0;}
        int leftNum = countNodes(root.left);
        int rightNum = countNodes(root.right);
        int num = leftNum + rightNum +1;
        return num;
    }
}
  • 方法二: 层序遍历法
class Solution {
    public int countNodes(TreeNode root) {
        int num = 0;
        if (root == null) {return num;}
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        while( !que.isEmpty()){
            int length = que.size();
            while(length > 0){
                TreeNode node = que.poll();
                num++;
                if(node.left != null){que.offer(node.left);}
                if(node.right != null) {que.offer(node.right);}
                length--;
            }
        }
        return num;
    }
}
  • 方法三: 利用完全二叉树的特点(后序遍历)
    完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
    对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
    对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算
    在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。
class Solution {
    public int countNodes(TreeNode root) {
        if (root == null){return 0;}
        int leftDepth = 0;
        int rightDepth = 0;
        TreeNode left = root.left;
        TreeNode right = root.right;
        while (left != null){
            left = left.left;
            leftDepth++;
        }
        while (right != null){
            right = right.right;
            rightDepth++;
        }
        //由于完全二叉树特点,左右深度一致必然为满二叉树
        if (leftDepth == rightDepth){return (2<<leftDepth) - 1;}//终止条件
        //不断地遍历必然会出现满二叉树
        int leftNum = countNodes(root.left); //左
        int rightNum = countNodes(root.right); //右
        int res = leftNum + rightNum + 1;   //中
        return res;
    }
}

T110. 平衡二叉树(高度问题)(后序遍历)

在这里插入图片描述

  • 深度和高度的区别
    前面求深度的时候,我们把它当作求根节点的高度了 故都用了后序遍历
    后序遍历能够从下到上 前序是从上到下
    在这里插入图片描述
    在这里插入图片描述

  • 代码实现(后序遍历)

class Solution {
    public boolean isBalanced(TreeNode root) {
        return getHight(root) != -1; //高度大于1返回-1
    }
    public int getHight(TreeNode root){//后序遍历
        if (root == null) {return 0;}
        int leftHigh = getHight(root.left);//左
        if (leftHigh == -1){return -1;}//一个子树不符合,整体不符合
        int rightHigh = getHight(root.right);//右
        if (rightHigh == -1) {return -1;}
        if (Math.abs(leftHigh-rightHigh)>1){return -1;}
        return Math.max(leftHigh,rightHigh) + 1;
    }
}

T257. 二叉树的所有路径 (前序遍历)

在这里插入图片描述

  • 从下到上 采用前序遍历,同时需要回溯思想
  • 做题思路
    需要所有路径,且从题目可以看出要从上到下遍历,因此选择前序遍历
  • 递归三部曲:
    参数和返回值:path和root,返回空
    终止条件:左右都空
    单层处理逻辑: 把值加入res,左右节点
class Solution {
    List<String> res = new ArrayList<String>();
    public List<String> binaryTreePaths(TreeNode root) {
        if (root == null){
            return res;
        }
        List<Integer> path = new ArrayList<Integer>();
        pathHelper(root,path);
        return res;
    }
    public void pathHelper(TreeNode root,List<Integer> path){
        //处理中节点
        path.add(root.val);
        if(root.left==null && root.right==null){//终止条件
            StringBuilder sb = new StringBuilder();
            for(int i = 0;i<path.size()-1;i++){
                sb.append(path.get(i)).append("->");
            }
            sb.append(path.get(path.size()-1));
             //如何构建字符串:先用一个list把val加进来,然后每个值中间插入->
            res.add(sb.toString());
        }
        //左
        if(root.left!=null){
            pathHelper(root.left,path);
            path.remove(path.size()-1);
        }
        //右
        if(root.right!=null){
            pathHelper(root.right,path);
            path.remove(path.size()-1);
        }
    }
}

T112. 路径总和 (前序)

在这里插入图片描述

  • 问题分析
    参数和返回值:根节点和计算器,布尔值
    终止条件:找到了叶节点且count为0
    单层逻辑:处理count;处理左节点;处理右节点
    注意只有左节点不是空才处理,如果得到了true及时返回不需要继续遍历
  • 代码实现
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {return false;}
        return pathHelper(root,targetSum);
    }
    public boolean pathHelper(TreeNode root,int count){
        count = count - root.val;
        if (root.left == null && root.right == null && count == 0){
            return true;//终止条件
        }
        if (root.left != null){//左不为空
            boolean left = pathHelper(root.left,count);
            if(left) {return true;}//找到了
        }
        if (root.right != null){
            boolean right = pathHelper(root.right,count);
            if(right) {return true;}
        }
        return false;
    }
}

T113. 路径总和Ⅱ

在这里插入图片描述

  • 与T112对比
    需要找出所有路径,因此需要回溯
    要返回具体路径,需要List<List<Integer>> 来保存结果
  • 代码实现
class Solution {
    int sum = 0;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null){return res;}
        LinkedList<Integer> path = new LinkedList<>();
        pathHelper(root,targetSum,res,path);
        return res;
    }
    //前序遍历 中左右
    public void pathHelper(TreeNode root,int targetSum,List<List<Integer>> res,LinkedList<Integer> path){
        path.add(root.val);
        sum += root.val;
        if(root.left == null && root.right == null){//找到叶子节点
            if(sum == targetSum){
                res.add(new ArrayList<>(path));//和为目标值 就把结果加进去
            }
            return;
        }
        if(root.left != null){
            pathHelper(root.left,targetSum,res,path);
            int leftVal = path.removeLast();
            sum -= leftVal;
        }
        if(root.right != null){
            pathHelper(root.right,targetSum,res,path);
            int rightVal = path.removeLast();
            sum -= rightVal;
        }
    }
}

T404. 左叶子之和 (后序遍历)

在这里插入图片描述

  • 做题思路
    节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
    递归顺序:由于要取递归函数的返回值求和 所以采用左右中 (这么做的话,在处理中的时候已经有了左和右两个值)
    终止条件:空节点
    单层逻辑:需要处理判断是否为左叶子的逻辑,进行加和
  • 代码实现
//后序遍历:返回和
class Solution {
    int sum = 0;
    public int sumOfLeftLeaves(TreeNode root) {
        return sumHelper(root,0);
    }
    public int sumHelper(TreeNode root,int sum){
        if(root==null){return 0;}
        int leftSum = sumHelper(root.left,sum);//左
        int rightSum = sumHelper(root.right,sum);//右
        if(root.left!=null&&root.left.left==null&&root.left.right==null){//判断是否为左叶子
            sum += root.left.val;
        }
        return leftSum+rightSum+sum;//处理中
    }
}

T513. 找树左下角的值(层序、前序)

在这里插入图片描述

  • 方法一:层序遍历
    最后一层的第一个元素
    get直接获取元素
class Solution {
    public int findBottomLeftValue(TreeNode root) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        while (!que.isEmpty()){
            List<Integer> path = new ArrayList<Integer>();
            int len = que.size();
            while(len > 0){//每一层的遍历
                TreeNode node = que.poll();
                path.add(node.val);
                if (node.left != null){ que.offer(node.left);}
                if (node.right != null){ que.offer(node.right);}
                len--;
            }
            result.add(path);
        }
        int value = result.get(result.size()-1).get(0);
        return value;
    }
}
  • 方法二:前序遍历(不好理解)
    最大深度的第一个元素
 //前序遍历
 //当到达最大深度时,取第一个元素
class Solution {
    int value = 0;
    int Deep = -1;
    public int findBottomLeftValue(TreeNode root) {
        findHelper(root,0);
        return value;
    }
    public void findHelper(TreeNode root,int deep){
        if(root==null){return ;}
        if(root.left == null && root.right==null){
            if(deep>Deep){
                value = root.val;
                Deep = deep;
            }
        }
        if(root.left != null){findHelper(root.left,deep+1);}
        if(root.right != null){findHelper(root.right,deep+1);}
    }
}

T543. 二叉树的直径

在这里插入图片描述

class Solution {
    int res;
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return res-1;
    }   
    public int dfs(TreeNode node){
        if(node == null) return 0;
        int left = dfs(node.left);
        int right = dfs(node.right);
        res = Math.max(res,left+right+1);
        return Math.max(left,right)+1;
    }
}

四、二叉树的修改与构造

T226. 翻转二叉树(前序、后序)

在这里插入图片描述

  • 参数:根节点,返回值:空
  • 终止条件:根节点为空
  • 单层逻辑:翻转左、右
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null ) {return null;}
        swap(root);
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
    public void swap(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

T106. 从中序与后序遍历序列构造二叉树(前序)

在这里插入图片描述
- 思路
找到后序数组最后一个元素在中序数组的位置,作为切割点
切割中序数组,切成中序左数组和中序右数组
切割中序数组,切成中序左数组和中序右数组
递归处理左区间和右区间

  • 代码
class Solution {
    Map<Integer,Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for (int i = 0;i<inorder.length;i++){
            map.put(inorder[i],i);//存放中序数组
        }
        //区间遵循左闭右闭准则
        return buildHelp(inorder,0,inorder.length-1,postorder,0,postorder.length-1);
    }
    public TreeNode buildHelp(int[] inorder,int inBegin,int inEnd,int[] postorder,int postBeigin,int postEnd){
        if (inBegin > inEnd || postBeigin > postEnd){return null;}//终止条件
        //单层逻辑
        int val = postorder[postEnd];//根节点的值
        TreeNode root = new TreeNode(val);
        int rootIndex = map.get(val);//根节点在中序数组的序号,
        int lengthLeft = rootIndex - inBegin;//左子树的长度
        root.left = buildHelp(inorder,inBegin,rootIndex-1,
                                postorder,postBeigin,postBeigin+lengthLeft-1);//处理左子树
        root.right = buildHelp(inorder,rootIndex+1,inEnd,
                                postorder,postBeigin+lengthLeft,postEnd-1);
        return root;
    }
}

T105. 从前序与中序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private Map<Integer, Integer> indexMap;

    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return null;
        }

        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);
        
        // 先把根节点建立出来
        TreeNode root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造哈希映射,帮助我们快速定位根节点
        indexMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
}


T654. 最大二叉树 (前序遍历)

在这里插入图片描述

  • 思路
    构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树
    参数:数组,起始和终止坐标;返回值:节点
    终止条件:数组空
    单层逻辑:找最大值,构造节点,处理左、右
  • 代码
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return constructHelper(nums,0,nums.length-1);//左闭右闭
    }
    public TreeNode constructHelper(int[] nums,int start,int end){
        if(end - start < 0 ){
            return null;}//终止条件
        
        int maxIndex = start;
        int maxValue = nums[maxIndex];
        for (int i =start+1;i<=end;i++){
            if(nums[i]>maxValue){
                maxValue=nums[i];
                maxIndex=i;}
        }//找最大值及坐标
        TreeNode root = new TreeNode(maxValue);//根节点
        root.left = constructHelper(nums,start,maxIndex-1);
        root.right = constructHelper(nums,maxIndex+1,end);
        return root;
    }
}

T617. 合并二叉树 (前序操作)

在这里插入图片描述

  • 思路
    同时遍历两个树:其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作
    参数:两个根节点;返回值:相加后的根节点
    终止条件:其中一个根节点为空
    单层逻辑:1和2相加,处理左、右
  • 代码
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        //终止条件:哪个有值就返回哪个
        if (root1 == null){return root2;}
        if (root2 == null){return root1;}
        //单层逻辑:前序遍历
        root1.val += root2.val;
        root1.left = mergeTrees(root1.left,root2.left);
        root1.right = mergeTrees(root1.right,root2.right);
        return root1;
    }
}

五、二叉搜索树的属性

T98. 验证二叉搜索树 (中序遍历)*

在这里插入图片描述

  • 思路分析
    中序遍历实现了从小到大,一直比较,若出现大于当前值,那就返回false
  • 代码实现
class Solution {
    TreeNode max;
    public boolean isValidBST(TreeNode root) {        
        if (root==null){return true;}
        boolean left = isValidBST(root.left);
        if(!left){return false;}
        if (max != null && max.val>=root.val ){
            return false;
        }
        max = root;
        boolean right = isValidBST(root.right);
        if(!right){return false;}
        return true;
    }
}

T700. 二叉搜索树中的搜索(前序遍历)

在这里插入图片描述

  • 思路分析
    注意与普通树的区别,利用其特点,可以减少递归次数
  • 代码实现
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null || root.val ==val){
            return root;
        }
        if (root.val>val){
            return searchBST(root.left,val);
        }
        else {
            return searchBST(root.right,val);
        }
    }
}

T530. 二叉搜索树的最小绝对差 (中序遍历)

在这里插入图片描述
- 思路
把二叉树变成数组,再求
直接用pre记录上一个节点,进行计算
中序遍历:从小到大
技巧:用一个pre指针指向前一个元素
- 代码

 //中序遍历出来是递增序列
class Solution {
    int result = Integer.MAX_VALUE;
    TreeNode temp;//记录上一个节点
    public int getMinimumDifference(TreeNode root) {
        if (root==null){return 0;}
        getHelper(root);
        return result;
    }
    public void getHelper(TreeNode root){
        if (root==null){return;}
        getHelper(root.left);//left
        if (temp != null){
            result = Math.min(result,root.val-temp.val);
        }
        temp = root;
        getHelper(root.right);
    }
}

T501. 二叉搜索树中的众数 (中序遍历)

在这里插入图片描述

  • 思路
    普通二叉树,进行遍历,用一个map统计所有频率,统计完后根据value排序
    二叉搜索树,中序遍历出来就是一个有序数组,只要比较前面的元素即可;
    稍微复杂一点的地方在于,对节点的处理逻辑:
    传进来一个节点,判断是否第一次出现,若是count为1;若不是,count加1。
    对count处理,如果是最大频率,那么把结果加入;若大于最大频率,更新最大频率,并且把结果清空,加入新的元素;更新指针
  • 代码
class Solution {
//指针+记录频率变量
    int count = 0;//用来计数
    int maxCount = 0;//最大频率
    ArrayList<Integer> result = new ArrayList<>();//存放结果
    TreeNode prev = null;//引入指针 指向前一个元素
    public int[] findMode(TreeNode root) {
        findHelper(root);
        int[] res = new int[result.size()];
        for(int i=0;i<result.size();i++){
            res[i]=result.get(i);
        }
        return res;
    }
    public void findHelper(TreeNode root){
        if(root==null){return;}
        findHelper(root.left);//处理左树
        
        //记录count
        if (prev == null|| prev.val != root.val) { count=1; }
        else if (prev.val == root.val){count++;}

        //判断count
        if(count==maxCount){
            result.add(root.val);
        }
        else if(count>maxCount){
            maxCount=count;
            result.clear();
            result.add(root.val);
        }
        prev = root;//指针移动

        findHelper(root.right);
    }
}

T538. 把二叉搜索树转换为累加树 (反中序遍历)

在这里插入图片描述

  • 思路分析
    中序遍历:得到递增的序列,对于序列,就是等于后面的元素之和
    如果采用右中左的顺序,那就可以转换为累加求和了
  • 代码
class Solution {
    int sum = 0;
    public TreeNode convertBST(TreeNode root) {
        convertHelper(root);
        return root;
    }
    public TreeNode convertHelper(TreeNode root){
        if (root == null){return null;}
        //右中左
        convertHelper(root.right);
        sum = sum + root.val;
        root.val = sum;
        convertHelper(root.left);
        return root;

    }
}

六、二叉树的公共祖先问题

T236. 二叉树的最近公共祖先 (后序遍历)

在这里插入图片描述
在这里插入图片描述

  • 思路分析
    什么叫最近公共祖先?深度要大,即要尽可能在树的底部;
    自然而然,想到自下而上搜索->后序遍历
    递归参数和返回值:三个节点,找到p和q就返回
  • 代码实现
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //终止条件
        if (root==null){return null;}
        if (root==p||root==q){return root;}//返回值必然是p或者q
        //遍历左右
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        //处理中节点
        if (left ==null && right == null) {return null;}
        else if (left != null && right == null){return left;}//p和q在左树上
        else if (left  == null && right != null){return right;}
        else{return root;}
        
    }
}

T235. 二叉搜索树的最近公共祖先 (前序遍历)

在这里插入图片描述

  • 思路分析
    与上一题区别在于,二叉搜索树有序。因此本题不需要从下到上遍历
    ,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。且一定是最近公共祖先(可以画图分析)
  • 代码实现
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val>q.val){return lowestCommonAncestor(root.left,p,q);}
        if (root.val<p.val&&root.val<q.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;       
    }
}

七、二叉搜索树的修改与构造

T701. 二叉搜索树的插入操作(前序)

在这里插入图片描述
在这里插入图片描述

  • 思路分析
    只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。
    因为要处理中节点,所以采取前序遍历。
  • 代码实现
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root == null){
            TreeNode node = new TreeNode(val);
            return node;
        }
        //BST往往不需要遍历整个树
        if(root.val>val){root.left=insertIntoBST(root.left,val);}
        if(root.val<val){root.right=insertIntoBST(root.right,val);}
        return root;
    }
}

T450. 删除二叉搜索树的节点 (前序遍历)

在这里插入图片描述

  • 思路分析
    需要对传进来的节点进行判断,采用前序遍历(中左右)
    二叉搜索树不需要遍历整个树,判断左右大小即可
    难点在于: 处理逻辑四种情况,最复杂的是左右都存在
  • 代码实现
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
    //终止条件
    if (root == null){return null;}
    //找到了节点
    if (root.val == key){
        //1.左右树都为空 直接删除
        if (root.left == null && root.right == null){
            return null;
        }
        //2.左为空,右不为空:
        if (root.left == null && root.right!=null){
            return root.right;
        }
        //3.左不为空,右为空:
        if (root.left != null && root.right == null){
            return root.left;
        }
        //4.左右均非空:把左孩子移到右子树最左面节点的做孩子上
        else if(root.left!=null&&root.right!=null){
            TreeNode node = root.right;
            while(node.left!=null){
                node = node.left;
            }
            node.left = root.left;
            root = root.right;
            return root;
        }
    }
    if (root.val > key){
        root.left = deleteNode(root.left,key);
    }
    if (root.val < key){
        root.right = deleteNode(root.right,key);
    }
    return root;
    }
}

T669. 修剪二叉搜索树(前序遍历)

在这里插入图片描述

  • 思路分析
    在这里插入图片描述
    删除0,把2赋给节点,因此递归函数需要返回值
    前序遍历,对中节点处理
  • 代码实现
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null){return null;}
        //单层逻辑
        if (root.val<low){//小于最小,遍历右树
            return trimBST(root.right,low,high);
        }
        if (root.val>high){
            return trimBST(root.left,low,high);
        }
        //处于区间之中
        root.left = trimBST(root.left,low,high);
        root.right = trimBST(root.right,low,high);
        return root;

    }
}

T108. 将有序数组转换为二叉搜索树

在这里插入图片描述

  • 思路分析
    核心思路: 数组的中间节点作为根节点,左区间作为左树,右区间作为右树
    递归函数的参数: 数组,开始索引,结束索引
  • 代码实现
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode node = sortedHelper(nums,0,nums.length-1);
        return node;
    }
    public TreeNode sortedHelper(int[] nums,int left,int right){
        if(left>right){return null;}
        int mid = left + ((right-left) >> 1);
        TreeNode node = new TreeNode(nums[mid]);
        node.left = sortedHelper(nums,left,mid-1);
        node.right = sortedHelper(nums,mid+1,right);
        return node;
    }
}
力扣树专题-6 广度优先搜索 刷熟一个模板(层序遍历打印二叉树)秒杀一堆问题leetcode102 111 116 617 java刷题笔记 广度优先搜索算法BFS专题 根据一个模板 秒杀其他题目 阅读详情

相关推荐

UWB定位进阶:如何利用DW1000的CIR数据做NLOS信号识别?

本文深入探讨了如何利用DW1000芯片的信道脉冲响应(CIR)数据进行非视距(NLOS)信号识别,以提升UWB定位精度。通过分析CIR数据的核心价值、预处理方法、特征提取技术以及机器学习模型构建,为研究人员和工程师提供了实用的解决方案,帮助克服室内定位中的NLOS挑战。

weixin_28729205的博客 474

二叉树前序中序后序javascript_力扣刷题之二叉树

力扣刷题学习路线力扣刷题之递归力扣刷题之DFS力扣刷题之BFS力扣刷题之动态规划力扣刷题之Tree力扣刷题之HashMap力扣刷题之Heap力扣刷题之Stack力扣刷题之Graph力扣刷题之Sort力扣刷题之二分查找力扣刷题之并查集二叉树本章主要讲解经典二叉树遍历实现(非递归):前序遍历,中序遍历,后序遍历。前序遍历(中左右)是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后...

weixin_36033417的博客 144

告别轮询!用STM32CubeMX给STM32F030配置ADC多通道+DMA,实测代码分享

本文详细介绍了如何使用STM32CubeMX为STM32F030配置ADC多通道采集与DMA传输,显著提升采集效率。通过实测对比轮询与DMA模式的性能差异,提供完整的代码实现和优化技巧,帮助开发者快速掌握这一高效采集方案。

weixin_42570296的博客 409

Leetcode刷题笔记——二叉树篇

本文给大家总结了二叉树中的DFS,二叉树本身就是一种递归定义的数据结构,所以二叉树和DFS是紧密相关的,其中的递归思想又诞生了很多衍生题,比如大多数的网格问题都可以看成是二叉树问题的一种变体【四叉结构】,这不是靠死记硬背就能解决的,需要大家在熟悉二叉树的性质的同时对类相关知识点的串联分析,这正好是很多大厂面试中面试官特别喜欢考察的面试者的点,最后,新的一年祝大家学习,工作一切顺利,找到心仪的offer,跳槽涨薪double,迎娶白富美,早日走向人生巅峰~

qq_42898642的博客 1249

告别手动归档:基于Allegro Skill的XTools一键生成Gerber与生产文件实践

本文详细介绍了基于Allegro Skill的XTools工具如何实现一键生成Gerber与生产文件,大幅提升PCB设计效率。通过自动化流程,解决了传统手动归档中的重复劳动、人为失误和标准不统一等问题,特别适合需要频繁输出生产文件的工程师。文章包含安装配置、实战操作、问题排查及进阶技巧,帮助用户快速掌握这一高效工具。

weixin_30505225的博客 460

QMT与聚宽策略代码对接实战:Redis信号传递与xtquant交易实现

本文详细介绍了如何将聚宽策略代码与本地QMT终端进行高效对接的实战方案。通过利用Redis Stream构建可靠信号通道,解决了云端信号到本地执行的延迟与丢失问题,并结合xtquant库实现自动化交易执行。文章重点阐述了信号封装、资金映射、异步下单、风控检查及独立资金管理等核心环节,为量化交易者搭建自主可控的实盘系统提供了完整路径。

lilh34434的博客 473

leetcode刷题——二叉树(3)

继续跟着代码随想录刷题。

hahahahanhanhan的博客 623

风力发电机缺陷检测平台

风力发电机缺陷检测平台相关内容展示

weixin_39779415的博客 1111

leetcode二叉树刷题调试不方便的解决办法

在leetcode中刷题时,如果没有会员就需要将代码拷贝到本地的编译器进行调试。但是leetcode中有一类题可谓是毒瘤,那就是二叉树的题。要调试二叉树有关的题需要根据测试用例给出的前序遍历,自己构建一个二叉树,非常不方便。作为一个懒人,在此之前我的解决办法就是硬看程序,反复检查,但是确实有点折磨了。前几天在刷二叉树有关的题时心血来潮写了一个函数来帮助构建二叉树。

2302_80372340的博客 657

具身大小脑全栈全网独家复现|LLM链式推理+边缘VLA感知+毫秒级运动小脑、优化人形步态僵硬运动滞后、打通思考-行走-操作全链路适配居家工业多场景落地

高层决策大脑(异步10Hz,GPU算力):轻量化机器人微调LLM+RhinoVLA边缘视觉大模型,负责自然语言链式工序拆解、全局环境语义感知、目标物体筛选、跨机器人动作适配,输出粗粒度运动目标,不介入底层伺服计算;依托View Registry实现异构机器人动作对齐,适配多形态机器人切换;中层调度小脑(硬实时1kHz,CPU算力)

qq_36130719的博客 255

手写中间件之——并行框架(1 并行框架的应用场景和需求)

我们为什么会需要一个带任务顺序编排的并行框架 1 复杂的微服务系统间调用 经常会有这样的调用场景:app(或web前端)调用后台的一个接口,该接口接到该请求后,需要调用其他多个微服务来获取数据,最终汇总一个最终结果返回给用户。 譬如用户请求“我的订单”,后台在收到请求后,就需要去调用用户详情rpc、商品详情rpc、库存rpc、优惠券rpc等等很多个服务。有些服务是可以并行去请求的,但有些服务...

tianyaleixiaowu的专栏 1万+

leetcode刷题Day7-8 二叉树1

大奋算法一坨,学习一下,主要跟着这个仓库刷。youngyangyang04/leetcode-master: 《代码随想录》LeetCode 刷题攻略:200道经典题目刷题顺序,共60w字的详细图解,视频难点剖析,50余张思维导图,支持C++,Java,Python,Go,JavaScript等多语言版本,从此算法学习不再迷茫!🔥🔥 来看看,你会发现相见恨晚!🚀。

kelekelekle的博客 1924

海思图像质量之HiPQTool调试

文章目录一. HiPQTool 界面二. 图像调试三. 将调试参数写入程序中 一. HiPQTool 界面 二. 图像调试 三. 将调试参数写入程序中

lzg2011的博客 2377

leetcode刷题-二叉树

429.N叉树的层序遍历力扣题目链接(opens new window)给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。例如,给定一个 3叉树 :思路这道题依旧是模板题,只不过一个节点有多个孩子了。

m0_52533650的博客 875

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

LeetCode刷题-199. 二叉树的右视图

难度:中等⭐️⭐️⭐️⭐️。

快乐的小小程序猿 184

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

力扣刷题——按输入的数组建立二叉树

但力扣中数组的NULL不是大写,这会导致编译器无法识别,请改成大写的NULL(在编译器中转化为int型为0,有需要可以改用其他代替)。力扣刷题的时候,自己在本地上调试很不方便,因为传入变量是一棵树,但是力扣给的示例是一个数组,所以写了这么一个函数。

Roy111111111的博客 336

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

力扣刷题 关于二叉树的初始化的基本问题

https://www.jianshu.com/p/b4757b543aa8

qq_35674954的博客 659

LeetCode HOT100刷题笔记(七):二叉树

ps:笔记和代码按本人理解整理,重思路

学习记录 987

力扣 (LeetCode)-对称二叉树,树|刷题打卡

Github来源:力扣 (LeetCode)|刷题打卡 | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者[已开启]任务一:刷题打卡 * 10 篇哪吒人生信条:如果你所学的东西 ...

程序员哆啦A梦,蓝胖子 1006

二叉树刷题总结:力扣刷题

这篇文章写的太好了!https://labuladong.gitbook.io/algo/shu-ju-jie-gou-xi-lie/2.3-shou-ba-shou-shua-er-cha-shu-xun-lian-di-gui-si-wei/er-cha-shu-xi-lie-1 一般二叉树的题离不开前序,中序,后序,层序遍历。(概念性的东西就不说了,很多博客都讲了,专注刷题) 前序遍历:中左右 中序遍历:左中右 后序遍历:左右中 层序遍历:类似于广度搜索,从左到右,从上到下顺着来。 一.二

gtjsjj的博客 422

力扣刷题 -- 965.单值二叉树

【代码】力扣刷题 -- 965.单值二叉树。

2403_82549373的博客 184

【LeetCode刷题指南】--二叉树的后序遍历,二叉树遍历

文章分享了两道二叉树题目解法:1. 力扣145题后序遍历,通过动态开辟数组存储遍历结果,使用递归计算节点数并实现后序遍历;2. 牛客网二叉树遍历题,根据前序序列构建二叉树后中序输出。两题均采用递归解法,时间复杂度O(n),空间复杂度O(n)。文章提供了详细解题思路、代码实现和复杂度分析,适合数据结构学习者参考,欢迎指正补充。

会当凌绝顶,一览众山小 1248

LeetCode HOT100刷题笔记(六):二叉树

ps:笔记和代码按本人理解整理,重思路

学习记录 1111

【初阶数据结构与算法】二叉树链式结构刷题训练(Leetcode二叉树遍历、单值二叉树、相同的树、另一棵树的子树、对称二叉树)

这里左边的p节点的左子树3和右边q节点的右子树3进行递归,它们都在外面,所以返回的结果用一个新的变量outside存起来,p节点的右子树4和q节点的右子树4进行递归,它们都在里面,所以返回的结果用新变量inside存起来。这道题是要求我们查看左边的二叉树里面的子树有没有和右边子树相同的,这道题相对于比前面的相同的树要稍微要难一点点,因为我们发现它们都是求树和树是否相同的问题,只是到这道题是查看左边的子树和右边的树是否相同,情况更为复杂。那么我们最后到底该怎么做呢?

hdxbufcbjuhg的博客 4004

LeetCode刷题——二叉树1

二叉树是一种基础的数据结构。二叉树在内存中的存储方式有顺序存储和链式存储,顺序存储是用数组,链式存储使用指针。二叉树的主要分为满二叉树和完全二叉树,满二叉树是指只含有度为0和度为2的节点且度为0的节点都处于同一层,完全二叉树是指除了最底层外其余各层均填满且最底层的节点集中在最左边。Java中定义二叉树的代码如下int val;

m0_64482337的博客 376

LeetCode刷题226-简单-翻转二叉树

算法作为极其重要的一点,是大学生毕业找工作的核心竞争力,所以为了不落后与人,开始刷力扣算法题!

布小禅的博客 489
上一篇: 面经:JUC学习笔记
下一篇: 瑞吉外卖-笔记1
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值