链表相关力扣练习题

移除链表元素

力扣链接

题意

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

法1—直接使用原来的链表进行删除

移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。 所以需要单独写一段逻辑来处理移除头结点的情况。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
        //删除头结点
        while(nullptr!=head&&head->val==val)
        {
            ListNode* temp=head;
            head=head->next;
            delete temp;
        }
        //删除非头结点
        ListNode* cur=head;
        while(cur!=nullptr&&cur->next!=nullptr)
        {
            if(cur->next->val==val)
            {
                ListNode* temp = cur->next;
                cur->next=temp->next;
                delete temp;
            }
            else
            {
                cur=cur->next;
            }
        }
        return head;
    }
};


法2—设置一个虚拟头结点

这样原链表的所有节点就都可以按照统一的方式进行移除了。 

最后呢在题目中,return 头结点的时候,别忘了 return dummyNode->next;, 这才是新的头结点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
       ListNode* dummyHead = new ListNode(0,nullptr);
       dummyHead->next=head;
       ListNode* cur = dummyHead;
       while(cur->next!=nullptr)
       {
           if(cur->next->val==val)
           {
               ListNode* del_node = cur->next;
               cur->next=del_node->next;
               delete del_node;
           }
           else
           {
               cur=cur->next;
           }
       }
       head=dummyHead->next;
       delete dummyHead;
       return head;
    }
};

设计链表

力扣 

题意

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

解题思路

使用虚拟头结点来实现这些方法会方便很多,都是一些链表基础性的操作,看代码就懂了。

class MyLinkedList 
{
public:
    //链表结点设计
    struct LinkedNode
    {
        int val;
        LinkedNode* next;
        LinkedNode(int value):val(value),next(nullptr){}
    };
private:
    int _size;// 链表结点个数
    LinkedNode* _dummyHead;//虚拟头结点
public:
    //链表初始化操作
    MyLinkedList() 
    {
        _size=0;
        _dummyHead=new LinkedNode(0);
    }
    //获取链表中第 index 个节点的值。如果索引无效,则返回-1。
    //注意,index从0开始的。有_size个节点,则索引范围为:0~_size-1
    int get(int index) 
    {
        if(index<0||index>_size-1)
            return -1;
        LinkedNode* cur=_dummyHead->next;
        while(index--)
        {
            cur=cur->next;
        }
        return cur->val;
    }
    //在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
    void addAtHead(int val) 
    {
        LinkedNode* add_node=new LinkedNode(val);
        add_node->next=_dummyHead->next;
        _dummyHead->next=add_node;
        _size++;
    }
    //将值为 val 的节点追加到链表的最后一个元素。
    void addAtTail(int val) 
    {
        LinkedNode* add_node=new LinkedNode(val);
        LinkedNode* tail_node=_dummyHead;
        while(tail_node->next!=nullptr)
        {
            tail_node=tail_node->next;
        }
        tail_node->next=add_node;
        _size++;
        
    }

    /*
在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
*/
    void addAtIndex(int index, int val) 
    {
        //index大于链表长度
        if(index>_size) 
        {
            cout<<"插入位置大于链表长度"<<endl;
            return;
        }
        //如果index小于0,则在头部插入节点。
        if(index<0)
        {
            LinkedNode* add_node = new LinkedNode(val);
            add_node->next=_dummyHead->next;
            _dummyHead->next=add_node;
            _size++;
        }
        else // index范围:0~_size
        {
            //寻找第index-1个节点
            LinkedNode* cur=_dummyHead;
            while(index--)
            {
                cur=cur->next;
            }

            LinkedNode* add_node = new LinkedNode(val);
            add_node->next=cur->next;
            cur->next=add_node;
            _size++;

        }
    }
    
