【数据结构】单链表

1.链表的概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

2. 链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了

3. 单链表的实现

首先在VS里面的源文件建立test.cSList.c,在头文件里面建立SList.h

SList.h:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

3.1 打印

SList.h:

void SListPrint(SLTNode* phead);//打印

SList.c:

#include "SList.h"
void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur != NULL)
    {
        printf("%d->", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

3.2 申请结点

SList.h:

SLTNode* BuyListNode(SLTDataType x);//动态申请结点

SList.c:

SLTNode* BuyListNode(SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    return newnode;
}

3.3 尾插

SList.h:

void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插

SList.c:

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);

    assert(*pphead != NULL);
    
    //找到尾结点
    SLTNode* tail = *pphead;
    while (tail->next != NULL)
    {
         tail = tail->next;
    }
    tail->next = newnode;
}

test.c:

#include "SList.h"
void TestSList1()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
}
int main()
{
    TestSList1();
    return 0;
}

运行结果:

3.4 头插

SList.h:

void SListPushFront(SLTNode** pphead, SLTDataType x);//头插

SList.c:

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newcode = BuyListNode(x);
    newcode->next = *pphead;
    *pphead = newcode;
}

test.c:

#include "SList.h"
void TestSList1()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListPushFront(&plist, 1);
    SListPushFront(&plist, 2);
    SListPushFront(&plist, 3);
    SListPrint(plist);
}
int main()
{
    TestSList1();
    return 0;
}

运行结果:

3.5 尾删

SList.h:

void SLisePopBack(SLTNode** pphead);//尾删

SList.c:

void SLisePopBack(SLTNode** pphead)
{
    assert(*pphead != NULL);
    SLTNode* prev = NULL;
    SLTNode* tail = *pphead;
    while (tail->next != NULL)
    {
        prev = tail;
        tail = tail->next;
    }
    free(tail);
    tail = NULL;
    prev->next = NULL;//一定要置空
}

test.c:

#include "SList.h"
void TestSList1()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListPushFront(&plist, 1);
    SListPushFront(&plist, 2);
    SListPushFront(&plist, 3);
    SListPrint(plist);
    SLisePopBack(&plist);
    SListPrint(plist);
}
int main()
{
    TestSList1();
    return 0;
}

运行结果:

3.6 头删

SList.h:

void SListPopFront(SLTNode** pphead);//头删

SList.c:

void SListPopFront(SLTNode** pphead)
{
    assert(*pphead != NULL);
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}

test.c:

#include "SList.h"
void TestSList1()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListPushFront(&plist, 1);
    SListPushFront(&plist, 2);
    SListPushFront(&plist, 3);
    SListPrint(plist);
    SLisePopBack(&plist);
    SListPrint(plist);
    SListPopFront(&plist);
    SListPrint(plist);
}
int main()
{
    TestSList1();
    return 0;
}

运行结果:

3.7 查找

SList.h:

SLTNode* SListFind(SLTNode* phead, SLTDataType x);//查找

SList.c:

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}

test.c:

#include "SList.h"
void TestSList2()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPushBack(&plist, 2);
    //Find接口有两个作用
    //1.找    2.修改
    SLTNode* pos = SListFind(plist, 2);
    int i = 1;
    while (pos)
    {
        printf("第%d个pos结点:%p->%d\n", i++, pos, pos->data);
        pos = SListFind(pos->next, 2);
    }
}
int main()
{
    TestSList2();
    return 0;
}

3.8 插入

3.8.1 前插

SList.h:

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//前插

SList.c:

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    //相当于头插
    if (*pphead == pos)
    {
        newnode->next = *pphead;
        *pphead = newnode;
    }
    else
    {
        //找到pos前一个位置
        SLTNode* posPrew = *pphead;
        while (posPrew->next != pos)
        {
            posPrew = posPrew->next;
        }
        posPrew->next = newnode;
        newnode->next = pos;
    }
}

test.c:

#include "SList.h"
void TestSList3()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SLTNode* pos = SListFind(plist, 2);
    if (pos)
    {
        SListInsert(&plist, pos,20);
    }
    SListPrint(plist);
}
int main()
{
    TestSList3();
    return 0;
}

运行结果:

3.8.2 后插

c++的STL库支持后插,一般我们单链表使用的都是后插。其次前插有一定的效率损失,而后插就没有损失。

SList.h:

void SListInsertAfter(SLTNode* pos, SLTDataType x);//后插

SList.c:

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

test.c:

#include "SList.h"
void TestSList4()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListInsertAfter(plist->next, 20);
    SListPrint(plist);
}
int main()
{
    TestSList4();
    return 0;
}

运行结果:

3.9删除

SList.h:

void SListErase(SLTNode** pphead, SLTNode* pos);//删除

SList.c:

void SListErase(SLTNode** pphead, SLTNode* pos)
{
    //头删,也可直接调用SListPopFront接口
    if (*pphead == pos)
    {
        *pphead = pos->next;
        free(pos);
        pos = NULL;//也可以不置空
    }
    else
    {
        SLTNode* prev = *pphead;
        while (prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
        pos = NULL;//也可以不置空
    }
}

test.c:

#include "SList.h"
void TestSList4()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListInsertAfter(plist->next, 20);
    SListPrint(plist);
    SListErase(&plist, plist->next->next);
    SListPrint(plist);
}
int main()
{
    TestSList4();
    return 0;
}

3.10 销毁

SList.h:

void SListDestory(SLTNode** pphead);//销毁

SList.c:

void SListDestory(SLTNode** pphead)
{
    assert(pphead);
    SLTNode* cur = *pphead;
    while (cur)
    {
        SLTNode* next = cur->next;
        free(cur);
        cur = next;
    }
    *pphead = NULL;
}

4.完整代码展示

SList.h:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;
void SListPrint(SLTNode* phead);//打印
SLTNode* BuyListNode(SLTDataType x);//动态申请结点
void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SListPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLisePopBack(SLTNode** pphead);//尾删
void SListPopFront(SLTNode** pphead);//头删
SLTNode* SListFind(SLTNode* phead, SLTDataType x);//查找
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//前插
void SListInsertAfter(SLTNode* pos, SLTDataType x);//后插
void SListErase(SLTNode** pphead, SLTNode* pos);//删除
void SListDestory(SLTNode** pphead);//销毁

SList.c:

#include "SList.h"
void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur != NULL)
    {
        printf("%d->", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

SLTNode* BuyListNode(SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    return newnode;
}

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {
        //找到尾结点
        SLTNode* tail = *pphead;
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        tail->next = newnode;
    }
}

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newcode = BuyListNode(x);
    newcode->next = *pphead;
    *pphead = newcode;
}

void SLisePopBack(SLTNode** pphead)
{
    SLTNode* prev = NULL;
    SLTNode* tail = *pphead;
    while (tail->next != NULL)
    {
        prev = tail;
        tail = tail->next;
    }
    free(tail);
    tail = NULL;
    prev->next = NULL;
}

void SListPopFront(SLTNode** pphead)
{
    assert(*pphead != NULL);
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    if (*pphead == pos)
    {
        newnode->next = *pphead;
        *pphead = newnode;
    }
    else
    {
        //找到pos前一个位置
        SLTNode* posPrew = *pphead;
        while (posPrew->next != pos)
        {
            posPrew = posPrew->next;
        }
        posPrew->next = newnode;
        newnode->next = pos;
    }
}

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

void SListErase(SLTNode** pphead, SLTNode* pos)
{
    //头删,也可直接调用SListPopFront接口
    if (*pphead == pos)
    {
        *pphead = pos->next;
        free(pos);
        pos = NULL;//也可以不置空
    }
    else
    {
        SLTNode* prev = *pphead;
        while (prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
        pos = NULL;//也可以不置空
    }
}

void SListDestory(SLTNode** pphead)
{
    assert(pphead);
    SLTNode* cur = *pphead;
    while (cur)
    {
        SLTNode* next = cur->next;
        free(cur);
        cur = next;
    }
    *pphead = NULL;
}

test.c:

#include "SList.h"
void TestSList1()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListPushFront(&plist, 1);
    SListPushFront(&plist, 2);
    SListPushFront(&plist, 3);
    SListPrint(plist);
    SLisePopBack(&plist);
    SListPrint(plist);
    SListPopFront(&plist);
    SListPrint(plist);
}
void TestSList2()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPushBack(&plist, 2);
    //Find接口有两个作用
    //1.找    2.修改
    SLTNode* pos = SListFind(plist, 2);
    int i = 1;
    while (pos)
    {
        printf("第%d个pos结点:%p->%d\n", i++, pos, pos->data);
        pos = SListFind(pos->next, 2);
    }
}
void TestSList3()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SLTNode* pos = SListFind(plist, 2);
    if (pos)
    {
        SListInsert(&plist, pos,20);
    }
    SListPrint(plist);
}
void TestSList4()
{
    SLTNode* plist = NULL;
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPrint(plist);
    SListInsertAfter(plist->next, 20);
    SListPrint(plist);
    SListErase(&plist, plist->next->next);
    SListPrint(plist);
}
int main()
{
    //TestSList1();
    //TestSList2();
    //TestSList3();
    TestSList4();
    return 0;
}

5.链表OJ 题

5.1 移除链表元素

删除链表中等于给定值 val 的所有节点。

链接:https://leetcode.cn/problems/remove-linked-list-elements/description/

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* prev=NULL,*cur=head;
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur==head)
            {
                //头删或者中间删除
                head=cur->next;
                free(cur);
                cur=head;
            }
            else
            {
                prev->next=cur->next;
                free(cur);
                cur=prev->next;
            }
        }
        else
        {
            //迭代向后走
            prev=cur;
            cur=cur->next;
        }
    }
    return head;
}

5.2 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

链接:https://leetcode.cn/problems/reverse-linked-list/description/

