剑指offer题库总结(三)之链表(C语言版本)

剑指offer题库总结(三)之链表(C语言版本)

题24. 反转链表

  • 题目描述:反转链表
    给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
    数据范围: 0≤n≤1000
    要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。
    如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。

  • 解题思路:采用递归的方法。
    假如链表只有2个元素,那么为:head head-next这两个节点,分别是节点0和节点1。反转就需要将节点1指向节点0:head->next->next = head;节点0指向null:head->next = NULL。每次都调用这两步操作。递归的终止条件:head或者head->next为空,说明链表遍历完成。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* ReverseList(struct ListNode* pHead ) {
    // write code here
    if(pHead == NULL || pHead->next == NULL)  //终止条件
    {
        return pHead;
    }
    struct ListNode *Reverse = ReverseList(pHead->next); //
    pHead->next->next = pHead;
    pHead->next = NULL;
    return Reverse;
}

题6.从尾到头打印链表

  • 题目描述:
    输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
    如输入{1,2,3}的链表如下图:
    在这里插入图片描述
    返回一个数组为[3,2,1]
    0 <= 链表长度 <= 10000

  • 示例:
    输入:{1,2,3}
    返回值:[3,2,1]

  • 解题思路:
    1.考虑边界值的判断,如果不合法返回值为-1;

  1. 明确题目要求返回值是一个数组,那需要获取该链表的长度作为数组的大小,在堆中开辟一块数组空间,同时倒序填写到数组中。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param listNode ListNode类 
 * @return int整型一维数组
 * @return int* returnSize 返回数组行数
 */
int* printListFromTailToHead(struct ListNode* listNode, int* returnSize ) {
    // write code here
    
    if(!listNode)
    {
        return -1;
    }
    struct ListNode *p = listNode;
    int len = 0;
    //判断链表有多少个元素
    while(p)
    {
        p = p->next;
        len++;
    }
    //在堆中开辟一块数组空间。
    int *Arr = (int *)malloc(len * sizeof(int));
    p = listNode;
    for(int i = len - 1; i >= 0; i--)
    {
        Arr[i] = p->val;
        p = p->next;
    }
    *returnSize = len;
    return Arr;
}

题18. 删除链表的节点

  • 题目描述:
    给定单向链表的头指针和一个要删除的节点的值(前提:链表中各值都不相等),定义一个函数删除该节点。返回删除后的链表的头节点。
  • 数据范围:
    0<=链表节点值<=10000
    0<=链表长度<=10000
  • 示例:
    输入:{2,5,1,9},5
    返回值:{2,1,9}
    说明:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 2 -> 1 -> 9
  • 解题思路:
    1.边界条件:如果非法,直接返回head;
    2.因为链表无法随机访问元素,必须从链表头部开始遍历,先需要查找到该元素的位置,采用双指针,一个指针为另一个指针的下一个元素,如:p = p2->next;
    3.删除元素:
    (1)情况1:元素就在头结点,那直接返回head->next;
    (2)情况2:元素不在头结点,那采用双指针遍历并删除,将前一个结点直接指向当前结点的下一个指针就删除了当前结点:p2->next = p->next;(实际p->next就是p2->next->next)。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param val int整型 
 * @return ListNode类
 */
struct ListNode* deleteNode(struct ListNode* head, int val ) {
    // write code here
    if(head == NULL)
    {
        return head;
    }
    struct ListNode *p = head;
    struct ListNode *p2 = head;
    int temp;
    if(p->val == val)
    {
        return head->next;
    }
    while(p->next)
    {
        temp = p->val;
        if(temp == val)
        {
            p2->next = p->next;
            return head;
        }
        p2 = p;
        p = p->next;
    }
    return head;
}

