[转载] Python 递归 深入理解递归 Python递归剖析,绝对让你看懂!

参考链接: Python | print()中的结束参数

目录

 递归剖析 递归的两个过程 return 返回值 详解

    递归思路二分法和递归尾递归递归练习题

  

 

 

 

递归剖析 

递归真的很重要,之前学的时候,学的一知半解,以为真正了解,每次想到递归,就记得一句:返回给函数的调用者,嗯?函数调用者,你是说外部,还是内部啊?疑问太多了,还有就是被告知一句:递归能解决的问题,循环都能解决,所以就更加不重视递归了!直到接触算法后,在解决问题时,最快,最容易理解的解法就是递归,但是此时的递归却是看不太懂为什么要这样做!我先来说下,在算法中遇到可以用递归轻松完成的:希尔排序、归并排序、快速排序、反转链表及各种反转问题、二叉树的深度遍历、二叉树的各种题基本可以用、全排列…还有算法步骤里需要用到,太多了,所以,递归非常重要!“To iterate is human, to recurse divine 迭代是人,递归是神”,接下来,我也是看了很多算法书和视频,重点来整理下递归! 

 递归的两个过程 

首先“递归”包括两个过程:递“去”的过程,“归”回的过程!先从一个简单的递归函数讲起 

def di_gui(n):

    print(n, "<===1====>")

    if n > 0:

        di_gui(n - 1)

    print(n, '<===2====>')

 

 

di_gui(5) # 外部调用后打印的结果是?

 

 递归的执行过程:首先,递归会执行“去”的过程,只需要满足终止条件,就会一直在函数内,带着更新的参数,调用函数自身,注意:到内部调用函数, 以下面的代码不会被执行,而是暂停阻塞;此时 随着函数每调用一次自身,还没有触发 返回值和到达终止条件,等同于在原来的基础上不断“向下/向内”开辟新的内存空间,记住,每次调用一次函数,就不是处在同一空间(想象下《盗梦空间》里的情景,梦中梦,都是处于不同的空间)   什么时候“递”去的过程结束?记住有两种情况>>> 一是:当前这层空间函数全部执行结束(终止条件),二是:执行到了return 返回值,直接返回;  重点来理解下,首先是一,看上面的列子,例子中没有return,但是不断的调用后,最终还是停止了,因为最后n=0时,di_gi(0)还是去调用,从上往下执行时,遇到if n>0 它被终止了,走不下去了,表明,自己能到达的这层空间已经全部执行完毕;接下来请原地返回吧,返回到哪里?返回到函数的调用者,好我们返回到 di_gui(0),把“到内部调用函数” 以下的代码全部执行完;执行完,看代码走到末尾,以为走出了最外层函数?注意了,此时它所处的空间并不是最外层哦,因为之前它被调用就在空间里面,所以回到的是 di_gui(1)的这一层空间,现在才是真正的开始“回”,所以继续把di_gui(1)的这一层空间,“到内部调用函数”以下的代码全部执行完,回到di_gui(2)的这一层空间…直到到达最开始 从外部调用,让参数5进入的最外层空间位置,才走出来!表示《盗梦空间》里,柯布醒了!  回来看下,代码输出的结果:5 4 3 2 1 00 1 2 3 4 5 ( 注意00这两个是在同一层空间哦)   从内存角度(本质)来分析:每调用一次函数,都会单独开辟一份栈帧空间,递归函数就是不停的开辟和释放栈帧空间的过程,具体来理解下:一个普通函数从执行到结束,就是一个开辟空间和释放空间的过程;而递归函数是在调用最外层函数时,先开辟一个最外层空间,每调用一次自身,就会在最外层空间内,再自己开辟本次的空间(所以递归耗内存)(还有一种说法是,不断的本次空间的基础上再开辟空间,等于是不断的嵌套,其实这两种说法本质上是一样的,因为信息都可以做到不共享),空间之间如果不通过参数传递或者用return 返回值,信息是不共享的,如下图↓↓↓  

  

 递归的数据共享情况:递归每一层间的数据是独立的,不共享,但是可以通过参数或者返回值来形成共享,参数具体在传入的是“引用类型”(比如列表)  递归 必须要有一个出口,如果没有出口就会重复调用,不断的开辟栈帧空间  

