笔记 | 未完结 | 重写leetcodeBook的初级算法和高级算法


一、数组

1.删除排序数组中的重复项

我使用了双指针

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==1) return 1;
        int sum=1;
        int i=0;//slow
        int j=1;//fast
        while(j!=nums.length){

            if(nums[i]==nums[j]){j++;}
            else{
                nums[i+1]=nums[j];i++;sum++;
            }
        }
        return sum;
    }
}

感觉不太行
在这里插入图片描述

别人的双指针:
在这里插入图片描述

    //双指针解决
    public int removeDuplicates(int[] A) {
        //边界条件判断
        if (A == null || A.length == 0)
            return 0;
        int left = 0;
        for (int right = 1; right < A.length; right++)
            //如果左指针和右指针指向的值一样,说明有重复的,
            //这个时候,左指针不动,右指针继续往右移。如果他俩
            //指向的值不一样就把右指针指向的值往前挪
            if (A[left] != A[right])
                A[++left] = A[right];
        return ++left;
    }
作者:数据结构和算法
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/x2gy9m/?discussion=4Zkrel
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

分析:

2.旋转数组

两次翻转

class Solution {
    public void rotate(int[] nums, int k) {
        int length=nums.length;
        k=k%length;
        exchange(nums,0,length-1);
        exchange(nums,0,k-1);
        exchange(nums,k,length-1);
    }
    void exchange(int[]nums,int start,int end)
    {
        while(start<end){
            int temp = nums[start];
            nums[start++] = nums[end];
            nums[end--]=temp;

        }
    }
}

还可以用环形旋转

3.存在重复元素

回忆一下Set用法,算法直接粘贴过来:

    public boolean containsDuplicate(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            //因为集合set中不能有重复的元素,如果有重复的
            //元素添加,就会添加失败
            if (!set.add(num))
                return true;
        }
        return false;
    }

set集合中的元素是不能有重复的,在添加的时候(add方法)如果有重复的,会把之前的值给覆盖,并且返回false

4.两数之和

HashMap解法
Java HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射
HashMap 是无序的,即不会记录插入的顺序
创建HashMap对象:

HashMap<Integer, String> Sites = new HashMap<Integer, String>();

常用的HashMap方法
put() 将键/值对添加到 hashMap 中
remove() 删除 hashMap 中指定键 key 的映射关系
get() 获取指定 key 对应对 value
values() 返回 hashMap 中存在的所有 value 值

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路:将数组中value值作为key,index值作为value,设计一个循环把数组中每一个值都放进HashMap中,然后在放入的同时检查HashMap中是否存在一个数和当前的数的和为target

class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap <Integer,Integer>map=new HashMap<>();
        for(int i=0;i<nums.length;i++){
            if(map.get(target-nums[i])!=null)
            return new int[]{map.get(target-nums[i]),i};
            else map.put(nums[i],i);
        }
        return new int[]{0,0};
    }
}

5.三数之和

题目:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。注意,输出的顺序和三元组的顺序并不重要。

continue 用来结束当前循环,并进入下一次循环,即仅仅这一次循环结束了
Class Arrays

该类包含用于操作数组的各种方法(例如排序和搜索)。 此类还包含一个静态工厂,允许将数组视为列表。

变量和类型方法描述
static ListasList​(T… a)返回由指定数组支持的固定大小的列表。

思路:由后面两个注意可以推测不用从“二数之和”的方法出发推三数之和。本题用双指针的方法,定住一个,双指针寻找两个相加为定点负数的数。因为顺序随意,所以可以进行排序来优化算法,两指针从两头往中间靠拢,涉及到的判断条件有:①排除重复的(分为定点数重复以及双指针指到的数重复) ②和大于定点的负数说明大了,右指针左移,另外一边同理

构造函数: List<List> list=new ArrayList<>();

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> list=new ArrayList<>();
    for(int i=0;i<nums.length;i++){
        if(i>0&&nums[i]==nums[i-1]) continue;
        if(nums[i]>0) break;
        int target=-nums[i];
        int left=i+1;
        int right=nums.length-1;
        while(left<right){
            if(nums[left] + nums[right] == target) {list.add(Arrays.asList(nums[i],nums[left],nums[right]));
            while(left<right && nums[left]==nums[left+1]) left++;
            while(left<right && nums[right]==nums[right-1]) right--;
            left++;
            right--;}
            else if((nums[left] + nums[right])<target) left++;
            else right--;
        }
    }
    return list;
    }
}

