LeetCode拾遗:1-10题

引言

在整理自己LeetCode刷题的代码时,突然想着不如借此机会,开个博客记录一下LeetCode刷题的思路、代码与心得,聊作复习与分享之用。其中部分题解题思路简单、直接、可笑之处,还请各位多多担待。如果部分简单的思路能给各位提供参考之处,也是不甚荣幸。

本篇博客主要记录当时分析题目时的思路和最后的代码,并不一定是最好的解法。还请大家多多指教。

题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com

LeetCode 1-10题

1. 两数之和

题目描述

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

难度:简单

输入输出示例

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路分析

不论是在线OJ还是LeetCode这类的刷题平台,第一题往往都是两数之和、相加这种问题,用于熟悉平台、热身练手。

本题采用嵌套循环的 双指针 方法可以很轻松地解决,而题目给出假设每种输入只会对应一个答案,所以无需考虑多答案情况。

代码


	public int[] twoSum(int[] nums, int target) {
        int len = nums.length;
        int[] result = new int[2];
        for (int i = 0; i < len; i++){
            for (int j = i + 1; j < len; j++){
                if (target == nums[i] + nums[j]){
                    result[0] = i;
                    result[1] = j;
                }
            }
        }
        return result;
    }
    

2. 两数相加

题目描述

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

难度:中等

输入输出示例

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

思路分析

一道类似于大数相加的问题,本题的难点在于相加的两数有可能非常大,以至于超过整型的表示范围,所以将链表表示的数转换为两个整型相加是不可取的。

对于这类题目,可以一步步实现加法的运算与进位,使用flag表示进位,自后向前进行加法运算(题目所给的链表恰好是反向的链表)。

代码


	public class ListNode {// 题目给出的ListNode链表
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }
    
	public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int tNum = l1.val + l2.val;
        int flag = 0;// 进位标志
        if (tNum > 9){
            flag = 1;
            tNum = tNum % 10;
        }else {
            flag = 0;
        }
        ListNode listNode = new ListNode(tNum);// 这里有不妥之处,应该创建空链表然后add比较好
        ListNode list = listNode;
        while (l1.next != null || l2.next != null || flag == 1){// 考虑到最高位有进位的情况,需要加上flag == 1
            int num1=0, num2=0;
            if (l1.next != null){
                l1 = l1.next;
                num1 = l1.val;
            }
            if (l2.next != null){
                l2 = l2.next;
                num2 = l2.val;
            }
            int num = num1 + num2 +flag;
            if (num > 9){
                flag = 1;// 再次进位
                num = num % 10;
            }else {
                flag = 0;// 进位重置
            }
            ListNode temp = new ListNode(num);
            list.next = temp;
            list = list.next;
        }
        return listNode;
    }
    

3. 无重复字符的最长子串

题目描述

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

难度:中等

输入输出示例

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

思路分析

题目说明非常简洁,要实现的功能也非常简单,然后我们果断使用 穷举法,代码洋洋洒洒一气呵成,然后…

Time Limit Error

没错,穷举法又一次成为了反面典型。

既然穷举法不行的话,我们换种思路,注意到题目中的 最长子串 ,既然是最长子串,需要输出的只是长度,我们是不是可以用一个 队列 来记录非重复子串,然后用一个max记录最长的长度。

这种方法正是 滑动窗口 方法,使用一个队列作为窗口,进入这个窗口的子串满足题目要求(即无重复字符)时,则继续进入新的字符;当不满足要求(即出现重复字符)时,则移动这个队列,把左边的元素移出,右面的元素加入,类似于整体窗口右移,从而使max能够记录下最长的长度。

代码


	public int lengthOfLongestSubstring(String s) {
        if (0 == s.length()){// 以前写C的时候留下的习惯,java大可不必
            return 0;
        }
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0, left = 0;
        for (int i =0; i<s.length(); i++){
            if (map.containsKey(s.charAt(i))){// 出现重复字符
                left = Math.max(left, map.get(s.charAt(i))+1);
            }
            map.put(s.charAt(i),i);// 滑动
            max = Math.max(max, i - left + 1);
        }
        return max;
    }
    

4. 寻找两个正序数组的中位数

题目描述

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

难度:困难

输入输出示例

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0
nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

思路分析

如果按照顺序刷题的话,这是我们刷题路上的第一道困难题。

我们总结一下题意,长度为m和n的两个正序数组,不会同时为空(意思是可能会存在其中一个为空的情况),找中位数。

如果没有题目对时间复杂度 O(log(m + n))的要求,我们大可直接将两个数组合并排序,然后找出中位数。这种思路虽然不行,但是给了我们一个启发。考虑单个有序数组,当长度为奇数时,中位数正是1/2长度处的元素;当长度为偶数时,中位数是长度1/2处两元素的平均数。