# 获取最大递归层数:

import sys

res=sys.getrecursionlimit()

print(res) # 结果:1000

# sys.setrecursionlimit(800) 可以自己设置最大递归层数

 

解释含有 return 返回值的递归,记住 return的返回流程是:先计算,再返回 ,来看下一段求阶乘的代码: 

def jie_cheng(n):

    if n <= 1:

        return 1

    return n * jie_cheng(n - 1)

    

print(jie_cheng(5))

 

 

return 是返回到函数调用者,递归函数和普通函数也是一样的,所以递归最后一层空间走到尽头(一是:指向完毕,二是:遇到return,回顾一下而已)遇到return,就要开始返回了,返回到它的调用者(即是阻塞的位置),直到回到最外层空间如果上面的内容看懂了的话,试着解析下:下面这段代码的执行流程 

def get_num(num):

    if num > 2:

        get_num(num - 1)

    print(num)

 

 

get_num(4) # 输出结果为:2 3 4

 

 

 

 代码变化一下: 

 

def get_num(num):

    if num > 2:

        get_num(num - 1)

    else:

        print(num)

 

 

get_num(4) # 输出结果为 2

 

'''

解析一下:加了else后,首先代码区有两个分支,

在num>2时,执行会有递归,当n=4 是开辟一层空间;

n=3时开辟一层空间,此时 get_num(2) 再开辟一个空间,

当它从上往下执行过程中,在他本层空间遇到if num>2 不成立,所以走分支 else,直接打印出来;

此时代码还没结束,回到上一层空间,num=3, num>2 已经进入了if 不会走else,

num=4 也不会走else,所以这两层空间没有值输出!

'''

 

 return 返回值 详解 

上面这一大部分,就算是递归入门了,接下来才刚刚开始哦!还要继续讲 return ;先来看下这几段代码:求全排列的一部分递归代码,试着分别写出运行结果,并依次分析原因↓↓↓ 

# 例1:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    remain = fullpermutation(list[1:])

    print(remain)

 

 

print(fullpermutation([1, 2, 3]))  

'''

输出结果为:

[[3]]

None

None

'''

 

递归只会在两种情况下触发“回”的过程,上述是在最后一层空间是碰到了return,所以给回到它的调用处(阻塞处),因为return会给调用者返回一个值,所以在本层空间,remain接收到了一个值:[[3]];接着执行下面的代码,即是打印remain,所以输出“[[3]]”,执行完print,等于回到了上一层空间,又到了调用处(阻塞处),那么这层空间还有返回值吗?答案是没有,所以“最后一层空间是碰到了return 给它返回的值 只会给最后一层使用”,所以接下来两层都是打印空! 

# 例2:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    return fullpermutation(list[1:])

 

 

print(fullpermutation([1, 2, 3]))

'''

输出结果为:

[[3]]

'''

 

这次是,在最后一层返回时,获得了一个返回值 [[3]] ,然后回到上一层时,前面又有return 表示需要把本层的返回值,返回到上层的接收处,重复,直到回到最外层,这个从底层传上来的返回值,一直传到了最外层,所以才打印出来的,只有最后一层,但是每次一层都获得了返回值,和例子1 后面两层没有返回值是不同的哦! 

# 例3:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    remain = fullpermutation(list[1:])

    print(list)

 

 

print(fullpermutation([1, 2, 3]))

'''

输出结果为:

[2, 3]

[1, 2, 3]

None

'''

 

最后一层碰到return,触发会的过程,回到调用处,执行阻塞处下面的代码,打印list,这个list是什么?它就是本层空间 参数的规模(因为代码写的是规模不断变小从[1,2,3]>>>[2,3]>>>[3]),显然,从最后一层回到上一层,此时规模是[2,3],所以打印list 就是[2, 3];接着继续回到上一层,即是最外层,规模是[1,2,3],所以打印[1, 2, 3],最后的None是因为最外层函数,没有返回值,所以才打印出None 

# 例4:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    remain = fullpermutation(list[1:])

    return list

 

 

