一些面试题(2)

转载:http://gushuizerotoone.iteye.com/blog/737780

注意时间复杂度
1.给出一个数列,找出连续相加最大的和
方法:(1)O(n) 一次扫描,如果sum<0, sum = 0.  英文数据结构书p23
     (2)O(nlogn) devide and conqure 左右两边分别找最大,合并后的值,看看最后左、右、合并三个哪个最大  英文数据结构书p21
=================================================================
2.二分查找 O(logN)整个数列已经之前排过序才能二分查找。每次比较选中间这个,如果比中间这个大,则继续找右边的中间值,否则找左边. 英文数据结构书p23
=================================================================
3.各种排序算法
参考java各种排序算法http://shulin-success.iteye.com/blog/493705其中讲到各种排序算法的时间复杂度和空间复杂度。
这个也有用排序算法http://hzhping350.blog.163.com/blog/static/831207362008821102419976/
1.冒泡排序:程序见算法导论 P23底 思想:最小的先冒起来
2.插入排序:思想:就像我们抓牌,插牌一样.在数列大部分有序的情况下比快排有效。最好情况O(n)
3.希尔排序: 思想见:数据结构课件DS07_Ch06a.pps第5页
4.堆排序:把数字填入初始化堆(未排序,只是按顺序填入)-->然后调整为最小堆(或最大堆)buildHeap--->然后排序 见数据结构试卷P14页背面教你怎样buildHeap.数据结构试卷P7及背面教你怎样heapSort
5.归并排序
6.快速排序:思想:找到一个pivot,把原数据集分为两个子集,一个大于pivot,一个小于pivot。pivot的位置就定了,在小于集(pivot)大于集.
7.桶排序:把区间[0,1)划分为n个相同大小的子区间(即桶),先对各个桶中的数进行排序,然后按桶的次序列出数字即可。需额外空间O(n),在数符合均匀分布的情况下,O(n)

3)总结:

——按平均的时间性能来分:
     1)时间复杂度为O(nlogn)的方法有:快速排序、堆排序和归并排序,其中以快速排序为最好;
     2)时间复杂度为O(n2)的有:直接插入排序、起泡排序和简单选择排序,其中以直接插入为最好,特          别是对那些对关键字近似有序的记录序列尤为如此;
当待排记录序列按关键字顺序有序时,直接插入排序和起泡排序能达到O(n)的时间复杂度;而对于快速排序而言,这是最不好的情况,此时的时间性能蜕化为O(n2),因此是应该尽量避免的情况。简单选择排序、堆排序和归并排序的时间性能不随记录序列中关键字的分布而改变。
——按平均的空间性能来分(指的是排序过程中所需的辅助空间大小):
     1) 所有的简单排序方法(包括:直接插入、起泡和简单选择)和堆排序的空间复杂度为O(1);
     2) 快速排序为O(logn ),为栈所需的辅助空间;
     3) 归并排序所需辅助空间最多,其空间复杂度为O(n );
     4)链式基数排序需附设队列首尾指针,则空间复杂度为O(rd )。
——排序方法的稳定性能:
     1) 稳定的排序方法指的是,对于两个关键字相等的记录,它们在序列中的相对位置,在排序之前和 经过排序之后,没有改变。
     2) 当对多关键字的记录序列进行LSD方法排序时,必须采用稳定的排序方法。
     3) 对于不稳定的排序方法,只要能举出一个实例说明即可。
     4) 快速排序,希尔排序和堆排序是不稳定的排序方法。

所需辅助空间最多:归并排序

所需辅助空间最少:堆排序

平均速度最快:快速排序

不稳定:快速排序,希尔排序,堆排序。
=================================================================
4.结合堆排序,分析最大(小)堆建立、插入、删除的时间复杂度
最大堆就是:所有的父亲都比儿子要大,且是完全(完全不一定满)二叉树
buildHeap:O(n)注意是不是O(logn):从最后一个元素/2开始(减1循环),percolateDown.见数据结构试卷P14页背面
DeleteMin: O(logn):把堆顶元素和最后一个元素B交换,让B从堆顶percolateDown.英文数据结构P152
Insert:O(logn):插入称为最后一个叶节点,然后向上回溯找到位置