二、字符串

1.字符串中的第一个唯一字符

熟悉哈希表

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

⭐哈希表中添加的方法是 put(),ArrayList是 add()
将字符串转换为char数组的方法:toCharArray()

getOrDefault()
getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
getOrDefault() 方法的语法为:
hashmap.getOrDefault(Object key, V defaultValue)

class Solution {
    public int firstUniqChar(String s) {
HashMap<Character,Integer>map = new HashMap<>();
char[]chars=s.toCharArray();
for(char c:chars){
    map.put(c,map.getOrDefault(c,0)+1);
}
int count=0;
for(char c:chars){
    if(map.get(c)==1)return count;
    count++;
}
return -1;
    }
}

2.验证回文串

熟悉一些字和字符串方法
· public static boolean isLetterOrDigit​(char ch)
确定指定的字符是字母还是数字。

· public String toLowerCase()
使用默认语言环境的规则将此String所有字符转换为小写。

class Solution {
    public boolean isPalindrome(String s) {
char[]chars = s.toCharArray();
int left=0;
int right=s.length()-1;
while(left<right){
    while(left<right && !Character.isLetterOrDigit(chars[left]))left++;
    while(left<right && !Character.isLetterOrDigit(chars[right]))right--;
    if(Character.toLowerCase(chars[left])!=Character.toLowerCase(chars[right]))     return false;
    left++;
    right--;
}
return true;
    }
}

正则表达式方法:
· public String replaceAll​(String regex, String replacement)
将给定替换的给定regular expression匹配的此字符串的每个子字符串替换

StringBuffer reverse()
导致此字符序列被序列的反向替换

public boolean equals​(Object anObject)
将此字符串与指定的对象进行比较。 当且仅当参数不是null且是String对象表示与此对象相同的字符序列时,结果为true
eg. s.equals(t)

class Solution {
    public boolean isPalindrome(String s) {
    s = s.replaceAll("[^a-zA-Z0-9]","").toLowerCase();
    String t=new StringBuffer(s).reverse().toString();
    // if(s==t)return true;
    return s.equals(t);
    }
}

注意正则表达式的“反”:[^a-zA-Z0-9]

3.最长回文子串

题目:给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

思路:
以双指针由中间向两边寻找的方式,遍历字符串,找到最长的那个

难点
⭐特殊情况的去重

  while(right<length-1&&s.charAt(right)==s.charAt(right+1)){//i->right 去掉left>0;
                right++;
            }

加入单个字符的判断
该思路其实已经省略了单字符判断:

while (right < length - 1 && left > 0 && s.charAt(right + 1) == s.charAt(left - 1)) {
            ++right;
            --left;}
        if (right - left + 1 > maxLen) {
            start = left;
            maxLen = right - left + 1;}

在没有添加去重之前会报错,比如“c,b,b”这样的例子
无优化:
在这里插入图片描述
添加优化(当字符串长度减去当前中心点的下标,如果该数没有当前最大回文数长度/2大,那就直接退出不用找了):

class Solution {
    public String longestPalindrome(String s) {
        int length=s.length();
        int max=0;
        int maxLen=0;
        int start=0;
        for(int i=0;i<length;){//i不自增
            int left=i;
            int right=i;  
               
            if (length - i <= maxLen / 2)//优化
            break;

            while(right<length-1&&s.charAt(right)==s.charAt(right+1)){//i->right 去掉left>0;
                right++;}
            i=right+1;
        while (right < length - 1 && left > 0 && s.charAt(right + 1) == s.charAt(left - 1)) {
            ++right;
            --left;
        }
        //保留最长的
        if (right - left + 1 > maxLen) {
            start = left;
            maxLen = right - left + 1;
        }
        }
    return s.substring(start,start+maxLen);
}
}

在这里插入图片描述

i不在for循环里自增而是改成i=right+1
可以看出没有区别
在这里插入图片描述
在这里插入图片描述

