(应届生学习之路)3、day3剑指offer10道算法,面试题5道

剑指offer10道

1、合并两个排序的链表(简单)

描述

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

数据范围: 0≤n≤10000≤n≤1000,−1000≤节点值≤1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)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}

答案

依然递归,两个为空返回空,一个为空返回另一个,然后判断两个结点哪个大,哪个大就找他下一个

public ListNode Merge (ListNode pHead1, ListNode pHead2) {
        // write code here
        if(pHead1 == null && pHead2 == null) return null;
        if(pHead1 == null) return pHead2;
        if(pHead2 == null) return pHead1;
        if(pHead1.val <= pHead2.val){
            pHead1.next = Merge(pHead1.next,pHead2);
            return pHead1;
        }else{
            pHead2.next = Merge(pHead1,pHead2.next);
            return pHead2;
        }
    }

2、树的子结构(中等)

描述

输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)

假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构

数据范围:

0 <= A的节点个数 <= 10000

0 <= B的节点个数 <= 10000

示例1

输入:

{8,8,7,9,2,#,#,#,#,4,7},{8,9,2}
返回值:

true

示例2

输入:

{1,2,3,4,5},{2,4}
返回值:

true

示例3

输入:

{1,2,3},{3,1}
返回值:

false

答案

依然递归,深度,判断两个都空就返回false,某个为空的时候,子树为空那么就返回true,母树为空就返回false,判断当前的两个结点的值是否相等,若相等就继续判断左子树和右子树的有没有相等,如果不等就返回false,然后又是一个递归,找到最终为true的IsSame就返回true,如果没有递归母树的左子树和右子树找

public boolean HasSubtree(TreeNode root1, TreeNode root2) {
        if(root2 == null||root1 == null) return false;
        return IsSame(root1,root2) || HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);
    }

    boolean IsSame(TreeNode root1, TreeNode root2) {
        
        if (root2 == null) return true;
        if (root1 == null) return false;
        if(root1.val != root2.val){
            return false;
        }
        return IsSame(root1.left,root2.left) && IsSame(root1.right,root2.right);
    }

3、二叉树的镜像(简单)

描述

操作给定的二叉树,将其变换为源二叉树的镜像。

数据范围:二叉树的节点数 0≤n≤10000≤n≤1000 , 二叉树每个节点的值 0≤val≤10000≤val≤1000

要求: 空间复杂度 O(n)O(n) 。本题也有原地操作,即空间复杂度 O(1)O(1) 的解法,时间复杂度 O(n)O(n)

比如:

源二叉树

镜像二叉树

示例1

输入:

{8,6,10,5,7,9,11}
返回值:

{8,10,6,11,9,7,5}
说明:

如题面所示

示例2

输入:

{}
返回值:

{}

答案

依然递归,如果为空的话就直接返回,不为空的话调转左右子树,然后先递归左子树,先把左子树左右调换,再递归右子树

public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot == null) return pRoot;
        TreeNode tmp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = tmp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }

4、顺时针打印矩阵(简单)

描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:

[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]

则依次打印出数字

[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]

数据范围:

0 <= matrix.length <= 100

0 <= matrix[i].length <= 100

示例1

输入:

[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
返回值:

[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]

示例2

输入:

[[1,2,3,1],[4,5,6,1],[4,5,6,1]]
返回值:

[1,2,3,1,1,1,6,5,4,4,5,6]

答案

设置这个矩阵有顶边界(top)、底边界(bottom)、左边界(left)、右边界(right),每遍历一圈,就依次将这些边界缩小

import java.util.*;
import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> res = new ArrayList<>();
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        if (matrix == null || matrix.length == 0) return res;
        int m = matrix.length;
        int n = matrix[0].length;
        print(0,n-1,0,m-1,matrix);
        return res;
    }
    void print(int left, int right, int top, int bottom, int [][] matrix) {
        for (int i = left; i <= right; i++) res.add(matrix[top][i]);
        top++;
        if (left > right || top > bottom) return;
        for (int i = top; i <= bottom; i++) res.add(matrix[i][right]);
        right--;
        if (left > right || top > bottom) return;
        for (int i = right; i >= left; i--) res.add(matrix[bottom][i]);
        bottom--;
        if (left > right || top > bottom) return;
        for (int i = bottom; i >= top; i--) res.add(matrix[i][left]);
        left++;
        print(left,right,top,bottom,matrix);
    }
}