===================================
二叉搜素树:
什么是搜素二叉树。任何一个结点的左边子节点所有的孙结点都小于它,右边的都大于它P27
查找x:O(d) d是depth是树的深度。从顶结点开始,如果结点小于x,则往左边找,结点大于x,则往右边找,结点等于x,则返回。英文数据结构P99

查找最小值:O(d)一直往左边找 英文数据结构P99
查找最大值:O(d)一直往右边找 英文数据结构P100

插入:O(d)从顶上结点开始比较往下顺,如果找到位置,插入.英文数据结构P101
删除: O(d)英文数据结构P102
     (1)如果删除的是叶节点,则直接删除,把它父节点->next设为null
     (2)如果删除的结点只有一个child a,则把a父节点->next设为a的儿子,a 删掉
      (3)如果删除的结点有两个child,则用右子树的最小值取代这个值(或者左子树的最大值),然后右子树删掉那个最小值(或者左子树删掉那个最大值)
===================================================
AVL Trees:
definition(英文数据结构P106):,每个结点的左子树和右子树的高度最大相差1的二叉搜索树

target:加快搜素(插入、删除)
======================================================
拓扑排序:;例子有一些课程是在一些课程之后才能学的,叫你给出一个学习的顺序。拓扑排序不能有环  数据结构课件DS10_Ch09b.pps
O(|V|+|E|)
======================================================
Dijkstra算法:O|V|.单源最短路径问题(其它所有点与一个开始点间的最短路径)
看算法导论P367的图即可明白

Dijkstra是贪心算法,因为每次选择时,都是选择最小值的结点开始访问
Dijkstra是广度优先搜索的类似例子,因为每次访问一个对象,被它指向的对象都会修改值,而且选择最小的边。因为未访问的都设置为无穷,所以实际上也是广度优先搜索(看不懂就算了)
伪代码看白纸笔记P4和P6
======================================================
Kruskal算法:用来生成图的最小生成树(边值和最小)O(|E|log|E|)看算法导论P349的图即可明白
也是贪心算法,每次都是查看最小的边,有环则放弃,没环就打上
伪代码看算法导论P348

Prim算法:用来生成图的最小生成树, 看算法导论P350的图即可明白
也是贪心算法,类似于Dijkstra,从一个点出发,只有一个树,每次找这棵树之外的最小的边加入到这棵树中
Prim算法是广度优先搜索的例子,因为每次找这棵树之外的最小的边加入到这棵树中就是广度优先搜索
=======================================================
红黑树:算法导论P163 target:加快搜素(插入、删除)
红黑树是一种平衡的二叉查找树,每个结点着色(Red或者black),通过对着色方式的限制(对于每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点),红黑树确保没有一条路径比其它路径长出两倍,因而是接近平衡的。正因为平衡,红黑树保证在最坏情况下,基本操作也达到O(lgn).因为一般高度为h的二叉树的操作时间都是O(h),即红黑树保证h不会过大,是 lgn的。

红黑性质:
每个结点或是红的,或是黑的
根结点是黑的
每个叶节点(nil)是黑的
如果一个结点是红的,则它的两个儿子都是黑的
对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

红黑树的插入操作:通过重新着色,左旋和右旋(了解一下)
======================================================