回过头来我们看这个题目,总长度为m + n的两个数组,取k = (m + n)/2,这个问题就可以转换为我们要找两个数组中第k小的元素。我们该如何缩小这个查找的范围呢?我们再设想一种情况。

当两个数组完全一致时,将k这个指标分配到两个数组中去,第k小的数字正是两个数组k/2小的数字。而实际情况下,两个数组很少会出现完全一致的情况,那么我们可以通过比较两个数组k/2处元素的大小,将较小的数组前k/2个元素全部抛弃。通过上面的分析我们明白,两个数组完全一致的情况下,k/2才会相等;换个思路来想,k/2处较小的数组,其前k/2的元素中不可能存在中位数,所以我们可以通过这种思路来不停地缩小范围。

而同理,当我们不断缩小范围后,剩余的两个数组同样满足前面我们所说的k/2规则,只不过此处的k经过舍去元素后,发生了改变。随着范围不断缩小,当k缩小到1时,即需要找到当前数组最小的元素,只需要比较当前两个元素,找出其中的最小者,即可找到中位数。

其中一个特殊情况是,若其中一个数组较小,被完全舍去后,剩余的k便可以直接在另一个数组中,找出第k小的数作为中位数。

分析到这里,我们很明显可以发现递归的方法了。

代码


	public int toFind(int[] nums1, int begin1, int end1, int nums2[], int begin2, int end2, int k){
        int len1 = end1 - begin1 + 1;
        int len2 = end2 - begin2 + 1;
        // 一份题解里写道,若len2比len1小就交换,保持len1最小,可以省掉越界时判断是哪个越界
        if(len1 > len2){
            return toFind(nums2, begin2, end2, nums1, begin1, end1, k);
        }
        // 越界
        if (len1 == 0){
            return nums2[begin2 + k - 1];
        }
        // k == 1意味着找到了,返回被标记的两者中最小的那个
        if (k == 1){
            return nums1[begin1] < nums2[begin2] ? nums1[begin1] : nums2[begin2];
        }
        // 标记后移
        int tag_1 = begin1 + Math.min(k/2, len1) - 1;
        int tag_2 = begin2 + Math.min(k/2, len2) - 1;

        if (nums1[tag_1] > nums2[tag_2]){
            return toFind(nums1, begin1, end1, nums2, tag_2 +1, end2, k - (tag_2 - begin2 +1));
        }else {
            return toFind(nums1, tag_1 + 1, end1, nums2, begin2, end2, k - (tag_1 - begin1 +1));
        }
    }

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {

        // 找到第k小的数,依次排除,将奇数个看作偶数个处理
        int k_left = (nums1.length + nums2.length + 1)/2;
        int k_right = (nums1.length + nums2.length + 2)/2;

        return (double)(toFind(nums1, 0,nums1.length - 1, nums2, 0, nums2.length - 1, k_left)
                + toFind(nums1, 0,nums1.length - 1, nums2, 0, nums2.length - 1, k_right))/2;
    }
    

5. 最长回文子串

题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

难度:中等

输入输出示例

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
输入: "cbbd"
输出: "bb"

思路分析

又是一个最大子串的问题,所涉及的是回文串的判断。单独的回文串判断并不难,本体的难点主要体现在 最长 。我们对这个关键字“最长”分析来看,那么最理想的情况下,一个字符串本身应该就是这个最长回文子串;次理想情况下,缺少一个字符后的字符串可以作为最长的回文子串。

分析到这一步我们可以发现,我们既然是在找 最长 的话,完全可以从最理想的情况下开始判断,判断他是否是回文串,然后去次理想情况下依次判断,依次类推,所找到的第一个回文子串一定是最长回文子串。

代码


	public Boolean isPalindrome(String str){
	// 判断是否是回文子串,这里使用双指针方法判断
        for (int i =0; i < str.length()/2; i++){
            if (str.charAt(i) != str.charAt(str.length()-1-i)){
                return false;
            }
        }
        return true;
    }
    public String longestPalindrome(String s) {
        int flag = 0;
        String result = "";
        for (int i = s.length(); i >0; i--){
            for (int j = 0; j <s.length()- i + 1; j++){
                if (isPalindrome(s.substring(j,j+i))){
                    result = s.substring(j,j+i);
                    flag = 1;// 跳出最外层循环的标志位,也可以通过符号标记tag标记外层循环然后break tag直接跳出
                    break;
                }
            }
            if (1 == flag){
                break;
            }
        }
        return result;
    }
    

6. Z 字形变换

题目描述

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

难度:中等

输入输出示例

输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

思路分析

一道找规律的题目,总结公式直接循环判断即可,对于numRows=1的情况直接输出防止TL。

对于每一行输出第几个字符,在纸上画一画很容易就总结出规律,这类题目比动态规划那种题目简单明了多了。