4.最长公共前缀

题目:编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “” 。

思路:选第一个字符先当公共前缀,然后缩短

class Solution {
    public String longestCommonPrefix(String[] strs) {
    
        String ans=strs[0];
        if(strs.length==1)return ans;
        for(String s:strs){
            ans=help(ans,s);
        }
        return ans;
    }
    public String help(String s1,String s2){
        int i=0;
        String s="";
        while(i<s1.length()&&i<s2.length()){
            if(s1.charAt(i)!=s2.charAt(i))
            break;
            else s=s1.substring(0,++i);//一开始写了i++,输出输出少了,猜到i小了但还没搞清楚
        }
        return s;
    }
}

不太行
在这里插入图片描述
另外一种写法涉及的indexOf方法indexOf() 方法有以下四种形式:

public int indexOf(int ch):						返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
public int indexOf(int ch, int fromIndex):	 	返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
int indexOf(String str): 						返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
int indexOf(String str, int fromIndex): 		返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

所以我们可以通过循环遍历String数组,以第一个字符串作为最长公共前缀,作为indexOf() 的参数,如果返回值为true,相当于得到了公共前缀,如果为false,则将参数缩短(substring(0,length-1)),直到找到或为算短到“”为止,然后继续下一个string进行匹配

    public String longestCommonPrefix(String[] strs) {
        //边界条件判断
        if (strs == null || strs.length == 0)
            return "";
        //默认第一个字符串是他们的公共前缀
        String pre = strs[0];
        int i = 1;
        while (i < strs.length) {
            //不断的截取
            while (strs[i].indexOf(pre) != 0)
                pre = pre.substring(0, pre.length() - 1);
            i++;
        }
        return pre;
    }

String和StringBuffer

String
在代码中遇到字符串常量时,编译器会使用该值创建一个 String 对象
String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上

String s1 = “Runoob”; // String 直接创建
String s2 =“Runoob”; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String(“Runoob”); // String 对象创建
String s5 =new String(“Runoob”); // String 对象创建
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。

如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类

StringBuffer
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

三、链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        Deque <ListNode>stack = new ArrayDeque<ListNode>();
        while(head!=null) {stack.push(head);head=head.next;}
        
        if (stack.size()==0) return null;
        
        ListNode reverse=stack.pop();
        ListNode newhead=reverse;

        while(stack.size()!=0) {
            ListNode next=stack.pop();
            // next.next=null;假设此处不添加就要在循环结束后添加,因为最后一个结点就是反转前的头结点,否则构成环
            reverse.next=next;
            reverse=reverse.next;
        }
        reverse.next=null;
        return newhead;
    }
}

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        // Deque <ListNode>stack = new ArrayDeque<ListNode>();
        Stack <ListNode> stack=new Stack<>();
        while(head!=null) {stack.push(head);head=head.next;}
        
        if (stack.isEmpty()) return null;
        
        ListNode reverse=stack.pop();
        ListNode newhead=reverse;

        while(!stack.isEmpty()) {
            ListNode next=stack.pop();
            // next.next=null;假设此处不添加就要在循环结束后添加,因为最后一个结点就是反转前的头结点,否则构成环
            reverse.next=next;
            reverse=reverse.next;
        }
        reverse.next=null;
        return newhead;
    }
}

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null) return head;
        ListNode next=head.next;
        ListNode reverse=reverseList(head.next);
        // reverse.next=head;
        next.next=head;
        head.next=null;
        return reverse;
    }
}
class Solution {
    public void deleteNode(ListNode node) {
        ListNode front=node;
        while(node.next!=null){
            node.val=node.next.val;            
            node=node.next;
}
        while (front.next!=null)
        if(front.next!=null&&front.next.next==null)
            front.next=null;
        else front=front.next;
    }
}

在这里插入图片描述

Java 集合

接口和实现类的关系

假如我们要实现一个队列的操作,首先我们要创建一个队列的抽象接口

public interface My_Queue< E >{
E remove();
void add(E element);
int size();
}

然后写具体实现,Deque有两种实现方法,一种是循环数组的形式,一种是链表的形式,所以有两种实现类:

public class Array_Queue() implements My_Queue< E >
{
private int head;
private int tail;
public void add(E element){};//写具体的实现方法
//...省略


}
public class LinkedList_Deque() implements My_Queue< E >
{
private Link head;
private Link tail;
public void add(E element){};//写具体的实现方法
//...省略
}

然后在构造集合对象中再使用具体的类,也可以及时的对他进行更改

在这里插入图片描述

List<Integer> list=new LinkedList<>();
Deque<TreeNode> deque=new LinkedList<>();

LinkedList 在指定位置插入的实例方法
在这里插入图片描述

Interface Deque< E >的实例方法

方法摘要
contains​(Object o)如果此双端队列包含指定的元素,则返回 true 。
E peek()检索但是不删除头部
E poll()检索并删除此双端队列表示的队列的头部
E pop()从此双端队列表示的堆栈中弹出一个元素

Collection 接口

Collection 主要有两个基本方法,一个添加一个返回实现Iteractor接口的对象,使用这个对象可以遍历集合中的元素

public interface Collection<E>{
boolean add(E element);
Iterator<E> iterator();
//...
}

迭代器

public interface iterator<E>{
E next();
boolean haveNext();
void remove();
//...
}

remove方法会删除上次调用的next方法返回的元素
remove之前必须要调用next

集合框架接口

List集合的访问可以通过迭代器或整数索引来访问
有几种随机访问的方法

void add(int index,E element)
E get (int index)
//...

散列集

用于快速查找对象,散列表为每个对象计算一个整数称为散列码
散列表用链表数组实现,每个列表被称为桶
查找位置:先计算散列码在和桶的总数取余
假如要插入一个元素但是桶已经被填充了,此时就要遍历那个通看看这个元素是不是已经存在了

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