======================================================
题目:google : programming interview question, algorithm interview question
1.查找两个已经排好序的数组的第k大的元素(中位数是此题的特殊情况(sa+sb)/2)
median of two sorted array
数组a,大小sa;数组b,大小sb
要求时间复杂度是O(log(sa+sb))
最直接的想法是类似于归并算法,两个游标,一个游a,一个游b,时间复杂度O(sa+sb)
第2种解法,有点而类似二分查找O(log(sa+sb))。附见笔记1,参考http://www.amirwatad.com/blog/archives/2010/03/06/finding-the-median-of-two-sorted-arrays/
------------------------------------------------------
2.二叉树先序遍历和中序遍历的非递归实现
用stack就可以,可以参照http://www.iteye.com/topic/507806
先循环把左节点加入栈,如果栈里还有元素的话,把栈里的顶元素取出来,访问它的右节点
先序遍历和中序遍历差不多,就是打印的句子出现的位置不一样。
其实后序遍历也差不多
------------------------------------------------------
3.查找两个字符串的最长公共子字符串(LCS--longest common substring)
参考:http://en.wikipedia.org/wiki/Longest_common_substring_problem#Suffix_tree
1.动态规划法(看上面的链接很容易明白,O(mn),m,n为两个字符串的长度): 思想,从小到大,find the longest common suffix for all pairs of prefixes of the strings.(查找所有的前缀子字符串的公共后缀字符串)
2.还有一个后缀树的方法,据说复杂度是O(m+n),待

查找两个字符串的最长公共子序列(LCS---longest common subsequence)和最长公共子字符串不同的是子序列不要求是连续的
DP:(1)  当X[i]=Y[j]时, L(i, j)=L(i-1, j-1)+1 。证明很简单。

(2)  当X[i]!=Y[j]时, 说明此事X[0...i]和Y[0...j]的最长公共子序列中绝对不可能同时含有X[i]和Y[j]。那么公共子序列可能以X[i]结尾,可能以Y[j]结尾,可以末尾都不含有X[i]或Y[j]。因此

                               L(i, j)= MAX{L(i-1 , j), L(i, j-1)}
很简单见链接http://hxraid.iteye.com/blog/622462
-------------------------------------------------------
4.查找一个字符串的最长回文子串------------------------------------------------------
5.判断链表是否有环(Find if a linked list has a cycle in it.)
O(n)
一个快指针(两倍速度),一个慢指针(一倍速度)
下面这个链接的底部
http://ostermiller.org/find_loop_singly_linked_list.html
-------------------------------------------------------
6.把一个字符串的单词反过来,是就地反Reverse Words In a String
O(n)
反转单词,如"hello my   world ”--->"world   my hello"空格有时候不止一个
void ReverseWordsInString(char *str)
{
    int start, end, length;

    length=strlen(str);
    ReverseString(str, 0, length-1);//首先是整个反转

    start=end=0;
    while (end < length)
    {
        if (str[end] ! = ' ')//一个单词的开始
        {
            start=end;
            while (str[end] != ' ' && end < length)//找到单词的介绍
                  end++;       
            end--;
            ReverseString(str, start, end);//反转这个单词
        }
        end++;
    }
}

//反转字符串(注意不是反转单词),即第1个和最后一个互换,第2个和倒数第2个互换
void ReverseString(str, start, end)
{
    char *temp;
    while (start < end)
    {
        temp=str[start];
        str[start]=str[end];
        str[end]=temp;
        start++;
        end--;
    }
}
----------------------------------------------
7.查找字符串中第一个不重复的字符:Find the first non-repeating character in a string

方法Hash,两次遍历。第一次遍历是计算频率,第二次遍历是从左到右查看频率为1的字符
参考http://geeksforgeeks.org/?p=1781
-----------------------------------------------
8.查找第k-th smallest element in 二叉查找树O(lgn)
Finding kth smallest element in Binary Tree

递归查找即可
http://mukeshiiitm.wordpress.com/2010/04/07/finding-kth-smallest-element-in-binary/
-----------------------------------------------
9.在数组中查找第k小的元素
可以建立一个k大小的最大堆O(k),如果比堆顶小,就insert到堆中O(lgk),因此时间复杂度是O(k+nlogk)-->O(nlogk),节约空间
可以用快排Parition(O(n))的思想,虽然快排时间复杂度是O(nlgn),但是仅是查找第k个元素的话时间复杂度仅是O(nlgk),但是可能就改变了数组的元素顺序了
快排partition的过程可以参考算法导论P85-86看程序再看图即可明白
而查找第k个元素的话,可以比较k和pivotIndex的大小,再决定往左边还是右边继续跑
程序参考http://en.wikipedia.org/wiki/Selection_algorithm