print(fullpermutation([1, 2, 3]))

'''

输出结果为:

[1, 2, 3]

'''

 

依照上面的步骤,触发回的过程,只要没有到达最外层,return list 返回本层的规模(参数规模),那么这个返回值就会给本层的接收者 remain 不可能给最外层的接收者,虽然这里没有打印 remain的值,但是这里的remain和第一个列子中的remain,后面层数是有返回值的哦(下面例子就会体现),本例,返回到最外层时,list的本层规模为 [1,2,3] 最外层接收者接收到,然后打印出来,所以是[1,2,3] 

# 例5:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    remain = fullpermutation(list[1:])

    print(remain)

    return list

 

 

print(fullpermutation([1, 2, 3]))

'''

输出结果为:

[[3]]

[2, 3]

[1, 2, 3]

'''

 

看上面的例子,这次我们是打印了 remain,因为返回的过程中,指向阻塞处(调用处)下面的代码,每次return list 即是 在本层返回 当前的参数规模,所以 remain 是能接收本层的返回值的,所以会打印 [[3]] ,[2, 3] ;最后 [1, 2, 3] 是最外层打印的 

# 例6:

def fullpermutation(list):

    if list is None:

        return None

    if len(list) == 1:

        return [list]

    res = []

    pivot = list[0]

    remain = fullpermutation(list[1:])

    print(remain)

    print(list)

    return list

 

 

print(fullpermutation([1, 2, 3]))

'''

输出结果为:

[[3]]

[2, 3]

[2, 3]

[1, 2, 3]

[1, 2, 3]

'''

 

 这个,就是所有的融合;通过上面的代码我们来总结下return的返回值:最后一层遇到的return 需要执行 回的过程,此时的返回值 值会返回给最后的调用处的接收者;然后执行 阻塞处下面的代码时,如果又遇到return 这里的返回值,如果没有到达最外层,都是给本层的接收者!为什么要大费周章的讲这个,是因为我们在写递归时,往往不清楚return 要怎么写,已及它的返回值是什么?接下来就要看下return 如何解决问题的  请用递归完成一个字符串的反转,输入字符串“abcd”,完成翻转成“dcba” 除了递归,我们可以直接用字符串的切片来完成,如下,但是这里是要讲递归的思想,不体现方法优劣!  

s="abcd"

print(s[::-1])

 

想一下递归的思路该怎么做? 思路一:按照上面的例子,不断的划分子规模,我们选的是划分原字符串的规模,都是往后截取的,比如第一次参数s=“abcd” 我们取参数s[1:] ,不断调用函数,每次传入参数为:‘bcd’ ‘cd’ ‘d’ ,这样最后一层返回d 我们能拿到最后一个值,然后最后一个返回值d 加上 当前规模的第一个值,就完成了反转,具体代码如下: 

def str_reverse(s):

    if len(s) <= 1: # 递归出口

        return s

    # last = str_reverse(s[1:])

    # return last + s[0]

    return str_reverse(s[1:]) + s[0] # 每次返回最后一层的值,加上当前规模的第一个值

 

思路二:改变下子规模,我们这次不划分原字符串,而是从它的索引下手,传入最后一个元素的索引,不断去递归索引的值,这时,变的是end的值,从 3 到 2 1 0,到0时触发回的过程,返回当前索引的值,即是 a (不断向前取值),这时我们再加上当前层的s,因为没有划分s所以s每一层都是等于‘abcd’的,我们每次取当前end索引指向的字符串的值,等于从前往后遍历字符串 ‘abcd’ ,两部分链接起来,就完成了反转:每次的返回值为 a, ba , cba, dcba, 

def reve(str, end):

    if end == 0:

        return "" + str[0]

    last = str[end] + reve(str, end - 1)

    return last

 

 

s = "abcd"