/**
 * 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 {


    public TreeNode helper(int inoStart,int inoEnd,int preStart,int[] preorder, int[] inorder){
        if(inoStart > inoEnd || preStart>preorder.length-1)
        return null;

        TreeNode root=new TreeNode(preorder[preStart]);
        int index=0;//用在中序
        for(int num:inorder){
            if (num==root.val)
             break;
            index++;
        }

        root.left=helper(inoStart,index-1,preStart+1,preorder,inorder);
        root.right=helper(index+1,inoEnd,preStart+index-inoStart+1,preorder,inorder);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
    
        return helper(0,inorder.length-1,0,preorder,inorder);
    }
}

递归来拆分子树。前序遍历的第一个必是根节点,在中序序列里找到那个结点进行拆分,再找下一个根节点。⭐如何找到下一个根节点?
举例:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
在这里插入图片描述

第一次拆分: [ 9 ] 3 [ 15,20,7 ] (根节点:3)
第二次拆分(先左): [ 9 ] (根节点:9)

root.left = helper(...,...,...);

//⭐怎样通过下标进行拆分?

通过整个树的下标划分出小树的中序序列,设置参数 int inStart 和 inEnd
以及小树先序序列起始的地方也就是根节点,设置参数为 int preStart
(分割左右的进阶版)
preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
所以说在第一次拆分时,root.left=helper(…)参数,可以看出参数为:

 inStart =inStart
 inEnd=index-1

root.right=helper(…)参数,可以看出参数为:

inStart = index+1
inEnd = inEnd

//⭐怎样确定在preorder序列上的指针
首先这个递归方法目的是从最小的子树开始搭建,也就是寻找根结点(null<-A->null),先从先序遍历找到根节点(第一个),再在中序遍历找到根节点,划分完成后,再通过下标找到先序遍历中下一个操作的根节点。由图可知处理玩结点3下一个要处理结点9,此时先序的prestart应该指向9
如何确定preStart
left的preStart=preStart+1 由根左右可知,左一定在根的下一个
right的preStart
由 根左右 左根右 以及小树的中序序列可知,preStart指向右节点,右节点在先序遍历的位置是当前的根下标+[根左]的宽度,如何确定[根左]的长度,可以在中序遍历中(根的下标) - (左下标)得到
也就是

preStart+(index-inoStart)+1

第二种写法
思路:其实也是在中序找结点,利用ist的查找方法,建立

List<Integer> inorderList= new ArrayList<>();
//...

利用查找方法找,然后切割
//前序遍历的第一个值就是根节点
查找和切割方法:

indexOf(int i)
subList(int start,int end)

从前序序列移出第一个元素并返回值的方法:

remove(0)

DFS BFS

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

class Solution {
    public List<String> letterCombinations(String digits) {
        
    char[][] tab = {{'a', 'b', 'c'}, {'d', 'e', 'f'}, {'g', 'h', 'i'},
            {'j', 'k', 'l'}, {'m', 'n', 'o'}, {'p', 'q', 'r', 's'},
            {'t', 'u', 'v'}, {'w', 'x', 'y', 'z'}};
        List<String> ans=new LinkedList<>();
        
        if (digits.length()==0||digits==null) return ans;
        //if (digits==""||digits==null) return ans;
        dfs(digits,tab,0,"",ans);
        return ans;

      
    }
  
    public void dfs(String digits,char[][]tab,int index,String path,List<String>ans){
        if(path.length()==digits.length())
            {ans.add(path);return;}
        char[] chars=tab[digits.charAt(index)-'2'];
        for(int i=0;i<chars.length;i++)
            dfs(digits,tab,index+1,path+chars[i],ans);
    }
}

在这里插入图片描述
注释那里会出现下面这个错误
在这里插入图片描述

来自:http://t.csdn.cn/CS0QL
null就是空,不占用内存空间,没有任何属性,也不能读取属性,即没有.length()等;
“” :表示string指向一个长度为0的字符串对象
str.length() = 0,此刻是一个字符串,已经为其分配了一定的内存空间

讲解String类:
https://www.bbsmax.com/A/ke5jmvg7Jr/
笔记写在另外一篇

五、回溯

拓号

第一次用的String类进行poll操作(substring),但这样会浪费很多内存

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans=new LinkedList<>();
        help(n,0,0,ans,"");
        return ans;
    }
    public void help(int n,int right,int left,List<String>ans,String s){
        if(s.length()==n*2)
        {ans.add(s);return;}
        if(left<n){
            s+='(';
            help(n,right,left+1,ans,s);
            // s = s.replaceFirst(".$", "");
            s=s.substring(0,s.length()-1);
        }
        if(right<left){
            s+=")";
            help(n,right+1,left,ans,s);
            s=s.substring(0,s.length()-1);
        }
    }
}

在这里插入图片描述改用StringBuilder

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans=new LinkedList<>();
        StringBuilder sb=new StringBuilder();
        help(n,0,0,ans,sb);
        return ans;
    }
    public void help(int n,int right,int left,List<String>ans,StringBuilder sb){
        if(sb.length()==n*2)
        {   String s=sb.toString();
            ans.add(s);return;}
        if(left<n){
            // s+='(';
            sb.append("(");
            help(n,right,left+1,ans,sb);
            sb.delete(sb.length()-1,sb.length());
            // s = s.replaceFirst(".$", "");
            // s=s.substring(0,s.length()-1);
        }
        if(right<left){
            sb.append(")");
            help(n,right+1,left,ans,sb);
            sb.delete(sb.length()-1,sb.length());
        }
    }
}

在这里插入图片描述
StringBuffer和StringBuilder的区别
StringBuffer和StringBuilder都是可变字符串类,但它们有以下区别:
线程安全性:StringBuffer是线程安全的,而StringBuilder是非线程安全的。
性能:StringBuilder比StringBuffer性能更好,因为它不考虑线程安全性,不需要进行同步操作。
应用场景:当需要进行多线程操作时,应该使用StringBuffer,因为它是线程安全的。当不需要考虑线程安全时,应该使用StringBuilder,因为它性能更好。

StringBuilder的常见用法

创建一个StringBuilder对象:
StringBuilder sb = new StringBuilder();

向StringBuilder中添加字符串:
sb.append("hello");

插入字符串:
sb.insert(0,"world ");

删除字符串:
sb.delete(5,10);

替换字符串:
sb.replace(5,10,"world");

获取字符串长度:
sb.length();

获取字符串内容:
sb.toString();

清空字符串:
sb.setLength(0);

全排列

在这里插入图片描述

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值