5、包含min函数的栈(简单)

描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。

此栈包含的方法有:

push(value):将value压入栈中

pop():弹出栈顶元素

top():获取栈顶元素

min():获取栈中最小元素

数据范围:操作数量满足 0≤n≤300 0≤n≤300  ,输入的元素满足 ∣val∣≤10000 ∣val∣≤10000 
进阶:栈的各个操作的时间复杂度是 O(1) O(1)  ,空间复杂度是 O(n) O(n) 

示例:

输入:    ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]

输出:    -1,2,1,-1

解析:

"PSH-1"表示将-1压入栈中,栈中元素为-1

"PSH2"表示将2压入栈中,栈中元素为2,-1

“MIN”表示获取此时栈中最小元素==>返回-1

"TOP"表示获取栈顶元素==>返回2

"POP"表示弹出栈顶元素,弹出2,栈中元素为-1

"PSH1"表示将1压入栈中,栈中元素为1,-1

"TOP"表示获取栈顶元素==>返回1

“MIN”表示获取此时栈中最小元素==>返回-1

示例1

输入:

 ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
返回值:

-1,2,1,-1

答案

这个就是弄一个辅助栈,每次压入的时候判断辅助栈中的顶是不是最小,哪个小就压入辅助栈,删除的时候两个一起删除,最后peek辅助栈得到最小

import java.util.*;
import java.util.Stack;

public class Solution {

    Stack<Integer> s1 = new Stack<>();
    //用于存储最小min
    Stack<Integer> s2 = new Stack<Integer>();
    public void push(int node) {
        s1.add(node);
        if(s2.isEmpty() || s2.peek() > node){
            s2.push(node);
        }else{
            s2.push(s2.peek());
        }
    }
    
    public void pop() {
        s1.pop();
        s2.pop();
    }
    
    public int top() {
        return s1.peek();
    }
    
    public int min() {
        return s2.peek();
    }
}

6、栈的压入、弹出序列(中等)

描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

1. 0<=pushV.length == popV.length <=1000

2. -1000<=pushV[i]<=1000

3. pushV 的所有数字均不相同

示例1

输入:

[1,2,3,4,5],[4,5,3,2,1]
返回值:

true
说明:

可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true  

示例2

输入:

[1,2,3,4,5],[4,3,5,1,2]
返回值:

false
说明:

由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false  

答案

也是一个辅助栈,遍历,如果栈为空,或者栈顶与popV的第i位不相等的话,就压入pushV中没有压入的数,看到相等了话就跳出将相等的删掉,然后继续,如果直到pushV的所有数字都压入栈的时候还没发现相等,那么就直接返回false

public boolean IsPopOrder (int[] pushV, int[] popV) {
        // write code here
        Stack<Integer> s = new Stack<>();
        int n = pushV.length;
        int j = 0;
        for (int i = 0; i < n; i++) {
            while (j < n && (s.isEmpty() || s.peek() != popV[i])) {
                s.push(pushV[j]);
                j++;
            }
            if (s.peek() == popV[i]) 
                s.pop();
            else return false;
        }
        return true;
    }

7、从上往下打印二叉树(简单)

描述