print(reve(s, len(s) - 1))

 

 两种做法都用到return,而是还有重要的递归思路,下面就来看看递归要用什么思路来解  来试下“反转链表”如何用递归做,首先对于一个单链表,要对它反转,我们的思路依旧可以通过不断向后划分子规模,找到它的最后一个结点,然后从后往前依次改变每个结点的指向,让最初的头结点变成尾结点,让它最后指向None;那么具体步骤是:用递归找到最后一个结点,我们可以通过前一个结点的next区域,不断递归,找到下一个结点,直到当前结点的next是None,说明它就是最后一个结点,这也是我们递归的出口,那么依次让它返回,同时改变指向,就完成了,具体看下面的图解:  

class ListNode: 

    def __init__(self, x):

        self.val = x

        self.next = None

 

 

class Solution:

    def ReverseList(self, pHead):

        if pHead is None: # 判断传入的头结点是否为空

            return None

        if pHead.next is None: # 如果只有一个结点,直接返回结点;同时也是递归的出口

            return pHead

        last_node = self.ReverseList(pHead.next) # last_node永远只接收到了最后一个结点

        pHead.next.next = pHead # 后一个结点,指向前一个结点

        pHead.next = None # 前一个结点,在不同的层,先指向None,如果到达最外层也会指向None

        return last_node # 最后返回最后一个结点,表示反转成功

 

到达最后一层,触发回的过程,返回 last_node 结点给最后第四层,执行阻塞处下面的代码,因为pHead.next.next 是None,它不用改变也行,直接到 return last_node 把 last_node给第三层用… 第三层获得 last_node ,执行阻塞处下面的代码,改变指向,pHead.next.next 是“5结点”,它的指针指向 pHead,即是上一个结点,然后上一个结点指向None;因为这里并不需要用一个temp 来保存前一个指针信息,表面上是断开链接,会丢掉数据,其实不会丢掉,因为他们不再同一层;可以用temp先保存前一个结点的数,这属于递归的写,在我的文章>>>反转链表多种解法 有提到,这里不细说!  直到 回到最外层,起初的头结点,自然会指向None,最后返回 last_node即可 这才是 链表反转 递归的详细过程,果然和我开始理解的不一样,当初怎么都无法理解,直到用断点调试,才发现真正的过程,是这样,大家可以用断点调试下,看下代码的具体过程,下面是测试代码: 

class ListNode:

    def __init__(self, x):

        self.val = x

        self.next = None

 

 

class Solution:

    def ReverseList(self, pHead):

        if pHead is None:

            return None

        if pHead.next is None:

            return pHead

        last_node = self.ReverseList(pHead.next)

        print(last_node.val)

        pHead.next.next = pHead

        pHead.next = None

        return last_node

 

    def print_list(self, node):  # 打印测试

        if node is None:

            return None

        while node:

            print(node.val, end="")

            node = node.next

        print()

 

 

if __name__ == '__main__':

    n1 = ListNode(1)  # 依次实例5个结点

    n2 = ListNode(2)

    n3 = ListNode(3)

    n4 = ListNode(4)

    n5 = ListNode(5)

    n1.next = n2  # 依次将结点链接起来,形成一个链表

    n2.next = n3

    n3.next = n4

    n4.next = n5

    n5.next = None

 

    obj = Solution()

    print(obj.ReverseList(n1).val)

    # obj.print_list(n1) # 1 2 3 4 5

    # obj.print_list(obj.ReverseList(n1))  # 5 4 3 2 1

 

递归思路 

 思想: 1.找到当前这个值与上一个值的关系 2.找到程序的出口 有个明确的结束条件 3.假设当前功能已经完成 每次进入更深一层递归时,问题规模相比上次递归都应有所减少  递归思路: (1)找重复:看哪一部分是 实现函数的变化;每次进入更深一层递归时,问题规模相比上次递归都应有所减少 (2)找变化:变化的量应该作为参数 (3)找边界(出口):终止条件  递归可以分为: (1)直接量+小规模子问题 (2)多个小规模子问题 (3) “切蛋糕”思维 (4)找递推公式,等价交换公式  

二分法和递归 

# 二分法一定是在排序好的数据里使用

 

lst = [33, 22, 44, 55, 66, 88, 77, 99, 101, 238, 345, 456, 567, 678, 789]

n = 76

lst.sort()

left = 0

right = len(lst) - 1

 