法一:画图+调试

struct ListNode* reverseList(struct ListNode* head){
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode* n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=head->next;
    while(n2)
    {
       //核心逻辑
       n2->next=n1;
       //迭代
       n1=n2;
       n2=n3;
       if(n3!=NULL)
          n3=n3->next;
    }
    return n1;
}

法二:头插法

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur=head;
    struct ListNode* newhead=NULL;
    while(cur)
    {
        struct ListNode* next =cur->next;
        //头插
        cur->next=newhead;
        //迭代
        newhead=cur;
        cur=next;
    }
    return newhead;
}

5.3 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

链接:https://leetcode.cn/problems/middle-of-the-linked-list/description/

//步幅法
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow=head,*fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}

5.4 链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

链接:https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&&tqId=11167&rp=2&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

 //千分尺法
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast=pListHead,*slow=pListHead;
    while(k--)
    {
        if ((fast==NULL)) {
            return  NULL;
        }
        fast=fast->next;
    }
    while(fast)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return  slow;;
}

5.5 合并链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/

思路:依次比较链表中的结点,每次取小的结点,尾插到新链表即可

法一:

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    //如果链表为空,返回另一个
    if(l1==NULL)
    {
        return l2;
    }
    if(l2==NULL)
    {
        return l1;
    }

    struct ListNode* head=NULL,*tail=NULL;
    //先取小的做第一个
    if(l1->val<l2->val)
    {
        head=tail=l1;
        l1=l1->next;
    }
    else
    {
        head=tail=l2;
        l2=l2->next;
    }
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            tail->next=l1;
            tail=l1;
            l1=l1->next;
        }
        else
        {
            tail->next=l2;
            tail=l2;
            l2=l2->next;
        }
    }
    if(l1)
    {
        tail->next=l1;
    }
    if(l2)
    {
        tail->next=l2;
    }
    return head;
}

法二:哨兵结点法

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    //如果链表为空,返回另一个
    if(l1==NULL)
    {
        return l2;
    }
    if(l2==NULL)
    {
        return l1;
    }

    struct ListNode* head=NULL,*tail=NULL;
    //哨兵位的头结点
    head=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            tail->next=l1;
            tail=l1;
            l1=l1->next;
        }
        else
        {
            tail->next=l2;
            tail=l2;
            l2=l2->next;
        }
    }
    if(l1)
    {
        tail->next=l1;
    }
    if(l2)
    {
        tail->next=l2;
    }
    struct ListNode* list = head->next;
    free(head);
    return list;
}

5.6 链表分割

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

链接:https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* lessHead,*lessTail,*greaterHead,*greaterTail;

        //开一个哨兵位头结点,方便尾插
        lessHead=lessTail=(struct ListNode*)malloc((sizeof(struct ListNode)));
        lessTail->next=NULL;
        greaterHead=greaterTail=(struct ListNode*)malloc((sizeof(struct ListNode)));
        greaterTail->next=NULL;

        struct ListNode* cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                lessTail->next=cur;
                lessTail=cur;
            }
            else 
            {
                greaterTail->next=cur;
                greaterTail=cur;
            }
            cur=cur->next;
        }
        
        lessTail->next=greaterHead->next;
        greaterTail->next=NULL;//易丢
        
        struct ListNode* newHead=lessHead->next;
        free(lessHead);
        free(greaterHead);
        
        return  newHead;
    }
};

5.7 链表的回文结构

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

链接:https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* slow = head, *fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    while (cur) {
        struct ListNode* next = cur->next;
        //头插
        cur->next = newhead;
        //迭代
        newhead = cur;
        cur = next;
    }
    return newhead;
}

class PalindromeList {
  public:
    bool chkPalindrome(ListNode* A) {
        struct ListNode* mid=middleNode(A);
        struct ListNode* rHead=reverseList(mid);

        struct ListNode* curA=A;
        struct ListNode* curR=rHead;
        while(curA&&curR)
        {
            if(curA->val!=curR->val)
            {
                return false;
            }
            else
            {
                curA=curA->next;
                curR=curR->next;
            }
        }
        return  true;
    }
};

5.8 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/description/

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* tailA=headA;
    struct ListNode* tailB=headB;
    int lenA=1;
    while(tailA->next)
    {
        lenA++;
        tailA=tailA->next;
    }
    int lenB=1;
    while(tailB->next)
    {
        lenB++;
        tailB=tailB->next;
    }

    //不相交
    if(tailA!=tailB)
    {
        return NULL;
    }

    int gap=abs(lenA-lenB);
    //长的先走,之后再同时走
    struct ListNode* longList=headA;
    struct ListNode* shortList=headB;
    if(lenA<lenB)
    {
        shortList=headA;
        longList=headB;
    }
    while(gap--)
    {
        longList=longList->next;
    }
    while(longList!=shortList)
    {
        longList=longList->next;
        shortList=shortList->next;
    }
    return longList;
}

  • 34
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 42
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值