C++算法篇 算法时间复杂度图解

究竟什么是时间复杂度呢?让我们来想象一个场景:某一天,小灰和大黄同时加入了一个公司......
 

一天过后,小灰和大黄各自交付了代码,两端代码实现的功能都差不多。大黄的代码运行一次要花100毫秒,内存占用5MB。小灰的代码运行一次要花100秒,内存占用500MB。于是......

由此可见,衡量代码的好坏,包括两个非常重要的指标:

1.运行时间;

2.占用空间。

关于代码的基本操作执行次数,我们用四个生活中的场景,来做一下比喻:

场景1:

给小灰一条长10寸的面包,小灰每3天吃掉1寸,那么吃掉整个面包需要几天?

640?wx_fmt=jpeg

答案自然是 3 X 10 = 30天。

如果面包的长度是 N 寸呢?

此时吃掉整个面包,需要 3 X n = 3n 天。

如果用一个函数来表达这个相对时间,可以记作 T(n) = 3n。

 

场景2:

给小灰一条长16寸的面包,小灰每5天吃掉面包剩余长度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸......那么小灰把面包吃得只剩下1寸,需要多少天呢?

这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log(2)16。

因此,把面包吃得只剩下1寸,需要 5 X log(2)16 = 5 X 4 = 20 天。

如果面包的长度是 N 寸呢?

需要 5 X logn = 5log(2)n天,记作 T(n) = 5log(2)n。

 

场景3:

给小灰一条长10寸的面包和一个鸡腿,小灰每2天吃掉一个鸡腿。那么小灰吃掉整个鸡腿需要多少天呢?

640?wx_fmt=jpeg

答案自然是2天。因为只说是吃掉鸡腿,和10寸的面包没有关系 。

如果面包的长度是 N 寸呢?

无论面包有多长,吃掉鸡腿的时间仍然是2天,记作 T(n) = 2。

 

场景4:

给小灰一条长10寸的面包,小灰吃掉第一个一寸需要1天时间,吃掉第二个一寸需要2天时间,吃掉第三个一寸需要3天时间.....每多吃一寸,所花的时间也多一天。那么小灰吃掉整个面包需要多少天呢?

答案是从1累加到10的总和,也就是55天。

如果面包的长度是 N 寸呢?

此时吃掉整个面包,需要 1+2+3+......+ n-1 + n = (1+n)*n/2 = 0.5n^2 + 0.5n。

记作 T(n) = 0.5n^2 + 0.5n。

640?wx_fmt=jpeg

上面所讲的是吃东西所花费的相对时间,这一思想同样适用于对程序基本操作执行次数的统计。刚才的四个场景,分别对应了程序中最常见的四种执行方式:

场景1:T(n) = 3n,执行次数是线性的。

void eat1(int n)
{    for(int i=1; i<=n; i++)
       s+=3;     
}

场景2:T(n) = 5log(2)n,执行次数是对数的。

void eat2(int n)
{   for(int i=1; i<n; i*=2)
       s++;      
}

场景3:T(n) = 2,执行次数是常量的。

void eat3(int n)
{   s=n;
}

场景4:T(n) = 0.5n^2 + 0.5n,执行次数是一个多项式。

void eat4(int n)
{   s=0;
    for(int i=1; i<=n; i++)
       for(int j=1; j<=i; j++) 
           s++;
}

 

渐进时间复杂度

 

有了基本操作执行次数的函数 T(n),是否就可以分析和比较一段代码的运行时间了呢?还是有一定的困难。

比如算法A的相对时间是T(n)= 100n,算法B的相对时间是T(n)= 5n^2,这两个到底谁的运行时间更长一些?这就要看n的取值了。所以,这时候有了渐进时间复杂度的概念,官方的定义如下:

若存在函数 f(n),使得当n趋近于无穷大时,T(n)/ f(n)的极限值为不等于零的常数,则称 f(n)是T(n)的同数量级函数。记作 T(n)= O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