while left <= right:  # 条件是 开头<=结尾

    middle = (left + right) // 2

    if lst[middle] > n:  # 每次用对折后,中间的数和 查找对象比较

        right = middle - 1

    elif lst[middle] < n:

        left = middle + 1

    elif lst[middle] == n:

        print("找到了")

        break

else:

    print("这个数不在列表中")

 

# 递归函数来做

lst = [22, 33, 44, 55, 66, 77, 88, 99, 101, 238, 345, 456, 567, 678, 789]

 

 

def func(n, left, right):

    if left <= right:  # 边界

        mid = (left + right) // 2

        if n > lst[mid]:

            left = mid + 1

            func(n, left, right)  # 递归的入口,目的是再确定一次中间的位置

        elif n < lst[mid]:

            right = mid - 1

            func(n, left, right)

        elif n == lst[mid]:

            print("找到了")

            return  # 递归出口

    else:

        print("没有这个数")

        return  # 递归的出口

 

 

func(66, 0, len(lst) - 1)

 

# 升级:如果找到了要求的数,请返回它的索引

 

lst = [22, 33, 44, 55, 66, 77, 88, 99, 101, 238, 345, 456, 567, 678, 789]

 

 

def func(n, left, right):

    if left <= right:  # 边界

        mid = (left + right) // 2

        if n > lst[mid]:

            left = mid + 1

            return func(n, left, right)  # 每一层都要返回给上一层的调用者

        elif n < lst[mid]:

            right = mid - 1

            return func(n, left, right)

        elif n == lst[mid]:

            print("找到了")

            return mid  # 多层函数只会将返回值返回给上一层的调用者

    else:

        print("没有这个数")

        return -1

 

 

print(func(66, 0, len(lst) - 1))

 

尾递归 

尾递归(自己调用自己,且非表达式:把值放到参数中算)

 

 

>>>求斐波那契数列第n位是几?(尾递归做法)

 

def feibo(num, res, temp):

     #使用尾递归法求解斐波那契数量的第num个数字

     if num == 0:

          return res

     else:

          return feibo(num - 1, temp, res + temp)

 

print(feibo(10,0,1))

 

# 直接递归  尾递归 与 循环 的对比

 

import time

 

 

def Fib_recursion(num):

    '''

    直接使用递归法求解斐波那契数量的第num个数字

    '''

    if num < 2:

        return num

    return Fib_recursion(num - 1) + Fib_recursion(num - 2)

 

 

def Fib_tail_recursion(num, res, temp):

    '''

    使用尾递归法求解斐波那契数量的第num个数字

    '''

    if num == 0:

        return res

    else:

        return Fib_tail_recursion(num - 1, temp, res + temp)

 

 

def Fib_circle(num):

    '''

    直接使用循环来求解

    '''

    a = 0

    b = 1

    for i in range(1, num):

        c = a + b

        a = b

        b = c

    return c

 

 

if __name__ == '__main__':

    num_list = [5, 10, 20, 30, 40, 50]

    for num in num_list:

        start_time = time.time()

        print(Fib_recursion(num))

        end_time = time.time()

        print(Fib_tail_recursion(num, 0, 1))

        end_time2 = time.time()

        print(Fib_circle(num))

        end_time3 = time.time()

        print('正在求解的斐波那契数字下标为%s' % num)

        print('直接递归耗时为 :', end_time - start_time)

        print('尾递归调用耗时为:', end_time2 - end_time)

        print('直接使用循环耗时为:', end_time3 - end_time2)

 

递归练习题 

>>>打印f-e的数:

 

def pri(f, e):

    if f > e:

        return

    else:

        print(f)

        return pri(f + 1, e)

 

 

pri(1, 6)

 

 

>>>求一个列表的和:

def sum_lis(li, f):  # 如果单独是传入一个列表,它体现不了变化

    """

    :param li: 传入一个列表

    :param f: 列表起始位置

    :return: 列表和

    """

    if f == len(li) - 1:  # 表示起始位置也是结束位置,即只有一个元素

        return li[f]

 

    return li[f] + sum_lis(li, f + 1)

 

 

print(sum_lis([1, 2, 3, 4, 5], 0))

 