//和算法导论的差不多,看算法导论的
function partition(list, left, right, pivotIndex)
     pivotValue := list[pivotIndex]
     swap list[pivotIndex] and list[right]  // Move pivot to end
     storeIndex := left
     for i from left to right-1
         if list[i] < pivotValue
             swap list[storeIndex] and list[i]
             storeIndex := storeIndex + 1
     swap list[right] and list[storeIndex]  // Move pivot to its final place
     return storeIndex
//注意,最后一行它写错了
function select(list, left, right, k)
     select pivotIndex between left and right
     pivotNewIndex := partition(list, left, right, pivotIndex)
     if k = pivotNewIndex
         return list[k]
     else if k < pivotNewIndex
         return select(list, left, pivotNewIndex-1, k)
     else
         return select(list, pivotNewIndex+1, right, k-pivotNewIndex)

扩展问题
(1)查找前k个数,partition中最后找到排序第k个序号前面的都是
堆排序中前k个数就是堆中的元素了
(2)第k到m大的数(比如第500到600的数),方法是一个500的最大堆找到第500小的元素,然后一个以第500小的元素做判断(大于这个元素才加进堆里面)的堆大小为600-500的最小堆,时间复杂度应该是O(nlgk+nlg(m-k))

如果n很大的话就用堆排序,不用快排的partition思想
-------------------------------------------------
可以花半天时间看一下编程之美的这些题
10.求二进制数中1的个数(编程之美---P119)
解法2 while(v){num+=v&0x01; v>>=1} 时间复杂度O(logv)
解法3 巧妙的解法 while(v){v &= (v-1); num++} 时间复杂度O(v中1的个数)
解法4 先存好256个数的1的个数,然后再访问,汗。。。 空间复杂度换时间复杂度,O(1)

扩展问题:A变成B需要改变多少位(bit),也就是A和B的二进制表示中有多少位是不同的?
C=A和B异或,再求C二进制中1的个数
--------------------------------------------------
11.阶乘(编程之美---P125)基本问题:N!有质因数m的个数
ret = 0;
while(N){
   N /= m;
   ret += N;
}

问题1,N!的末尾有多少个0,如N=10, N!=3628800, 末尾有两个0
问题1转化为基本问题,N!有质因数5的个数(至于怎样转化看编程之美)
问题2,求N!的二进制表示中最低位1的位置
问题2转化为基本问题,N!有质因数2的个数(至于怎样转化看编程之美)

扩展问题:判断整数n是否是2的方幂
思想,2的方幂是二进制表示中只有1个1,其它都为0,因此 (n>0 && (n & (n-1))==0)有点类似于上面那题解法3
-------------------------------------------------
12.最大公约数 (编程之美P151)解法2
f(42,30) = f(30,12)=f(18,12) = f(12,6) =f(6,0)=6
gcd(x,y){
   if(x<y) return gcd(y,x);
   if(y == 0) return x;
   else return gcd(x-y, y);
}
-------------------------------------------------
13.寻找数组中的最大值和最小值的比较次数(编程之美P165)一边扫描,记录,需要比较2N次,max比较N次,min比较N次
解法2,1.5*N次,它是先以2大小为一单元切割,单元中大的排在前头,小的排在后面(N/2的比较次数),然后比较偶数位的需要N/2的比较找到最大,比较奇数位的需要N/2的比较找到最小,因此需要N/2+N/2+N/2 = 1.5N次比较

1.5N次比较式最小的了

可以看看解法4,看看分治思想
--------------------------------------------------
14.找数组中满足a+b = sum(sum为给定的一个数)的a和b
思想:
解法2:先排序O(NlgN),对每个元素a,计算出sum-a,二分查找看sum-a在不在数组中O(lgN),每个元素都要计算,就是O(N*lgN),
总的O(NlgN+NlgN) = O(NlgN)

解法三,先排序O(NlgN),
第一个元素和最后一个元素相加和sum比较,如果>sum,则第一个元素后移,<sum,最后一个元素前移, = sum返回 O(N)
总的复杂度为O(NlgN+N)=O(NlgN)