题22. 链表中倒数最后k个结点

  • 题目描述:
    输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
    如果该链表长度小于k,请返回一个长度为 0 的链表。

  • 数据范围:0≤n≤105,0≤ai​≤109,0 ≤k≤10^9

  • 要求:空间复杂度 O(n),时间复杂度 O(n)

  • 进阶:空间复杂度 O(1),时间复杂度 O(n)

  • 例如:输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
    在这里插入图片描述
    其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。

  • 示例1:
    输入:{1,2,3,4,5},2
    返回值:{4,5}
    说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。

  • 示例2:
    输入:{2},8
    返回值:{}

  • 解题思路:
    方法一:先通过遍历的方式获取链表长度len,然后判断len与k的大小,若k大则返回空链表,否则从头遍历到len-k的位置。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
    // write code here
    if((!pHead) || (0 >= k))
    {
        return NULL;
    }   
    struct ListNode *tmp = (struct ListNode *)malloc(sizeof(struct ListNode));
    int len = 0;
    tmp = pHead;
    while(tmp)
    {
        len++;
        tmp = tmp->next;
    }                 
    if(k > len)
    {
        return NULL;
    }
    else
    {
       tmp = pHead;
       for(int i = 0; i < len - k; i++)
       {
           tmp = tmp->next;
       }
        return tmp;
    }
}
  • 方法二:
    双指针移动的方法,一个指针先移动k步,然后第2个指针和第1个指针同时移动,那么第2个指针就与第一个指针相差k步,当第一个指针到达链表尾部,那么返回第2个指针(此时它指的位置就是倒数第k个节点)。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
    // write code here
    if((!pHead) || (0 >= k))
    {
        return NULL;
    }   
    struct ListNode *tmp1 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *tmp2 = (struct ListNode *)malloc(sizeof(struct ListNode));
    tmp1 = pHead;
    tmp2 = pHead;
    int i;
    for(i = 0; i < k; i++)
    {
        if(tmp1 != NULL)
        {
            tmp1 = tmp1->next;
        }
        else //k大于链表长度时,返回空链表
        {
            return NULL;
        }
    }
    while(tmp1)
    {
        tmp1 = tmp1->next;
        tmp2 = tmp2->next;
    }
    return tmp2;
}

题52. 两个链表的第一个公共结点

  • 题目描述:
    输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
  • 数据范围: n≤1000
  • 要求:空间复杂度 O(1),时间复杂度 O(n)
    例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
    在这里插入图片描述可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。
  • 返回值描述:
    返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。
  • 示例1
    输入:{1,2,3},{4,5},{6,7}
    返回值:{6,7}
  • 说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分 这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的 。
  • 解题思路:
  • 方法1:常规思路
    (1)先判断两个链表是否为NULL,只要其中一个为NULL,则没法相交。
    (2)先获取每个链表的长度,分别记为len1和len2,然后比较做差值diff = len1-len2;如果diff>=0,则让pHead1先走diff步,否则让pHead2先走diff步,然后两个指针同时走,当两个指针pHead1 == pHead2时,说明两个指针同时指向一个节点了。返回其中一个指针即可。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    if(pHead1==NULL||pHead2==NULL)
    {
        return NULL;       
    } 
    struct ListNode *p1 = pHead1;
    struct ListNode *p2 = pHead2;
    int len1 = 0;
    int len2 = 0;
    int diff = 0; //记录两个链表相差步数。
    
    //先获取链表的长度
    while(p1)
    {
        len1++;
        p1 = p1->next;
    }
    while(p2)
    {
        len2++;
        p2 = p2->next;
    }
    diff = len1 - len2;
    //哪个链表长,则先移动diff步
    if(diff >=0)
    {
        //前面计算了链表长度,已经指向空了,指针复位到链表头结点。
        p1 = pHead1;
        p2 = pHead2;
        for(int i =0; i < diff; i++)
        {
            p1 = p1->next;
        }
        for(int j = 0; j < len2; j++)
        {
            if(p1 == p2)
            {
                return p1;
            }
            else
            {
                p1 = p1->next;
                p2 = p2->next;
            } 
        }
    }
    else //diff<0
    {
        p1 = pHead1;
        p2 = pHead2;
        for(int i = 0; i < 0 - diff; i++)
        {
            p2 = p2->next;
        }
        for(int j = 0; j < len1; j++)
        {
            if(p1 == p2)
            {
                return p1;
            }
            else
            {
                p1 = p1->next;
                p2 = p2->next;
            } 
        }
    } 
    return NULL;
}
  • 方法2:两个指针遍历2个链表
    (1)先判断两个链表是否为NULL,只要其中一个为NULL,则没法相交。
    (2)两个链表同时开始走,分别遍历pHead1和pHead2,由于不知道两个链表的长度,则当其中一个链表先到达NULL时,则将该指针指向另一个链表的头指针继续一起遍历。这样到达公共结点时,两个指针的移动步数相等,只是过程不一样。pHead1走了a+c+b;pHead2走了b+c+a步(a+c是pHead1的长度,b+c是pHead2的长度)。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    if(pHead1==NULL||pHead2==NULL)
    {
        return NULL;       
    } 
    struct ListNode *p1 = pHead1;
    struct ListNode *p2 = pHead2;
    int i=0;
    int j=0;
    
    while(1)
    {
        if(p1 == p2)
        {
            return p1;
        }  
        p1 = p1->next;
        p2 = p2->next;
        if(p1 == NULL)
        {
            p1 = pHead2;
            i++;
            if(i == 2) //只能出现一次从p1的尾部重新指向到p2的头部,否则就是无交集
            {
                return NULL;
            }
        }
        if(p2 == NULL)
        {
            p2 = pHead1;
            j++;
            if(j == 2)
            {
                return NULL;
            } 
        }
    }
}