# 多引入一个 变化的参数,可以想到第一个元素加上剩下的元素,规模不断变小

 

# 需求:求 1+2+3+4........100 的和

num = 1

count = 0

while num <= 100:

    count += num

    num += 1

print(count)

 

'''

思路:

sum(1) + 2 +3.....100

sum(2) + 3........100

sum(3) + 4........100

...

sum(98)+99+100

sum(99) + 100

sum(100)

 

sum(100) = sum(99) + 100

sum(99) = sum(98) + 99

sum(98) = sum(97) + 98

.....

 

sum(2) = sum(1) + 2

 

sum(1) = 1

'''

# 用递归函数解决

def sum(num):

    if num == 1:  # 出口

        return 1

    return num + sum(num - 1)  # 一直返回sum(num-1)+num,每次递归调用,有return才能有返回值

 

 

print(sum(100))

 

>>>需求:打印斐波那契数列

def fibo(num):  # 参数是表示第n个斐波那契数,函数整体表示获取斐波那契数列中第n个数字的值

    if num == 1 or num == 2:  # 第一个和第二个都是1

        return 1  # 返回1,出口

    return fibo(num - 1) + fibo(num - 2)  # 规律,后一项加上后两项,就等于斐波那契数列第n个数字的值

 

 

if __name__ == '__main__':

    list_num = []  # 创建一个空列表,开始

    for i in range(1, 21):  # 遍历1-20

        list_num.append(fibo(i))  # 注意这里开始调用函数,获得一个斐波那契数字,将获取到的值填充到list_num

    print(list_num)

 

 

# 最佳解法:

def fei_bo(n):

    if n <= 1:

        return (n, 0)

    else:

        (a, b) = fei_bo(n - 1)

        return (a + b, a)

 

 

print(fei_bo(5))

# 这里是线性的解法,不再重复计算前一项已知的数

 

# 求最大公约数

def maxg(m, n):

    if n == 0:

        return m

    return maxg(n, m % n)

 

 

print(maxg(6, 0))

 

# 最大公约数:

# 如果m%n=0 则n是m的最大公约数;例如4%2=0 则2是最大公约数 

# 如果m%n=K 则 n%k=?0 >>> f(m,n)=f(n,m%n)

 

 

>>>用递归实现列表排序:

def ins(li, k):

    if k == 0:

        return

    # 对前K个元素进行排序

    ins(li, k - 1)

    # 把位置K的元素插入到前面的部分

    x = li[k]

    index = k - 1

    while x < li[index]:

        li[index + 1] = li[index]

        index -= 1

    li[index + 1] = x

    print(li)

 

 

ins([1, 4, 3, 2], 3)

 

>>>需求:递归实现遍历目录

 

import os

 

 

def get_alldirfile(source_path):  # 定义一个函数获取用户输入的路径名下所有目录和文件

    if not os.path.exists(source_path):  # 判断用户输入的目录是否存在

        return  # 不存在,直接返回,结束该函数,找到一个出口

    list_name = os.listdir(source_path)  # lisdir获取所有目录,并全部放到一个列表中

    for flie_dirname in list_name:  # 遍历下所有的文件目录名

        abs_path = os.path.join(source_path, flie_dirname)  # 拼接成绝对路径

        # 判断下一级是否是目录还是文件,是文件结束,是目录继续深入,直到是文件结束

        if os.path.isfile(abs_path):  # 是文件

            print("file_path:%s" % (abs_path))

        # 也可进行复制操作,open(abs_path,"w",encoding="utf-8")

        if os.path.isdir(abs_path):

            get_alldirfile(abs_path)  # 递归函数

 

 

if __name__ == '__main__':

    path = r"F:\日语\快乐50音"

    get_alldirfile(path)

 

# 优化

import os

 

 

def file_get(file_path, n):

    list_file = os.listdir(file_path)

    for file in list_file:

        abs_file = os.path.join(file_path, file)

        if os.path.isdir(abs_file):

            print("\t" * n, file)

            file_get(abs_file, n + 1)

        else:

            print("\t" * n, file)

 

 

file_get("D:\KuGou", 1)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值