空间换取时间的解法
每个元素hash存一下O(N)
对于每个元素a,计算sum-a在不在hash表中 O(1) ,N个元素O(N*1)
O(N)+O(N*1) = O(N)
但是空间上多了O(N)的hash表
------------------------------------------------
15.求数组中的最长递归子序列(编程之美P194)1, -1, 2 , -3, 1000, 4, -5, 6, -7
得到
-1, 2, 4, 6
编程之美的解析不是太好,可以边参考编程之美,边参考以下url
http://student.csdn.net/space.php?uid=116484&do=blog&id=39673

初始想法dp:每个以一个字符结束的最长递归子序列的长度是多少,然后遍历比较,见解法1
时间复杂度O(N^2)

优化:记录minV(最长递归子序列长度)优化的见编程之美(见上面URL和我写在旁边的附注)
时间复杂度O(NlgN)?(试)
-------------------------------------------------
16.字符串移位包含 (编程之美P215)看解法二,双倍连接原字符串,再查看是不是包括小的即可
-------------------------------------------------
17.数组循环移位(编程之美P199)看最后的解法2,easy。和反转单词差不多,见上面6
------------------------------------------------
18.从无头单链表中删除节点(编程之美P226)因为链表无头,所以不能从头遍历
只能内容替换
看书即可,很快明白
-------------------------------------------------
19.判断两个链表是否相交P235首先要明白什么是相交链表,就是前面是分开的,后面是重叠的,一直重叠到底
解法2,hash的方法,首先hash第一个链表的地址,再遍历第二个链表的地址,看是否在hash表中,时间复杂度O(len(h1)+len(h2)),但是建hash表有额外空间消耗O(len(h1))
解法3,转为判断链表是否有环的问题,见上面5,把第1个链表的尾节点连接第2个链表的头节点,遍历第2个链表,看是否有环(这有个前提,就是一开始给的两个链表无环)
最好的方法解法四,看两个链表的最后一个节点是否相同,即可O(len(h1)+len(h2))

扩展问题:
1.怎样求出两个链表相交的第一个结点,
遍历第一个链表,得到长度len(h1),遍历第二个链表,得到长度len(h2)
看看那个长,再长的链表提前|len(h1)-len(h2)|步开始走,然后比较,直到相等的,即为第一个相交结点。

2.如果链表有环,怎样判断两个链表是否相交
判断第1个链表是否有环,用一快一慢指针,得到一个在环内的结点A(快慢指针重叠的位置)
判断第2个链表是否有环,用一快一慢指针,如果无环--->不相交,如果有环,在得到有(快慢指针重叠的位置还没发现第一个链表的a的话--->不想交,发现--->相交
---------------------------------------------------
19-1:给一个字符串和一个字符集,求该字符串包含字符集的最短子串http://club.itqun.net/showtopic-207005.html

就是给一个很长的字符串str 还有一个字符集比如{a,b,c} 找出str里包含{a,b,c}的最短子串。要求O(n)?
比如,字符集是a,b,c,字符串是abdcaabcx,则最短子串为abc

用两个变量 front rear 指向一个的子串区间的头和尾
用一个int cnt[255]={0}记录当前这个子串里 字符集a,b,c 各自的个数,一个变量sum记录字符集里有多少个了
rear 一直加,更新cnt[]和sum的值,直到 sum等于字符集个数
然后front++,直到cnt[]里某个字符个数为0,这样就找到一个符合条件的字串了

继续前面的操作,就可以找到最短的了
---------------------------------------------------
19-2:5个数排序 要比较的最少次数
7次http://blog.csdn.net/fisher_jiang/archive/2008/05/13/2442486.aspx
---------------------------------------------------
20.Lowest Common Ancestor(待)http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor
---------------------------------------------------
20.背包问题与最小数目硬币(dynamic programming)最小数目硬币,给出一个sum比如11,给出有几种硬币,比如1,3,5,求用最少的硬币数达到sum,这里为551,三枚。见http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg
看看代码很容易明白。sum从1开始遍历,小于sum的都已经计算过了,则遍历一下所有的硬币,如果vj<sum则加进来,sum-vj已经求过了
Set Min[i] equal to Infinity for all of i
Min[0]=0

For i = 1 to S
   For j = 0 to N - 1
     If (Vj<=i AND Min[i-Vj]+1<Min[i])
        Then Min[i]=Min[i-Vj]+1

Output Min[S]


背包问题:见
http://xmuzzx.blog.hexun.com/39126356_d.html
即,看url那个图,即从上到下一项项物品往下遍历
f(i,j)遍历到第i项物品,j为背包的价值容量
if(w[n]<m)
    //不把n加进来,和把n加进来取一个最大值,所有的f(n-1,m)的值已经先前计算出来了
    f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n)}//p(n)为第n项物品的价值