题25. 合并两个排序的链表

  • 题目描述:
    输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

  • 数据范围: 0≤n≤1000,−1000≤节点值≤1000

  • 要求:空间复杂度 O(1),时间复杂度 O(n)
    如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
    在这里插入图片描述
    或输入{-1,2,4},{1,3,4}时,合并后的链表为{-1,1,2,3,4,4},所以对应的输出为{-1,1,2,3,4,4},转换过程如下图所示:
    在这里插入图片描述

  • 示例1
    输入:{1,3,5},{2,4,6}
    返回值:{1,2,3,4,5,6}

  • 示例2
    输入:{},{}
    返回值:{}

  • 示例3
    输入:{-1,2,4},{1,3,4}
    返回值:{-1,1,2,3,4,4}

  • 解题思路:
    初始化:定义cur指向新链表的头结点,有一个技巧:创建单链表,都会设一个虚拟头结点,也叫哨兵,因为这样每一个结点都有一个前驱结点。

  • 操作:
    如果p1指向的结点值小于等于p2指向的结点值,则将p1指向的结点值链接到cur的next指针,然后p1指向下一个结点值
    否则,让p2指向下一个结点值
    循环步骤1,2,直到p1或者p2为nullptr
    将p1或者p2剩下的部分链接到cur的后面。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    int tmp;
    struct ListNode *pNew = ( struct ListNode *)malloc(sizeof( struct ListNode));
    struct ListNode *pNewTemp = pNew;
    if((pHead1 == NULL) && (pHead2 == NULL))
    {
        return NULL;
    }
    while((pHead1) && (pHead2))
    {
        //比较两个指针的大小
        if(pHead1->val <= pHead2->val)
        {
            pNewTemp->next = pHead1;
            pHead1 = pHead1->next;
        }
        else
        {
            pNewTemp->next = pHead2;
            pHead2 = pHead2->next;
        }
        pNewTemp = pNewTemp->next;
    }
    //当其中一个链表已经移动到尾部后,则将pNewTemp的next指针指向非空指针,相当于将剩下的成员拼接。
    pNewTemp->next = pHead1 ? pHead1 : pHead2;
    return pNew->next; //虚拟指针向下next一次。
}

题76.删除链表中重复的结点

题目描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5

  • 数据范围:链表长度满足 0≤n≤1000 ,链表中的值满足 1≤val≤1000

  • 进阶:空间复杂度 O(n),时间复杂度 O(n)
    例如输入{1,2,3,3,4,4,5}时,对应的输出为{1,2,5},对应的输入输出链表如下图所示:
    在这里插入图片描述

  • 示例1
    输入:{1,2,3,3,4,4,5}
    返回值:{1,2,5}

  • 示例2
    输入:{1,1,1,8}
    返回值:{8}

  • 解题思路:
    采用双指针,一个表示当前节点的指针cur,另一个指针表示当前节点的下一个节点diff,然后进行比较。主要伪代码如下:
    if((pHead = NULL) || (pHead->next == NULL))
    说明就是空链表或者只有一个节点的链表,直接return pHead;
    然后遍历链表:
    如果当前节点cur->val == diff->val && diff不是NULL,说明cur和diff是重复节点:
    diff = diff->next; diff指向下一个节点也cur继续比较,知道不相等且不为NULL
    if(cur->next == diff && diff) //表示cur和diff是值不相等的
    pre->next = cur; //创建的pre指针是cur的前一个结点。
    pre = cur;
    cur = diff;
    else //值相等
    cur = diff;

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* deleteDuplication(struct ListNode* pHead ) {
    // write code here
    if((pHead == NULL) || (pHead->next == NULL))
    {
        return pHead;
    }
    //创建一个新节点,可以规避掉头节点是重复值的情况,达到统一处理的效果
    struct ListNode *preHead = (struct ListNode *)malloc(sizeof(struct ListNode));    
    preHead->next = pHead;
    struct ListNode *pre = preHead; //指向当前节点的前一个结点
    struct ListNode *cur = pHead; //指向当前节点
    while(cur)
    {
        //创建一个节点表示当前节点的下一个节点,用来和当前节点对比
        struct ListNode *diff = cur->next; 
        while((diff) && (diff->val == cur->val)) //当前结点和下一个节点的值重复了
        {
            diff = diff->next;
        }
        if(cur->next == diff) //如果代表前后两个数不同,那么当前节点下一个节点就是diff,可以更新pre和cur
        {      
            pre->next = cur;
            pre = cur;
            cur = diff;
        }
        else
        {
            cur = diff;  //这里表示当前cur有重复值,那么就把cur更新到下一个数,pre不动
        }
    }
    pre->next = cur; //实际这里跳出循环,即cur此时为NULL
    return preHead->next;
}