代码


	public String convert(String s, int numRows) {
        int length = s.length();
        String result = "";
        if (1 == numRows){
            return s;// 一行直接返回
        }else {
            for (int a = 0; a < numRows; a++) {
            // 找到的规律直接套进去
                if (0 == a) {
                    for (int i = 0; i < length; i = i + (numRows - 1) * 2) {
                        result += s.charAt(i);
                    }
                } else if (numRows - 1 == a) {
                    for (int i = numRows - 1; i < length; i = i + (numRows - 1) * 2) {
                        result += s.charAt(i);
                    }
                } else {
                    for (int i = a, j = i + (numRows - a - 1) * 2; i < length; i = i + (numRows - 1) * 2, j = i + (numRows - a - 1) * 2) {
                        result += s.charAt(i);
                        if (j < length) {
                            result += s.charAt(j);
                        }
                    }
                }
            }
        }
        return result;
    }
    

7. 整数反转

题目描述

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

难度:简单

输入输出示例

输入: 123
输出: 321
输入: -123
输出: -321
输入: 120
输出: 21

思路分析

最简单的思路是,首先转换为String,然后翻转转换回来。

其中需要考虑两种特殊情况,负数与溢出,使用tag捕获负数情况,try-catch来捕获溢出情况。

代码


	public int reverse(int x) {
        int result = 0;
        char t;
        int tag = 0;// 用来标记负数情况
        String temp = String.valueOf(x);// 转换为String
        if (x < 0){
           temp = temp.substring(1,temp.length());// 滤掉负号,防止干扰翻转
           tag = 1;// 标记负数
        }
        char toChange[] = temp.toCharArray();// 转换为字符数组

        for (int i = 0; i<toChange.length/2; i++){
        // 翻转
            t = toChange[i];
            toChange[i] = toChange[toChange.length - i - 1];
            toChange[toChange.length - i - 1] = t;
        }
        temp = String.copyValueOf(toChange);
        if (1 == tag){
        //加回来负号
            temp = "-"+temp;
        }
        try {// try-catch捕获溢出异常
            result = Integer.valueOf(temp);
        }catch (Exception e){
            return 0;// 溢出输出0
        }
        return result;
    }
    

8. 字符串转换整数 (atoi)

题目描述

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

  • 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
  • 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
  • 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示

  • 本题中的空白字符只包括空格字符 ’ ’ 。
  • 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [ − 2 31 -2^{31} 231, 2 31 − 1 2^{31} − 1 2311]。如果数值超过这个范围,请返回 INT_MAX ( 2 31 − 1 2^{31} − 1 2311) 或 INT_MIN ( − 2 31 -2^{31} 231) 。

难度:中等

输入输出示例

输入: "42"
输出: 42
输入: "   -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
     我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
     因此无法执行有效的转换。
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 
     因此返回 INT_MIN 。

思路分析

题目描述得非常繁杂,提取出的题意为:丢掉开头的空格后,如果首字母是正负号或数字则将完整的提取出来,忽略后面的字符,否则输出0,溢出则输出最大值/最小值。

说到这里,我们的思路就非常清晰了,去段首空格,然后正则匹配即可。

题目的难点在于特殊条件有点多,+、-、空、+-、-+这些特殊情况都要过滤掉,仍然使用try-catch捕获溢出。(这个题的代码写得有点low)

代码


	public int myAtoi(String str) {
        int result = 0;
        str = str.trim();// 去空格
        if (str.equals("") || str.equals("-") || str.equals("+")){
        // 特殊情况直接返回
            return result;
        }
        if (str.charAt(0) == '+' && str.charAt(1) != '-')
        // 正号+数字的情况
        {
            str = str.substring(1,str.length());
        }
        // 这里当时写得太low了,可以有更好的处理办法的,而不是if一串条件
        if (str.charAt(0) == '0' || str.charAt(0) == '1' || str.charAt(0) == '2' || str.charAt(0) == '3' || str.charAt(0) == '4' || str.charAt(0) == '5' || str.charAt(0) == '6' || str.charAt(0) == '7' || str.charAt(0) == '8' || str.charAt(0) == '9'){
        // 正则
            Pattern p = Pattern.compile("\\d+");
            Matcher m = p.matcher(str);
            m.find();
            try {// 捕获溢出
                result = Integer.parseInt(m.group());
            }catch (Exception e){
                result = (int)Math.pow(2,31);
            }
        }else if (str.charAt(0) == '-' && (str.charAt(1) == '0' || str.charAt(1) == '1' || str.charAt(1) == '2' || str.charAt(1) == '3' || str.charAt(1) == '4' || str.charAt(1) == '5' || str.charAt(1) == '6' || str.charAt(1) == '7' || str.charAt(1) == '8' || str.charAt(1) == '9')){
        // 负数情况
            str = str.substring(1,str.length());
            Pattern p = Pattern.compile("\\d+");
            Matcher m = p.matcher(str);
            m.find();
            try {
                result = Integer.parseInt('-' + m.group());
            }catch (Exception e){
                result = -(int)Math.pow(2,31)-1;
            }
        }
        return result;
    }
    