else
    f(n,m)=f(n-1,m)
----------------------------------------
21 从两个文件(各含50亿个url)中找出共同的url
http://mianshiti.blog.sohu.com/144535301.html
可以估计每个文件的大小为5G*64=300G,远大于4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
遍历文件a,对每个url求取hash(url)%1000,然后根据所得值将url分别存储到1000个小文件(设为 a0,a1,...a999)当中。这样每个小文件的大小约为300M。遍历文件b,采取和a相同的方法将url分别存储到1000个小文件 (b0,b1....b999)中。这样处理后,所有可能相同的url都在对应的小文件(a0 vs b0, a1 vs b1....a999 vs b999)当中,不对应的小文件(比如a0 vs b99)不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
比如对于a0 vs b0,我们可以遍历a0,将其中的url存储到hash_map当中。然后遍历b0,如果url在hash_map中,则说明此url在a和b中同时存在,保存到文件中即可。
如果分成的小文件不均匀,导致有些小文件太大(比如大于2G),可以考虑将这些太大的小文件再按类似的方法分成小小文件即可。
---------------------------------------------
22.有1到10w这10w个数,去除2个并打乱次序,如何找出那两个数?
方法一:
申请10w个bit的空间,每个bit代表一个数字是否出现过。
开始时将这10w个bit都初始化为0,表示所有数字都没有出现过。
然后依次读入已经打乱循序的数字,并将对应的bit设为1。
当处理完所有数字后,根据为0的bit得出没有出现的数字。

方法二:
首先计算1到10w的和,平方和。
然后计算给定数字的和,平方和。
两次的到的数字相减,可以得到这两个数字的和,平方和。
所以我们有
x + y = n
x^2 + y^2 = m
解方程可以得到x和y的值。
---------------------------------------------
23.如何找出字典中的兄弟单词http://mianshiti.blog.sohu.com/144535354.html
问题:

给定一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的兄弟单词。现在给定一个字典,用户输入一个单词,如何根据字典找出这个单词有多少个兄弟单词?



答案:
使用hash_map和链表。
首先定义一个key,使得兄弟单词有相同的key,不是兄弟的单词有不同的key。例如,将单词按字母从小到大重新排序后作为其key,比如bad的key为abd,good的key为dgoo。
使用链表将所有兄弟单词串在一起,hash_map的key为单词的key,value为链表的起始地址。
开始时,先遍历字典,将每个单词都按照key加入到对应的链表当中。当需要找兄弟单词时,只需求取这个单词的key,然后到hash_map中找到对应的链表即可。
这样创建hash_map时时间复杂度为O(n),查找兄弟单词时时间复杂度是O(1)。
-------------------------------------------
24.海量日志数据,提取出某日访问百度次数最多的那个IP。

IP地址最多有2^32=4G种取值可能,所以不能完全加载到内存中。
可以考虑分而治之的策略,按照IP地址的hash(IP)%1024值,将海量日志存储到1024个小文件中。每个小文件最多包含4M个IP地址。
对于每个小文件,可以构建一个IP作为key,出现次数作为value的hash_map,并记录当前出现次数最多的1个IP地址。
有了1024个小文件中的出现次数最多的IP,我们就可以轻松得到总体上出现次数最多的IP。

转载于:https://www.cnblogs.com/buptLizer/archive/2011/09/03/2165300.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值