不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入{8,6,10,#,#,2,1},如以下图中的示例二叉树,则依次打印8,6,10,2,1(空节点不打印,跳过),请你将打印的结果存放到一个数组里面,返回。

数据范围:

0<=节点总数<=1000

-1000<=节点值<=1000

示例1

输入:

{8,6,10,#,#,2,1}
返回值:

[8,6,10,2,1]

示例2

输入:

{5,4,#,3,#,2,#,1}
返回值:

[5,4,3,2,1]

答案

利用队列的先进先出原则,将结点从左到右加入队列,然后依次出队把值存到列表中

public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        if(root == null) return list;
        Queue<TreeNode> q = new ArrayDeque<>();
        q.offer(root);
        while(!q.isEmpty()){
            TreeNode cur = q.poll();
            list.add(cur.val);
            if(cur.left != null) q.add(cur.left);
            if(cur.right != null) q.add(cur.right);
        }
        return list;
    }

8、二叉搜索树的后序遍历序列(中等)

描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回 true ,否则返回 false 。假设输入的数组的任意两个数字都互不相同。

数据范围: 节点数量 0≤n≤10000≤n≤1000 ,节点上的值满足 1≤val≤1051≤val≤105 ,保证节点上的值各不相同
要求:空间复杂度 O(n)O(n) ,时间时间复杂度 O(n2)O(n2)

提示:

1.二叉搜索树是指父亲节点大于左子树中的全部节点,但是小于右子树中的全部节点的树。

2.该题我们约定空树不是二叉搜索树

3.后序遍历是指按照 “左子树-右子树-根节点” 的顺序遍历

4.参考下面的二叉搜索树,示例 1

示例1

输入:

[1,3,2]
返回值:

true
说明:

是上图的后序遍历 ,返回true   

示例2

输入:

[3,1,2]
返回值:

false
说明:

不属于上图的后序遍历,从另外的二叉搜索树也不能后序遍历出该序列 ,因为最后的2一定是根节点,前面一定是孩子节点,可能是左孩子,右孩子,根节点,也可能是全左孩子,根节点,也可能是全右孩子,根节点,但是[3,1,2]的组合都不能满足这些情况,故返回false

示例3

输入:

[5,7,6,9,11,10,8]
返回值:

true

答案

后序遍历的二叉树有个性质,就是根结点在最后一位,左右子树在前面排着,

 首先我们需要按照题目给出列表的逆序进行遍历,这样我们的遍历视角转成了[根−右子树−左子树][根−右子树−左子树]。假定原输入列表的顺序为[tn,tn1,...,t2,t1][tn​,tn1​​,...,t2​,t1],则该逆序后列表内容[t0,t1,t2,...,tn][t0​,t1​,t2​,...,tn​]有如下特性:

  1. 当ti<ti+1ti​<ti+1​时,说明节点ti+1ti+1​是titi​的右子节点
  2. 当ti>ti+1ti​>ti+1​时,说明节点ti+1ti+1​是已经遍历过的所有节点中比ti+1ti+1​大的且最接近ti+1ti+1​的节点的左子节点,设ti+1ti+1​对应的该父节点为rootroot。同时要满足ti+1,ti+2,...,tnti+1​,ti+2​,...,tn​都要比rootroot小
  3. 根据我们推理的以上性质(加粗部分),用单调栈工具进行操作,如果能找到不符合以上性质的矛盾点,则返回false,如果上述性质都满足,则返回true
public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0) return false;
        Stack<Integer> s = new Stack<>();
        int root = Integer.MAX_VALUE;
        for(int i = sequence.length - 1;i >= 0;i--){
            if(sequence[i] > root) return false;
            while(!s.isEmpty() && s.peek() > sequence[i]){
                root = s.pop();
            }
            s.add(sequence[i]);
        }
        return true;
    }

9、复杂链表的复制(较难)

描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。

示例:

输入:{1,2,3,4,5,3,5,#,2,#}

输出:{1,2,3,4,5,3,5,#,2,#}

解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。

以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5

后半部分,3,5,#,2,#分别的表示为

1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null

如下图:

示例1

输入:

{1,2,3,4,5,3,5,#,2,#}
返回值:

{1,2,3,4,5,3,5,#,2,#}

答案

public RandomListNode Clone(RandomListNode pHead) {
        //空节点直接返回
        if (pHead == null)
            return pHead;
        //添加一个头部节点
        RandomListNode cur = pHead;
        //遍历原始链表,开始复制
        while (cur != null) {
            //拷贝节点
            RandomListNode clone = new RandomListNode(cur.label);
            //将新节点插入到被拷贝的节点后
            clone.next = cur.next;
            cur.next = clone;
            cur = clone.next;
        }
        cur = pHead;
        RandomListNode clone = pHead.next;
        RandomListNode res = pHead.next;
        //连接新链表的random节点
        while (cur != null) {
            //跟随前一个连接random
            if (cur.random == null)
                clone.random = null;
            else
                //后一个节点才是拷贝的
                clone.random = cur.random.next;
            //cur.next必定不为空
            cur = cur.next.next;
            //检查末尾节点
            if (clone.next != null)
                clone = clone.next.next;
        }
        cur = pHead;
        clone = pHead.next;
        //拆分两个链表
        while (cur != null) {
            //cur.next必定不为空
            cur.next = cur.next.next;
            cur = cur.next;
            //检查末尾节点
            if (clone.next != null)
                clone.next = clone.next.next;
            clone = clone.next;
        }
        return res;
    }

10、二叉搜索树与双向链表(中等)

描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示

数据范围:输入二叉树的节点数 0≤n≤10000≤n≤1000,二叉树中每个节点的值 0≤val≤10000≤val≤1000
要求:空间复杂度O(1)O(1)(即在原树上操作),时间复杂度 O(n)O(n)

注意:

1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
2.返回链表中的第一个节点的指针
3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构

4.你不用输出双向链表,程序会根据你的返回值自动打印输出

输入描述:

二叉树的根节点

返回值描述:

双向链表的其中一个头节点。

示例1

输入:

{10,6,14,4,8,12,16}
返回值:

From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;
说明:

输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。     

示例2

输入:

{5,4,#,3,#,2,#,1}
返回值:

From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1;
说明:

                    5
                  /
                4
              /
            3
          /
        2
      /
    1
树的形状如上图       

答案

依然递归

import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    //返回的第一个指针,即为最小值,先定为null
    public TreeNode head = null; 
    //中序遍历当前值的上一位,初值为最小值,先定为null
    public TreeNode pre = null;  
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            //中序递归,叶子为空则返回
            return null;    
        //首先递归到最左最小值 
        Convert(pRootOfTree.left);
        //找到最小值,初始化head与pre
        if(pre == null){      
            head = pRootOfTree;
            pre = pRootOfTree;
        }
        //当前节点与上一节点建立连接,将pre设置为当前值
        else{      
            pre.right = pRootOfTree;
            pRootOfTree.left = pre;
            pre = pRootOfTree;
        }
        Convert(pRootOfTree.right);
        return head;
    }
}

面试题5道

1、谈一谈你对操作系统的理解?

操作系统是一种软件,它是计算机系统中的核心组件,负责管理和协调计算机硬件资源,为应用程序提供运行环境和服务。

操作系统的主要作用包括:

  1. 资源管理:操作系统负责管理计算机的硬件资源,如处理器、内存、硬盘和外部设备等,以便合理地分配和利用这些资源。它通过调度算法和资源分配机制,确保每个任务或进程都能得到适当的资源。

  2. 进程管理:操作系统能同时运行多个程序,通过进程管理,它可以控制程序的执行、调度和协作,以便提高计算机的整体效率。它负责创建、终止、挂起和恢复进程,以及管理进程之间的通信与同步。

  3. 文件管理:操作系统负责管理计算机的文件系统,方便用户存储和获取数据,确保数据的安全性和完整性。它提供了文件的创建、读写、删除和重命名等操作,以及文件的权限管理和保护。

  4. 用户界面:操作系统提供了与计算机交互的用户界面,可以是命令行界面或图形用户界面(GUI),使得用户可以方便地使用计算机。用户可以通过输入指令或点击图标进行操作和访问系统功能。

  5. 错误检测和恢复:操作系统能够监测和处理软件和硬件错误,提供错误检测和恢复的机制,以保证计算机的稳定性和可靠性。它可以监测和捕获程序的异常、处理硬件故障、提供备份和恢复机制等。

操作系统是计算机系统中的大管家,它负责管理和协调计算机的各种资源,为应用程序提供一个安全、高效的运行环境。操作系统的设计和优化对于提高计算机的性能、可靠性和用户体验至关重要。

2、简单说下你对并发和并行的理解?

当谈到计算机系统中的并发和并行时,它们具有不同的含义。

并发(Concurrency)是指系统能够处理多个任务的能力,这并不意味着这些任务一定会同时进行。并发的任务可能会交错进行,因此并发可以在单核CPU上实现。这是因为CPU可以通过时间片轮转或其他任务切换策略,在各个任务之间快速切换,给人以它们在同时进行的错觉。一个简单的例子就是我们的操作系统,它可以在运行大量应用程序(如我们的浏览器,文档编辑器,音乐播放器等)同时,保持系统稳定和响应,尽管实际上,那些进程并不总是“同时”运行。

而并行(Parallelism)则是指系统同时执行多个任务的能力。并行显然需要硬件的支持,如多核心或多处理器。在这种情况下,多个任务确实可以在同一时间内进行。例如,现代的多核CPU可以让我们在看电影的同时进行视频编码,每一个任务在不同的处理器核心上执行,这就是并行。

总的来说,如果你有两个线程在单核心的CPU上,那么可能会通过交错执行达到并发。如果你的电脑有多个核心或处理器,你就可以在多个核心或处理器上同时执行多个线程,这是并行。

3、同步和异步有什么区别?

同步和异步是操作系统中的两种重要概念,它们主要涉及到程序的运行方式和时间管理。

  1. 同步(Synchronous)操作是在一个操作完成之前,不进行下一个操作。这是一种阻塞调用,也就是说,进行某项操作的过程中,不得不停下来等待,直到这个操作完成。例如,当你在核对大批量的数据时,你需要等待所有数据都加载完毕才能继续进行下一项操作,这就是同步。

  2. 异步(Asynchronous)操作是不需要立刻得到结果,即使未完成也可进行其它操作。这是一种非阻塞调用,也就是说,还没得到结果,就继续做别的事情,不会因为单一操作的等待而阻塞。例如,你去网上订一张火车票,由于网站服务器繁忙,订票需要一些时间,但是你不会就一直盯着屏幕等,而是可以一边浏览新闻或者查看其他信息一边等待订票结果,这就是异步操作。

这两种方式各有利弊,选择使用同步还是异步,主要取决于具体的需求和场景。

4、阻塞和非阻塞有什么区别?

阻塞和非阻塞是描述任务或操作在等待结果时的行为方式的概念。

阻塞是指任务在等待某个操作完成时,暂停自己的执行,并等待操作完成后再继续执行。在阻塞状态下,任务会一直等待,直到所需的资源或结果就绪。在此期间,任务不能执行其他操作。例如,当一个线程调用阻塞式IO操作时,它会被挂起,直到IO操作完成后才能继续执行。

非阻塞是指任务在等待某个操作完成时,不会暂停自己的执行,而是立即返回,继续执行其他任务。非阻塞的任务会周期性地查询所需资源或结果的状态,判断是否就绪,从而决定是否继续执行。例如,在进行非阻塞式IO操作时,任务会立即返回,并周期性地检查IO操作的状态,直到IO完成后再处理结果。

简单来说,阻塞是等待结果时暂停自己的执行;非阻塞是等待结果时继续执行其他任务。

在实际应用中,阻塞和非阻塞可以用在不同的场景中。阻塞适用于需要确保结果完整性和依赖顺序的情况,而非阻塞适用于需要提高并发性和响应性的情况。选择适合的阻塞和非阻塞方式可以提高程序的效率和性能。

5、computed、watch、methods的区别

computed计算属性是用来声明式的描述一个值依赖了其它的值。当你在模板里把数据绑定到一个计算属性上时,Vue会在其依赖computed计算属性是用来声明式的描述一个值依赖了其它的值。当你在模板里把数据绑定到一个计算属性上时,Vue会在其依赖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值