    void deleteAtIndex(int index) 
    {
        // 如果索引 index 有效,则删除链表中的第 index 个节点。
        if(index>=0&&index<_size)
        {
            LinkedNode* cur=_dummyHead;
            while(index--)
            {
                cur=cur->next;
            }
            LinkedNode* del_node=cur->next;
            cur->next=del_node->next;
            delete del_node;
            _size--;
        }
    }
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */


反转链表

力扣 

题意

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

法1—分组

之前在讲解单链表时,讲过反转链表的代码,这里再用两种办法解决此题。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* reverseList(ListNode* head) 
    {
        if(nullptr==head || nullptr==head->next)
            return head;
        ListNode* temp=head->next;
        head->next=nullptr;
        while(nullptr!=temp)
        {
            //依次将temp所指向的节点插入到头部
            ListNode* add_node=temp;
            temp=temp->next;
            add_node->next=head;
            head=add_node;
        }
        return head;
    }
};

 法2—双指针

  • 首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
  • 然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下cur这个节点的下一个节点。
  • 为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
  • 接下来,就是循环往下走,继续移动pre和cur指针。
  • 最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* reverseList(ListNode* head) 
    {
        if(nullptr==head || nullptr==head->next)
            return head;
        ListNode* temp;//保存cur的下一个节点
        ListNode* pre = nullptr;
        ListNode* cur=head;
        while(nullptr!=cur)
        {
            temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
        
    }
};

 法3—递归

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* reverse(ListNode* pre,ListNode* cur)
    {
        if(nullptr==cur)
            return pre;
        ListNode* temp=cur->next;
        cur->next=pre;
        return reverse(cur,temp);

    }
    ListNode* reverseList(ListNode* head) 
    {
        if(nullptr==head || nullptr==head->next)
            return head;
        return reverse(nullptr,head);
        
    }
};

两两交换链表的中的节点

力扣

题意

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 

解题思路

这道题目正常模拟就可以了。 建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。

创建虚拟头结点dummyHead,令 dummyHead.next = head。令 cur 表示当前到达的节点,初始时 cur= dummyHead。每次需要交换 cur后面的两个节点。

如果 cur 的后面没有节点或者只有一个节点,则没有更多的节点需要交换,因此结束交换。否则,获得 cur 后面的两个节点 node1 和 node2,通过更新节点的指针关系实现两两交换节点。

具体而言,交换之前的节点关系是 cur-> node1 -> node2,交换之后的节点关系要变成 cur-> node2 -> node1,因此需要进行如下操作:

cur.next = node2;
node1.next = node2.next;//节点1的next指向节点2的next
node2.next = node1;//节点2的next指向节点1

完成上述操作之后,节点关系即变成 cur-> node2 -> node1->.....。再令 cur= node1,对链表中的其余节点进行两两交换,直到全部节点都被两两交换。

两两交换链表中的节点之后,新的链表的头节点是 dummyHead.next,返回新的链表的头节点即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* swapPairs(ListNode* head)
    {
        if(nullptr==head)
            return nullptr;
        ListNode* dummyHead=new ListNode(0,head);
        ListNode* cur=dummyHead;
        while(cur->next!=nullptr&&cur->next->next!=nullptr)
        {
            ListNode* node1=cur->next,*node2=cur->next->next;
            cur->next=node2;
            node1->next=node2->next;
            node2->next=node1;
            cur=cur->next->next;
        }
        head=dummyHead->next;
        delete dummyHead;
        return head;
    }
};

删除链表的倒数第N个节点

力扣

题意

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 

解法1—栈

我们也可以在遍历链表的同时将所有节点依次入栈。根据栈「先进后出」的原则,我们弹出栈的第 n 个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点。这样一来,删除操作就变得十分方便了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        if(!head)
            return head;
        ListNode* dummy_head=new ListNode(0,head);
        stack<ListNode*> my_stack;
        ListNode* cur=dummy_head;
        while(cur)
        {
            my_stack.push(cur);
            cur=cur->next;
        }
        for(int i=0;i<n;i++)
        {
            my_stack.pop();
        }
        ListNode* pre_delNode=my_stack.top();//待删除节点的上一个节点
        ListNode* delNode=pre_delNode->next;
        pre_delNode->next=delNode->next;
        delete delNode;
        head=dummy_head->next;
        delete dummy_head;
        return head;
    }
};

 解法2—双指针

双指针的经典应用。

由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 fast 和 slow 同时对链表进行遍历,并且 fast 比 slow 超前 n 个节点。当fast 遍历到链表的末尾时(即 fast 为空指针),slow 就恰好处于倒数第 n 个节点。

具体地,初始时 fast 和 slow 均指向头节点。我们首先使用 fast 对链表进行遍历,遍历的次数为 n。此时,slow 和 fast之间间隔了 n−1 个节点,即 slow 比 fast 超前了 n 个节点。