渐进时间复杂度用大写O来表示,所以也被称为大O表示法。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

让我们回头看看刚才的四个场景。

场景1:

T(n) = 3n 

最高阶项为3n,省去系数3,转化的时间复杂度为:

T(n) =  O(n)

640?wx_fmt=png

场景2:

T(n) = 5logn 

最高阶项为5logn,省去系数5,转化的时间复杂度为:

T(n) =  O(logn)

640?wx_fmt=png

场景3:

T(n) = 2

只有常数量级,转化的时间复杂度为:

T(n) =  O(1)

640?wx_fmt=png

场景4:

T(n) = 0.5n^2 + 0.5n

最高阶项为0.5n^2,省去系数0.5,转化的时间复杂度为:

T(n) =  O(n^2)

640?wx_fmt=png

这四种时间复杂度究竟谁用时更长,谁节省时间呢?稍微思考一下就可以得出结论:

O(1)< O(logn)< O(n)< O(n^2)

时间复杂度的巨大差异

 

640?wx_fmt=jpeg

640?wx_fmt=jpeg

我们来举过一个栗子:

算法A的相对时间规模是T(n)= 100n,时间复杂度是O(n)

算法B的相对时间规模是T(n)= 5n^2,时间复杂度是O(n^2)

算法A运行在小灰家里的老旧电脑上,算法B运行在某台超级计算机上,运行速度是老旧电脑的100倍。

那么,随着输入规模 n 的增长,两种算法谁运行更快呢?

640?wx_fmt=png

从表格中可以看出,当n的值很小的时候,算法A的运行用时要远大于算法B;当n的值达到1000左右,算法A和算法B的运行时间已经接近;当n的值越来越大,达到十万、百万时,算法A的优势开始显现,算法B则越来越慢,差距越来越明显。

这就是不同时间复杂度带来的差距。

640?wx_fmt=jpeg

 

一、概念

时间复杂度是总运算次数表达式中受n的变化影响最大的那一项(不含系数)

(1)时间频度 

    一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道算法花费的时间多少

    一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。

    一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。

 

(2)时间复杂度 

     n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 

     一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n))    称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

注意,时间频度与时间复杂度是不同的,时间频度不同但时间复杂度可能相同。

如:T(n)=n^2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n^2)。

 

常见的时间复杂度有:

常数阶O(1)   <   对数阶O(log2n)   <   线性阶O(n)    <   线性对数阶O(nlog2n)   <   平方阶O(n^2)   < 

立方阶O(n^3)   <   k次方阶O(n^k)   <   指数阶O(2^n)   <   阶乘阶O(n!)   <    O(n^n)

 

(3)最坏时间复杂度和平均时间复杂度  最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。 这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。

     在最坏情况下的时间复杂度为T(n)=0(n),它表示对于任何输入实例,该算法的运行时间不可能大于0(n)。 平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。

指数阶0(2n),显然,时间复杂度为指数阶0(2n)的算法效率极低,当n值稍大时就无法应用。
 

二、最坏时间复杂度和平均时间复杂度

对于时间复杂度的分析,一般是这两种方法:

(1)最坏时间复杂度

    最坏情况运行时间(运行时间将不会再坏了。通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间

对于追问为什么是最坏时间复杂度的好奇宝宝:

         1、如果最差情况下的复杂度符合我们的要求,我们就可以保证所有的情况下都不会有问题。

         2、也许你觉得平均情况下的复杂度更吸引你(见下),但是:第一,难计算第二,有很多算法的平均情况和最差情况的复杂度是一样的. 第三,而且输入数据的分布函数很可能是你没法知道。

 

(2)平均时间复杂度

    平均时间复杂度也是从概率的角度看,更能反映大多数情况下算法的表现。当然,实际中不可能将所有可能的输入都运行一遍,因此平均情况通常指的是一种数学期望值,而计算数学期望值则需要对输入的分布情况进行假设。平均运行时间很难通过分析得到,一般都是通过运行一定数量的实验数据后估算出来的。
 

三、如何推导出时间复杂度,有如下几个原则:

 

1、如果运行时间是常数量级,用常数1表示;

2、只保留时间函数中的最高阶项;

3、如果最高阶项存在,则省去最高阶项前面的系数。

1、常数阶举例运用

右侧注释中的 num 表示语句执行的次数。

code-1

int sum = 0, n = 100;       /* num = 1 */
sum = (n+1) * n / 2;        /* num = 1 */
printf("%d", sum);          /* num = 1 */

这段代码的运行次数函数是 f(n) = 1 + 1 + 1 ,根据“推导大O阶方法”中的第一条规则,把 1 + 1 + 1 用 1 替换,运行次数函数变成了 f(n) = 1。该函数只有常数项,只需使用规则1就可以推导出它即这段代码的时间复杂度是 O(1) 。

假如 sum = (n+1) * n / 2 执行3次,将上面的代码修改为:code-2

int sum = 0, n = 100;       /* num = 1 */
sum = (n+1) * n / 2;        /* num = 1 */
sum = (n+1) * n / 2;        /* num = 1 */
sum = (n+1) * n / 2;        /* num = 1 */
printf("%d", sum);          /* num = 1 */

code-2的运行次数函数是 f(n) = 1 + 1 + 1 + 1 + 1 。 按照推导大O阶第一条规则,用1取代所有的加法常数,这段代码的运行次数函数是 f(n) = 1。这段代码的时间复杂度依然是 f(n) = O(1) 。所有这类代码的时间复杂度都是 O(1)。O(1)叫做常数阶。不存在 O(2) 、 O(9) 这类写法。

执行次数

N=10,大约执行1次

N=100,大约执行1次

N=1000,大约执行1次

N=10000,大约执行1次

2、线性阶举例运用

code-3

int i;
for(i = 0; i < n; i++)
{
    // 时间复杂度为O(1)的代码
}

code-3的运行次数函数是 f(n) = n * 1。加法常数为0个,跳过规则一。变量n的最高阶是 n * 1,无其他项,跳过规则二。n * 1中的系数本来就是1,也可以直接跳过规则三,得到code-3的时间复杂度是f(n) = O(n)。

code-4

int i;
for(i = 0; i < n; i++)
{   // 时间复杂度为O(1)的代码
}

int j;
for(j = 0; j < m; j++)
{    // 时间复杂度为O(1)的代码
}

code-4的运行次数函数是f(n) = n * 1 + m * 1。直接跳过规则一。n * 1 + m * 1有两个变量,但次数都是1,任何一项 n * 1 或 m * 1 都可视为最高价,根据推导规则二“保留最高阶”,得出运行次数函数是f(n) = n * 1 或 f(n) = m * 1。最后根据规则三,得出code-4的时间复杂度是f(n) = O(n)。

执行次数

N=10,大约执行10次

N=100,大约执行100次

N=1000,大约执行1000次

N=10000,大约执行10000次

 

3、对数阶举例运用

code-5

int count = 1;
while(count < n)
{   count = count * 2;
    //其他时间复杂度为O(1)的代码 
}

code-5似乎不能用前面的推导大O阶方法来分析时间复杂度,按分析“运行时间中的对数”的一般法则规定:

如果一个算法用常数时间(O(1)将问题的大小削减为其一部分(通常是1/2),那么该算法就是 O(log N)。 
另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1),那么这种算法就是 O(N) 。

code-5中,假设 n = 8 ,初始化时,while(count < n) 需要运行8次。经过一次循环后,count变为2, 
循环需要运行4次,变为原来的一半。根据那条一般法则,判断 code-5 的时间复杂度是 O(log N)。

将code-5修改为code-6

int count = 1;
while(count < n)
{    count = count + 2;
    //其他时间复杂度为O(1)的代码 
}

code-6每次执行循环后,会把问题减少2个常数,时间复杂度应为 O(N)。若将code-6中的count = count + 2改为code = count - 2,时间复杂度仍然是 O(N)。

执行次数

底数为2的情况

N=10,大约执行3次

N=100,大约执行7次

N=1000,大约执行10次

N=10000,大约执行13次

 

4、平方价举例运用
code-7

int i, j;                       /*1*/
for(i = 0; i < n; i++)          /*2*/
{   for(j = 0; j < n; j++)      /*3*/
    { 
        //时间复杂度为O(1)的代码  /*4*/
    }                           /*5*/
}                               /*6*/

code-7中第二个循环体的时间复杂度是O(N)。第一个循环体将第二个循环体再执行N次,时间复杂度变为O(N^2)。 
如果将第二个循环体中的n改为m,那么code-7的时间复杂度就是O(N*M)。注意,O(N*M)和O(N^2)都叫做 
平方阶,二者实质相同。

多层循环体的时间复杂度就是每层循环体的运行次数相乘。

code-8

int i, j;
for(i = 0; i < n; i++)
{   for(j = i; j < n; j++)
    {     //时间复杂度为O(1)的代码
    }
}

code-8运行次数是(n+1)*n*n/2。只保留最高阶并且去掉它的系数,时间复杂度是O(N^2)。

code-9

void function(int count)
{   int j;
    for(j = count; j < n; j++)
       printf("%s", "hello,world");    
}
n++;                               /*   num = 1          */
function(n);                       /*   num = n          */
int i,j;                           /*   num = 1          */
for(i = 0; i < n; i++)             /*   num = n*n        */
    function(i);
for(i = 0; i < n; i++)             /*   num = (n+1)*n/2  */
{   for(j = i; j < n; j++)
      printf("%s", "hi");    
}

code-9的时间复杂度是多少呢?首先将每行代码的执行次数标出来。

code-9的执行次数(首先忽略掉常数项)是n + n*n + (n+1)*n/2,计算结果为1.5*n*n + 2*n。 只保留最高阶1.5*n*n,最后将系数变为1,执行次数为n*n,时间复杂度为O(N^2)。

执行次数

N=10,大约执行100次

N=100,大约执行10000次

N=1000,大约执行1000000次

N=10000,大约执行100000000次

 

5、递归函数举例运用

code-10
求该方法的时间复杂度

long aFunc(int n) 
{   if (n <= 1)   return 1;
    else          return aFunc(n - 1) + aFunc(n - 2);    
}

显然运行次数,T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。
显然 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列

这里,给定规模 n,计算 Fib(n) 所需的时间为计算 Fib(n-1) 的时间和计算 Fib(n-2) 的时间的和。

T(n<=1) = O(1)

T(n) = T(n-1) + T(n-2) + O(1)

                     fib(5)   
                 /             \     
           fib(4)                fib(3)   
         /      \                /     \
     fib(3)      fib(2)         fib(2)    fib(1)
    /     \        /    \       /    \  
通过使用递归树的结构描述可知算法复杂度为 O(2^n)。

执行次数

N=10,大约执行1024次

N=100,大约执行2^100次

N=1000,大约执行2^1000次

N=10000,大约执行2^10000次

 

code-11:

int Fibonacci(int n)
{   if (n <= 1)  return n;
    else
    {     int iter1 = 0;
          int iter2 = 1;
          int f = 0;
          for (int i = 2; i <= n; i++)
          {
             f = iter1 + iter2;
             iter1 = iter2;
             iter2 = f;
          }
          return f;
    }
}

同样是斐波那契数列,由于实际只有前两个计算结果有用,我们可以使用中间变量来存储,这样就不用创建数组以节省空间。同样算法复杂度优化为 O(n)。

code-12:

通过使用矩阵乘方的算法来优化斐波那契数列算法。优化之后算法复杂度为O(log2n)。

static int Fibonacci(int n)
{  if (n <= 1)   return n;
   int[,] f = { { 1, 1 }, { 1, 0 } };
   Power(f, n - 1);
   return f[0, 0];
}
 
static void Power(int[,] f, int n)
{  if (n <= 1)   return;
   int[,] m = { { 1, 1 }, { 1, 0 } };
   Power(f, n / 2);
   Multiply(f, f);
   if (n % 2 != 0)   Multiply(f, m);
}
 
static void Multiply(int[,] f, int[,] m)
{   int x = f[0, 0] * m[0, 0] + f[0, 1] * m[1, 0];
    int y = f[0, 0] * m[0, 1] + f[0, 1] * m[1, 1];
    int z = f[1, 0] * m[0, 0] + f[1, 1] * m[1, 0];
    int w = f[1, 0] * m[0, 1] + f[1, 1] * m[1, 1];
    f[0, 0] = x;
    f[0, 1] = y;
    f[1, 0] = z;
    f[1, 1] = w;
}


 

code-13:

decimal Factorial(int n)
{  if (n == 0) return 1;
   else        return n * Factorial(n - 1);
}

阶乘(factorial),给定规模 n,算法基本步骤执行的数量为 n,所以算法复杂度为 O(n)。

 

 

6、立方阶举例运用

code-14:

decimal Sum3(int n)
{   decimal sum = 0;
    for (int a = 0; a < n; a++)
      for (int b = 0; b < n; b++)
         for (int c = 0; c < n; c++)
             sum += a * b * c;
    return sum;
}

这里,给定规模 n,则基本步骤的执行数量约为 n*n*n ,所以算法复杂度为 O(n^3)。
 

7、指数级举例运用

code-15:

decimal Calculation(int n)
{
    decimal result = 0;
    for (int i = 0; i < (1 << n); i++)  //  1<<n 相当于2^n次方,如果
        result += i;
    return result;
}

这里,给定规模 n,则基本步骤的执行数量为 2^n,所以算法复杂度为 O(2^n)。
 

 

四、练习

1、单项选择题

1.个算法应该是( )。

A.程序    B.问题求解步骤的描述    C.要满足五个基本特性    D. A和C

2.某算法的时间复杂度为O(n^2),表明该算法的( )。

A.问题规模是n^2    B.执行时间等于n^2

C.执行时间与n^2成正比    D.问题规模与n^2成正比

3.以下算法的时间复杂度为( )。

void fun(int n) 
{  int i=l;
   while(i<=n)
   i=i*2;
}

A. O(n)    B. O(n^2)    C. O(nlog2n)    D. O(log2n)

4.【2011年计算机联考真题】设n是描述问题规模的非负整数,下面程序片段的时间复杂度是()。

x=2;
while(x<n/2)
x=2*x;

A. O(log2n)    B. O(n)    C. O(nlog2n)    D. O(n^2)

5.【2012年计算机联考真题】求整数n (n>=0)阶乘的算法如下,其时间复杂度是( )。

int fact(int n)
{  if (n<=l) return 1;
   return n*fact(n-1);
}

A. O(log2n)    B. O(n)    C. O(nlog2n)     D. O(n^2)

6.有以下算法,其时间复杂度为( )。

void fun (int n)
{  int i=0;
   while(i*i*i<=n)   i++;
}

A. O(n)      B. O(nlogn)    C.      D. 

7.程序段如下,其中n为正整数,则最后一行的语句频度在最坏情况下是( )。

for(i=n-l;i>l;i--)
   for(j=1;j<i;j++)
     if (A[j]>A[j+l]) A[j]与 A[j+1]对换;

A. O(n)    B. O(nlogn)    C. O(n^3)    D. O(n^2)

8.以下算法中加下划线语句的执行次数为()。

int m=0, i, j;
for(i=l;i<=n;i++)
  for(j=1;j<=2*i;j++)  m++;

A. n(n+1)    B. n    C. n+1    D. n^2

9.下面说法错误的是( )。
Ⅰ.算法原地工作的含义是指不需要任何额外的辅助空间
Ⅱ.在相同的规模n下,复杂度O(n)的算法在时间上总是优于复杂度O(2^n)的算法
Ⅲ.所谓时间复杂度是指最坏情况下,估算算法执行时间的一个上界
Ⅳ.同一个算法,实现语言的级别越高,执行效率就越低

A. Ⅰ      B. Ⅰ、Ⅱ      C. Ⅰ、Ⅳ      D. Ⅲ

2、综合应用题

1.一个算法所需时间由下述递归方程表示,试求出该算法的时间复杂度的级别(或阶)。


式中,n是问题的规模,为简单起见,设n是2的整数幂。



2.分析以下各程序段,求出算法的时间复杂度。

程序段①

i=l;k=0;
while(i<n-l)
{  k=k+10*i;
   i++;
}

 程序段②

y=0;
while((y+1)*(y+1)<=n)
y=y+1;

程序段③

for(i=l;i<=n;i++)
   for(j =1;j <=i;j ++)
      for(k=l;k<=j;k++)
           x++;

程序段④

for(i=0;i<n;i++)
   for(j=0;j<m;j++)
      a[i][j]=0;

答案与解析

一、单项选择题

1.    B
程序不一定满足有穷性,如死循环、操作系统等,而算法必须有穷。算法代表了对问题求解步骤的描述,而程序则是算法在计算机上的特定的实现。

2.    C
时间复杂度为O(n^2),说明算法的执行时间T(n)<=c * n^2(c为比例常数),即T(n)=O(n^2),时间复杂度T(n)是问题规模n的函数,其问题规模仍然是n而不是n^2。

3.    D
基本运算是i=i*2,设其执行时间为T(n),则2T(n)<=n,即T(n)<=log2n=O(log2n)。

4.    A
在程序中,执行频率最高的语句为“x=2*x”。设该语句共执行了 t次,则2t+1=n/2,故t=log2(n/2)-1=log2n-2,得 T(n)=O(log2n)。

5.    B
本题是求阶乘n!的递归代码,即n*(n-1)*...*1共执行n次乘法操作,故T(n)=O(n)。

6.    C
算法的基本运算是i++,设其执行时间为T(n),则有,T(6)*T(n)*T(n)<=n,即T(n)3<=n。故有,

更加直观和快速的解题方法:要计算语句i++的执行次数(由于每执行一次i加1),其中判断条件可理解为i3=n,即,因此有

7.    D
当所有相邻元素都为逆序时,则最后一行的语句每次都会执行。此时,


所以在最坏情况下的该语句频度是O(n^2)。

8.    A
m++语句的执行次数为


9.    A
Ⅰ,算法原地工作是指算法所需的辅助空间是常量。Ⅱ,题中是指算法的时间复杂度,不要想当然认为是程序(该算法的实现)的具体执行时间,而赋予n—个特殊的值。时间复杂度为O(n)的算法,必然总是优于时间复杂度为O(2n)的算法。Ⅲ,时间复杂度总是考虑在最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。Ⅳ为严蔚敏教材的原话。

二、综合应用题

1.解答:

时间复杂度为O(nlog2n)。

设n=2k(k>=0),根据题目所给定义,有,由此,可得一般递推公式,进而,可得,即,即为

2.解答:

①基本语句是k=k+10*i,共执行了n-2次,所以T(n)=O(n)。

②设循环体共执行T(n)次,每循环一次,循环变量y加1,最终T(n)=y。故(T(n))2<=n,解得 T(n)=O(n1/2)。

③    x++是基本语句,

④a[i][j]=0是基本语句,内循环执行m次,外循环执行n次,共执行了    m*n次,所以 T(m, n)=O(m*n)0

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值