9. 回文数

题目描述

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

难度:简单

输入输出示例

输入: 121
输出: true
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。

思路分析

判断是否是回文数,可以用 双指针 方法或者 堆栈 方法实现,这里用双指针方法,由两端到中间进行比较。这个没太多好说的。

代码

	public boolean isPalindrome(int x) {
        String temp = String.valueOf(x);
        char[] chr = temp.toCharArray();
        for (int i = 0; i < chr.length/2; i++){
            char t = chr[i];
            chr[i] = chr[chr.length - i - 1];
            chr[chr.length - i - 1] = t;
        }
        String toBack = String.copyValueOf(chr);
        if (temp.equals(toBack)){
            return true;
        }else {
            return false;
        }
    }

10. 正则表达式匹配

题目描述

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

难度:困难

输入输出示例

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

思路分析

我认为这道题是前10题中难度最大的一道题,要求实现正则表达式的匹配。规则其实很简单,".“可以匹配任意字符,”*“表示前面字符可以匹配0-n个。而复杂点就在于”.*"这种组合,可以匹配空气,也可以匹配任意个任意数,如何去决断它是这道题的难点。

这种题我们首先去尝试的思路肯定是暴力法了,对于p和s,从末尾到头部进行一一匹配,遇到".“直接匹配当前那个元素,遇到”*“可以从后向前用一个while匹配一或多个字符,然后…成功卡在了倒数第三个测试用例的”.*"上。(其实想想这种题也不可能让你轻松用暴力法直接做出来吧)

所以我们怎么做呢?先分析一下,在匹配的过程中会遇到哪些情况呢?

  • 第一种,遇到字符,这种情况两个字符正好匹配即可;
  • 第二种,遇到".","."可以与任意字符匹配上,这种和第一种情况其实是一样的;
  • 第三种,遇到"*",这种情况则衍生出不同的组合,"*“可以与字符、”."进行搭配,表示出现0次或n次。

那么到了这里,我们可以考虑使用 动态规划 来解决此问题了。用 dp[i][j] 表示 s 的前 i 个是否能被 p 的前 j 个匹配

  • 对于第一种情况,匹配到字符,dp[i][j] = dp[i - 1][j - 1]
  • 对于第二种情况,匹配到".",和第一种情况一样,dp[i][j] = dp[i - 1][j - 1]
  • 对于第三种情况就更复杂了,我们分两种情况来分析:

(1) 如果p.charAt(j-1) != s.charAt(i),那么意味着p中"*“前的字符是与s中当前字符匹配不上的,那么”*“所代表的只能是前面的字符出现0次;
(2) 而如果p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == ‘.’,意味着p中”*"前的字符是与s中当前字符可以匹配的。

那么根据s中匹配情况的不同,可以有以下三种情况:

  • dp[i][j] = dp[i-1][j] (多个字符匹配的情况);
  • dp[i][j] = dp[i][j-1] (单个字符匹配的情况);
  • dp[i][j] = dp[i][j-2] (没有匹配的情况)。

根据递推式,我们可以开始尝试写代码了。

代码

	public boolean isMatch(String s,String p){
        if (s == null || p == null) {// 特殊情况判断
            return false;
        }
        boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
        dp[0][0] = true;//dp[i][j] -> s 的前 i 个是否能被 p 的前 j 个匹配
        for (int i = 0; i < p.length(); i++) {
            if (p.charAt(i) == '*' && dp[0][i - 1]) {
                dp[0][i + 1] = true; // 初始化
            }
        }
        for (int i = 0; i < s.length(); i++) {
            for (int j = 0; j < p.length(); j++) {
                if (p.charAt(j) == '.' || p.charAt(j) == s.charAt(i)){//任意元素或是元素匹配
                    dp[i + 1][j + 1] = dp[i][j];
                }
                if (p.charAt(j) == '*') {
                    if (p.charAt(j - 1) != s.charAt(i) && p.charAt(j - 1) != '.') {//前一个元素不匹配且不为任意元素
                        dp[i + 1][j + 1] = dp[i + 1][j - 1];
                    } else {
                        dp[i + 1][j + 1] = (dp[i + 1][j] || dp[i][j + 1] || dp[i + 1][j - 1]);
                            /*
                            dp[i][j] = dp[i-1][j] // 多个字符匹配的情况
                            or dp[i][j] = dp[i][j-1] // 单个字符匹配的情况
                            or dp[i][j] = dp[i][j-2] // 没有匹配的情况
                             */

                    }
                }
            }
        }
        return dp[s.length()][p.length()];
    }

后记

还没想好写什么…

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值