在这之后,我们同时使用 slow 和 fast 对链表进行遍历。当 fast 遍历到链表的末尾(即 fast为空指针)时,slow 恰好指向倒数第 n 个节点。

如果我们能够得到的是倒数第 n 个节点的前驱节点而不是倒数第 n 个节点的话,删除操作会更加方便。因此我们可以考虑在初始时将slow指向虚拟头节点,其余的操作步骤不变。这样一来,当 fast遍历到链表的末尾时,slow 的下一个节点就是我们需要删除的节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        if(!head)
            return head;
        ListNode* dummy_head=new ListNode(0,head);
        ListNode* fast,*slow;
        fast=head;
        slow=dummy_head;
        while(n--&&fast!=nullptr)
        {
            fast=fast->next;
        }
        while(fast!=nullptr)
        {
            fast=fast->next;
            slow=slow->next;
        }
        ListNode* del_node=slow->next;
        slow->next=del_node->next;
        delete del_node;
        head=dummy_head->next;
        delete dummy_head;
        return head;
    }
};

链表相交

力扣

题意

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

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

解法1

设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 cc ,则有:

  • 头节点 headA 到 node 前,共有 a - c 个节点;
  • 头节点 headB 到 node 前,共有 b - c 个节点;

考虑构建两个节点指针 A​ , B 分别指向两链表头节点 headA , headB ,做如下操作:

  • 指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:

        a + (b - c)

  • 指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:

        b + (a - c)

 如下式所示,此时指针 A , B 重合:a+(b−c)=b+(a−c)。

  • 若两链表 有 公共尾部 (即 c > 0 ) :指针 A , B 同时指向「第一个公共节点」node 。
  • 若两链表 无 公共尾部 (即 c = 0 ) :指针 A , B 同时指向 null 。

 因此返回 A 即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution 
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        ListNode* A=headA,*B=headB;
        while(A!=B)
        {
            A=A!=nullptr?A->next:headB;
            B=B!=nullptr?B->next:headA;
        }
        return A;
    }
};

解法2

简单来说,就是求两个链表交点节点的指针。我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

 此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution 
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) 
        { 
            // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) 
        { 
            // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) 
        {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) 
        {
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) 
        {
            if (curA == curB) 
            {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};


环形链表II

力扣

题意

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

 

思路

判断链表是否有环

可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。 

为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢?

首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。

那么来看一下,为什么fast指针和slow指针一定会相遇呢?

可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针,会发现最终都是这种情况, 如下图:

fast和slow各自再走一步, fast和slow就相遇了。

这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。 

如果有环,如何找到环的入口?

假设从头结点到环形入口节点 的节点数为x环形入口节点到 fast指针与slow指针相遇节点 节点数为y从相遇节点 再到环形入口节点节点数为 z。 如图所示:

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数。

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

 两边消掉一个(x+y): x + y = n (y + z)

 因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y 。

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z;

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,因为有环,所以他们总会遇到的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution 
{
public:
    ListNode *detectCycle(ListNode *head) 
    {
        ListNode* fast,*slow;
        fast=slow=head;
        while(fast!=nullptr&&fast->next!=nullptr)
        {
            slow=slow->next;
            fast=fast->next->next;
            if(slow==fast)
            {
                //有环
                ListNode* index1=fast;
                ListNode* index2=head;
                while(index1!=index2)
                {
                    index1=index1->next;
                    index2=index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
};

补充:

为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?

首先slow进环的时候,fast一定是先进环来了。

如果slow进环入口,fast也在环入口,那么把这个环展开成直线,就是如下图的样子:

可以看出如果slow 和 fast同时在环入口开始走,一定会在环入口3相遇,slow走了一圈,fast走了两圈。 

重点来了,slow进环的时候,fast一定是在环的任意一个位置,如图:

那么fast指针走到环入口3的时候,已经走了k + n 个节点,slow相应的应该走了(k + n) / 2 个节点。因为k是小于n的(图中可以看出),所以(k + n) / 2 一定小于n。

也就是说slow一定没有走到环入口3,而fast已经到环入口3了

这说明什么呢?

在slow开始走的那一环已经和fast相遇了

为什么fast不会跳过slow呢? 在上面已经说过一次了,fast相对于slow是一次移动一个节点,所以不可能跳过去

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值