题23.链表中环的入口结点

  • 题目描述
    给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
  • 数据范围: n≤10000,1<=结点值<=10000
  • 要求:空间复杂度 O(1),时间复杂度 O(n)
    例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
    在这里插入图片描述
    可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
  • 示例1
    输入:{1,2},{3,4,5}
    返回值:3
    说明:返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3

输入:{1},{}
返回值:“null”
说明:没有环,返回对应编程语言的空结点,后台程序会打印"null"

输入:{},{2}
返回值:2
说明:环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2

  • 解题思路:
    方法1:类似hash法,记录每次遍历到的结点
    因为每个节点的值都是大于0的,小于10000,定义一个指针遍历,遍历到当前的就取负,如果遍历到一个节点他的值是负的就证明以前遍历过了,也就有环了,取个相反数返回就行。
    空间复杂度 O(1),时间复杂度 O(n),符合题意。
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* EntryNodeOfLoop(struct ListNode* pHead ) {
    // write code here
    if(pHead == NULL)
    {
        return pHead;
    }
    struct ListNode *p = pHead;
    while(p != NULL && p->next != NULL)
    {
        if(p->val > 0)
        {
            p->val = 0 - p->val; //值取相反数,表示其曾经遍历到了
            p = p->next;
        }
        else //该值小于0则是曾经遍历到了,因为题中说所有元素都是大于0的。
        {
            p->val = 0 - p->val; //把值再做相反数,还原回来。
            return p;
        }
    }
    return NULL;
}

方法2:快慢指针
通过定义slow和fast指针,slow每走一步,fast走两步,若是有环,则一定会在环的某个结点处相遇(slow == fast),根据下图分析计算,可知从相遇处到入口结点的距离与头结点与入口结点的距离相同。
空间复杂度 O(1),时间复杂度 O(n),符合题意。
利用快慢指针遍历链表,快指针每次移动2步,慢指针每次移动1步,若链表带环,则两指针一定会在环中相遇。那么接下来要找这个环的入口了。
我们假设从头结点到环形入口结点的结点数为x。环形入口结点到fast指针与slow指针相遇结点,结点数为y。从相遇结点再到环形入口结点,结点数为z。如图所示:
在这里插入图片描述当两个指针相遇时,slow指针走过的节点数为x + y,fast指针走过的节点数为x + y + n (y + z)(n为fast指针在环内走了n圈才遇到slow指针,(y+z)为 一圈内节点的个数A)。

因为fast指针是一步走两个结点,slow指针一步走一个结点, 所以 fast指针走过的结点数 = slow指针走过的结点数 * 2 即(x + y) * 2 = x + y + n (y + z) 故可得: x + y = n (y + z)。

那么环形的入口即x的距离:x = n (y + z) - y => x = (n - 1) (y + z) + z

当 n为1的时候, x = z,即从头结点出发一个指针,从相遇结点也出发一个指针,这两个指针每次只走一个结点, 那么当这两个指针相遇的时候就是环形入口的结点。

所以在相遇结点处定义一个指针index1,在头结点定义一个指针index2。它们同时移动,每次移动一个结点,当两者相遇时就是环形入口的结点。n大于1同理,index1指针在环里多转了(n-1)圈后,然后再遇到index2,相遇点依然是环形的入口节点。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* EntryNodeOfLoop(struct ListNode* pHead ) {
    // write code here
    if(pHead == NULL)
    {
        return pHead;
    }
    //定义2个快慢指针
    struct ListNode *fast = pHead;
    struct ListNode *slow = pHead;
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next; //快指针每次移动2步
        slow = slow->next;  //慢指针每次移动1步
        if (slow == fast) {
                struct ListNode *index1 = fast;  //相遇结点
                struct ListNode *index2 = pHead; //重新定义一个指针指向头结点
                //相遇结点和头指针同时移动,当两个结点相等则是环结点的入口
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
    }
    return NULL;
}

单题更新请关注: 知乎账号

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值