【春招冲刺班-7天刷题挑战】(一)

目录

一、LC 105. 从前序与中序遍历序列构造二叉树

1.1 题求

1.2 求解

二、LC 172. 阶乘后的零

2.1 题求

2.2 求解

三、 古生物血缘远近判定

3.1 题求

3.2 求解

四、LC 84. 柱状图中的最大矩形 ☆

4.1 题求

4.2 求解

五、LC 85. 最大矩形

5.1 题求

5.2 求解

六、LC 146. LRU 缓存机制

6.1 题求

6.2 求解

七、LC 907. 子数组的最小值之和

7.1 题求

7.2 求解

八、LC 403. 青蛙过河

8.1 题求

8.2 求解


一、LC 105. 从前序与中序遍历序列构造二叉树

1.1 题求

1.2 求解

法一:递归

# 44ms - 89.77
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        
        # preorder - 根节点 - 左子树 - 右子树 - [3,9,20,15,7]
        # inorder  - 左子树 - 根节点 - 右子树 - [9,3,15,20,7]
       
        def helper(lhs, rhs):
            ''' 递归 '''

            # 越界返回
            if lhs > rhs:
                return None
            
            # 当前(子)树根节点
            root = TreeNode(preorder.pop(0))
            # 当前(子)树根节点在中序遍历中的位置索引
            idx = hashmap[root.val]

            # 当前(子)树根节点的左子树
            root.left = helper(lhs, idx-1)
            # 当前(子)树根节点的右子树
            root.right = helper(idx+1, rhs)
            
            # 返回当前(子)树根节点
            return root
        
        # 中序遍历节点值及其索引映射表
        hashmap = {val: idx for idx, val in enumerate(inorder)}
            
        return helper(0, len(preorder)-1)

法二:迭代

# 36ms - 97.19% - 费解
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder:
            return None

        root = TreeNode(preorder[0])
        stack = [root]   # 前序遍历首个节点 - 根节点
        inorderIndex = 0  # 中序遍历首个节点
        # 依次枚举前序遍历中除了第 1 个节点外的每个节点
        for i in range(1, len(preorder)):
            # 前序遍历第 i 个节点
            preorderVal = preorder[i]  
            # 栈顶当前节点
            node = stack[-1]
            # 如果 inorder[inorderIndex] 和栈顶节点不同,于是将当前节点 node 作为栈顶节点的左儿子
            if node.val != inorder[inorderIndex]:
                node.left = TreeNode(preorderVal)
                stack.append(node.left)  # 左儿子入栈
            # 如果 inorderIndex 恰好指向栈顶节点,那么不断地弹出栈顶节点并栈顶节点 inorderIndex,
            # 并将当前节点作为最后一个弹出的节点的右儿子
            else:
                while stack and stack[-1].val == inorder[inorderIndex]:
                    node = stack.pop()  # 栈顶节点
                    inorderIndex += 1  # 栈顶节点
                node.right = TreeNode(preorderVal)
                stack.append(node.right)

        return root

参考资料:

力扣

https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou-zao-9


二、LC 172. 阶乘后的零

2.1 题求

2.2 求解

法一:计算因子 5

# 88ms - 32.11%
class Solution:
    def trailingZeroes(self, n: int) -> int:
        zero_count = 0
        for i in range(5, n+1, 5):
            current = i
            while current % 5 == 0:
                zero_count += 1
                current //= 5
        return zero_count

# 88ms - 32.11%
class Solution:
    def trailingZeroes(self, n: int) -> int:
        zero_count = 0
        for i in range(5, n+1, 5):
            power_of_5 = 5  # 5 的幂次
            while i % power_of_5 == 0:
                zero_count += 1
                power_of_5 *= 5
        return zero_count

法三:高效的计算因子 5

# 36ms - 74.84%
class Solution:
    def trailingZeroes(self, n: int) -> int:
        zero_count = 0
        current_multiple = 5
        while n >= current_multiple:
            zero_count += n // current_multiple
            current_multiple *= 5
        return zero_count

# 36ms - 74.84%
class Solution:
    def trailingZeroes(self, n: int) -> int:
        zero_count = 0
        while n > 0:
            n //= 5
            zero_count += n
        return zero_count

参考资料

力扣

力扣

力扣


三、</> 古生物血缘远近判定

3.1 题求

3.2 求解

法一:动态规划 - 递归 (从右往左比较)

# 16ms - 60.00%
### 编辑距离 ###

# 提取输入
word1, word2 = input().split(',')  # word1, word2 = map(str, input().split(','))

# 构造问题求解类对象
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # 哈希表备忘录
        memo = {}  

        def dp(i, j):
            ''' 返回 word1[0..i] 和 word2[0..j] 的最小编辑距离 '''
            if memo.get((i, j)) is not None:
                return memo[(i, j)]

            # word1 已经到头了, 只需要在 word1 插入剩余 word2 即可
            if i == -1:
                return j + 1  
            # word2 已经到头了, 只需要在 word2 插入剩余 word1 即可
            elif j == -1:
                return i + 1  

            # 当前值相等, 编辑距离完全取决于前面的部分
            if word1[i] == word2[j]:
                memo[(i, j)] = dp(i-1, j-1)
            # 插入、删除、修改三种方式中, 最少次数/编辑距离能够达到的一者
            else:
                memo[(i, j)] = min(dp(i-1, j), dp(i, j-1), dp(i-1, j-1)) + 1

            return memo[(i, j)]
        
        return dp(len(word1)-1, len(word2)-1)
        
# 实例化类对象并调用方法
solver = Solution()
print(solver.minDistance(word1, word2))    

法二:动态规划 - 迭代 (从左往右比较)

# 16ms - 60.00%
### 编辑距离 ###

# 提取输入
word1, word2 = input().split(',')  # word1, word2 = map(str, input().split(','))

import collections 

# 构造问题求解类对象
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        
        if not word1:
            return n
        elif not word2:
            return m
        
        dis = 0                              # 最小距离
        visited = {(0, 0)}                   # 已遍历坐标/索引
        queue = collections.deque([(0, 0)])  # 辅助 BFS 队列

        while queue:
            num = len(queue)
            for _ in range(num):
                # 当前需要试探的坐标 (两字符串索引)
                i, j = queue.popleft()
                
                # 从左往右遍历 - 相等直接跳过 不增加操作数
                while i < m and j < n and word1[i] == word2[j]:
                    i += 1
                    j += 1
               
                # 与 DFS 不同, 当两个字符串都正好遍历完, 则此时操作数即最少操作数
                # (相当于其余部分完全相同 或 遍历到尽头 无需任何操作 应返回结果了)                   
                if i == m and j == n:
                    return dis
                
                # i 还在范围内 且 i+1 尚未遍历过 —— 增            
                idx_add = (i+1, j)
                if i < m and idx_add not in visited:
                    visited.add(idx_add)
                    queue.append(idx_add)
                    
                # j 还在范围内 且 j+1 尚未遍历过 —— 删                
                idx_delete = (i, j+1)
                if j < n and idx_delete not in visited:
                    visited.add(idx_delete)
                    queue.append(idx_delete)
                    
                # i, j 还在范围内 且 i+1, j+1 尚未遍历过 —— 改                    
                idx_update = (i+1, j+1)
                if i < m and j < n and idx_update not in visited:
                    visited.add(idx_update)
                    queue.append(idx_update)
                    
            # 每层操作数+1 (增、删、改 均为 1 个操作)
            dis += 1 
        
# 实例化类对象并调用方法
solver = Solution()
print(solver.minDistance(word1, word2))    

参考资料:

力扣

力扣

力扣


四、LC 84. 柱状图中的最大矩形 ☆

4.1 题求

4.2 求解

法一:暴力法

# 超出时间限制
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        max_area = heights[0]
        for i in range(len(heights)):
            if heights[i] == 0:
                continue
                
            # 往左扫描
            cur_len = heights[i]
            k = i - 1 
            while k >= 0 and heights[k] > 0:
                cur_len = min(cur_len, heights[k])  # 最小高度
                max_area = max(max_area, (i-k+1)*cur_len)  # 最大面积
                k -= 1

        return max_area

官方说明

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        int ans = 0;
        // 枚举左边界
        for (int left = 0; left < n; ++left) {
            int minHeight = INT_MAX;
            // 枚举右边界
            for (int right = left; right < n; ++right) {
                // 确定高度
                minHeight = min(minHeight, heights[right]);
                // 计算面积
                ans = max(ans, (right-left+1) * minHeight);
            }
        }
        return ans;
    }
};

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        int ans = 0;
        for (int mid = 0; mid < n; ++mid) {
            // 枚举高
            int height = heights[mid];
            int left = mid, right = mid;
            // 确定左右边界
            while (left - 1 >= 0 && heights[left - 1] >= height) {
                --left;
            }
            while (right + 1 < n && heights[right + 1] >= height) {
                ++right;
            }
            // 计算面积
            ans = max(ans, (right - left + 1) * height);
        }
        return ans;
    }
};

# 308ms - 38.62%
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        
        # 柱子总数
        n = len(heights)
        # 左、右侧最近最低柱子索引
        left, right = [0] * n, [0] * n
        
        # 单调递增栈 - 辅助存放左侧最近最低柱子索引
        mono_stack = []
        # 从左往右遍历
        for i in range(n):
            # 栈非空 且 栈顶位置高度 heights[mono_stack[-1]] 高于当前位置高度 heights[i],
            # 则不会挡住 heights[i], 直至遇到距离最近且高度低于 heights[i] 的首根柱子
            while mono_stack and heights[mono_stack[-1]] >= heights[i]:
                mono_stack.pop()
            # mono_stack[-1] 即 i 左侧距离最近且高度低于 heights[i] 的首根柱子索引
            left[i] = mono_stack[-1] if mono_stack else -1  # -1 是左哨兵
            mono_stack.append(i)
        
        # 单调递增栈 - 辅助存放右侧最近最低柱子索引
        mono_stack = []
        # 从右往左遍历
        for i in range(n-1, -1, -1):
            while mono_stack and heights[mono_stack[-1]] >= heights[i]:
                mono_stack.pop()
            # mono_stack[-1] 即 i 右侧最近的小于 heights[i] 的柱子的索引
            right[i] = mono_stack[-1] if mono_stack else n  # n 是右哨兵
            mono_stack.append(i)
        
        # 最大矩形面积
        ans = max((right[i]-left[i]-1) * heights[i] for i in range(n)) if n > 0 else 0
        return ans

# 208ms - 92.67%
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        
        # 柱子总数
        n = len(heights)
        # 左、右侧最近最低柱子索引
        left, right = [0] * n, [n] * n  # new init

        # 单调递增栈 - 辅助存放左侧最近最低柱子索引
        mono_stack = []
        # 从左往右遍历
        for i in range(n):
            while mono_stack and heights[mono_stack[-1]] >= heights[i]:
                right[mono_stack[-1]] = i  # new step - i 即为此时弹出的位置的右边界 
                mono_stack.pop()
            # mono_stack[-1] 即 i 左侧最近的小于 heights[i] 的柱子的索引
            left[i] = mono_stack[-1] if mono_stack else -1  # -1 是左哨兵
            mono_stack.append(i)
        
        # 最大矩形面积
        ans = max((right[i]-left[i]-1) * heights[i] for i in range(n)) if n > 0 else 0
        return ans

参考资料:

力扣

力扣


五、LC 85. 最大矩形

5.1 题求

5.2 求解

# 256ms - 17.77%
class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0
        
        m = len(matrix)     # 行数
        n = len(matrix[0])  # 列数
        dp = [[0 for _ in range(n+1)] for _ in range(m)]  # dp 数组

        max_area = 0
        for i in range(m):
            for j in range(n):
                # 横向计算
                if matrix[i][j] == '0':  # 0 不考虑
                    continue
                dp[i][j] = dp[i][j-1] + 1  # 最右边的 dp +1 作为 index = -1
    
                # 纵向比较
                cur_area = cur_width = dp[i][j]  # 当前最大面积、宽度
                k = i - 1
                while k >= 0 and dp[k][j] > 0:
                    cur_width = min(dp[k][j], cur_width)
                    cur_area = max((i-k+1) * cur_width, cur_area)   
                    k -= 1
                    
                # 最大矩形面积
                max_area = max(max_area, cur_area)

        return max_area

# 88ms - 65.11%
class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0
        
        m = len(matrix)
        n = len(matrix[0])
        max_area = 0
        left = [[0 for _ in range(n)] for _ in range(m)]

        # 横向最大长度
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '1':
                    left[i][j] = (0 if j == 0 else left[i][j-1]) + 1
        
        # 对于每一列使用基于柱状图的方法 (左转的柱状图, left 相当于 heights)
        for j in range(n):  
            up = [0] * m    # 上边界
            down = [m] * m  # 下边界
            stack = []
            # 从上往下遍历
            for i in range(m):
                while stack and left[stack[-1]][j] >= left[i][j]:
                    down[stack[-1]] = i  # i 即为此时弹出的位置的下边界 
                    stack.pop()
                up[i] = stack[-1] if stack else -1  # 栈顶即为当前位置的上边界
                stack.append(i)

            # 当前列的最大矩形面积
            for i in range(m):
                max_area = max(max_area, (down[i]-up[i]-1) * left[i][j])

        return max_area

参考资料:

力扣

力扣


六、LC 146. LRU 缓存机制

6.1 题求

6.2 求解

法一:基于数组

# 超出时间限制
class LRUCache:
    def __init__(self, capacity: int):
        self.size = capacity              # LRU 容量固定不变
        self.hashmap = {}                 # 记录 key-value
        self.deque = collections.deque()  # 记录 key 的最近调用顺序
        
    def get(self, key: int) -> int:
        val = self.hashmap.get(key)
        if val is None:
            return -1
        else:
            self.update(key, val)  # 更新最近调用的 key 顺序
            return val

    def put(self, key: int, value: int) -> None:
        # 待加入值已存在, 则更新最近调用的 key 顺序
        if self.get(key) != -1:
            self.update(key, value)  
        # 待加入值不存在, 且长度未达到上限
        elif len(self.deque) < self.size:
            self.add(key, value) 
        # 待加入值不存在, 但长度到达上限, 则删一增一
        else:
            self.delete(key, value)
        #print(self.deque)
    
    def update(self, key, value):
        self.hashmap[key] = value
        num = len(self.deque)  # 需要弹出的次数
        # 1. 前端
        while self.deque[0] != key:
            self.deque.append(self.deque.popleft())
            num -= 1
        # 2. 找到
        cur_key = self.deque.popleft()  # 最近使用
        num -= 1
        # 3. 其余
        while num > 0:
            self.deque.append(self.deque.popleft())
            num -= 1
        # 4. 最近
        self.deque.appendleft(cur_key)  # 最近使用
  
    def delete(self, key, value):
        del self.hashmap[self.deque.pop()]  # 删除最后一个最不常用值
        self.hashmap[key] = value  # 更新键值对  
        self.deque.appendleft(key)  # 加入新值在首位

    def add(self, key, value):
        self.hashmap[key] = value  # 加入键值对
        self.deque.appendleft(key)   # 加入新值在首位

官方说明

class LRUCache(collections.OrderedDict):
    def __init__(self, capacity: int):
        super().__init__()

        self.capacity = capacity

    def get(self, key: int) -> int:
        if key not in self:
            return -1
        self.move_to_end(key)
        return self[key]

    def put(self, key: int, value: int) -> None:
        if key in self:
            self.move_to_end(key)
        self[key] = value
        if len(self) > self.capacity:
            self.popitem(last=False)

# 612ms - 52.21%
class DLinkedNode:
    ''' 双链表节点 '''
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None
        
class LRUCache:
    ''' LRU 缓存 '''
    def __init__(self, capacity: int):
        self.cache = dict()          # 哈希表:key -> node
        self.head = DLinkedNode()    # 伪头部节点节点
        self.tail = DLinkedNode()    # 伪尾部节点
        self.head.next = self.tail   # ->
        self.tail.prev = self.head   # <-
        self.capacity = capacity     # 最大容量
        self.size = 0                # 当前容量

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        # 如果 key 存在,先通过哈希表定位 node,再移到 head
        node = self.cache[key]
        self.moveToHead(node)
        return node.value

    def put(self, key: int, value: int) -> None:
        if key not in self.cache:
            # 如果 key 不存在,创建一个新的节点
            node = DLinkedNode(key, value)
            # 添加进哈希表
            self.cache[key] = node
            # 添加至双向链表的头部
            self.addToHead(node)
            self.size += 1
            if self.size > self.capacity:
                # 如果超出容量,删除双向链表的尾部节点
                removed = self.removeTail()
                # 删除哈希表中对应的项
                self.cache.pop(removed.key)
                self.size -= 1
        else:
            # 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
    
    def addToHead(self, node):
        node.prev = self.head       # <-
        node.next = self.head.next  # ->
        self.head.next.prev = node  # <-
        self.head.next = node       # ->
    
    def removeNode(self, node):
        node.prev.next = node.next  # ->
        node.next.prev = node.prev  # <-

    def moveToHead(self, node):
        self.removeNode(node)
        self.addToHead(node)

    def removeTail(self):
        node = self.tail.prev       # <-
        self.removeNode(node)       
        return node

参考资料

力扣

力扣


七、LC 907. 子数组的最小值之和

7.1 题求

7.2 求解

法一:暴力法

# 超出时间限制
class Solution:
    def sumSubarrayMins(self, arr: List[int]) -> int:
        n = len(arr)
        summ = 0
        for i in range(n):  # 起点索引
            dp = arr[i]  
            for j in range(i, n):  # 终点索引
                dp = min(arr[j], dp) 
                summ += dp
        return summ % (10**9 + 7)

官方说明

法一:单调栈

# 192ms - 38.80%
class Solution:
    def sumSubarrayMins(self, arr: List[int]) -> int:
        MOD = 10 ** 9 + 7
        N = len(arr)

        # prev has i* - 1 in increasing order of A[i* - 1]
        # where i* is the answer to query j
        stack = []          # 单调栈
        prev_ = [None] * N   # 左边界数组        
        for i in range(N):
            while stack and arr[stack[-1]] >= arr[i]:  # 遇小弹出
                stack.pop()
            # 左侧首个小于 arr[i] 的数的索引
            prev_[i] = stack[-1] if stack else -1  # -1 为左哨兵
            stack.append(i)  ### 作为后续数组中潜在的左边界索引

        # next has k* + 1 in increasing order of A[k* + 1]
        # where k* is the answer to query j
        stack = []          # 单调栈
        next_ = [None] * N  # 右边界数组       
        for k in range(N-1, -1, -1):
            while stack and arr[stack[-1]] > arr[k]:  # 遇小弹出
                stack.pop()
            # 右侧首个小于 arr[k] 的数的索引
            next_[k] = stack[-1] if stack else N  # N 为右哨兵
            stack.append(k)  ### 作为后续数组中潜在的右边界索引
        
        # print(arr)    # [3, 1, 2, 4]
        # print(prev_)  # [-1, -1, 1, 2]
        # print(next_)  # [1, 4, 4, 4]
        
        # Use prev/next array to count answer
        # 分别计算 arr[i] 作为最小值时的子数组个数 * arr[i] 
        return sum((i-prev_[i]) * (next_[i]-i) * arr[i]
                   for i in range(N)) % MOD

法二:最小值栈

# 136ms - 84.80%
class Solution:
    def sumSubarrayMins(self, arr: List[int]) -> int:
        MOD = 10**9 + 7
        stack = []  # 重要点的编码记录栈 —— 以 val 为最小值的子数组个数 count
        ans = dot = 0  # 和 & 子数组和
        
        # index, val
        for j, y_val in enumerate(arr):
            # Add all answers for subarrays [i, j], i <= j
            y_cnt = 1
            
            # x_val >= y_val 则 x_val 不再作为最小值, 而以 y_val 取而代之
            while stack and stack[-1][0] >= y_val:  
                x_val, x_cnt = stack.pop()  
                y_cnt += x_cnt              # y_val 可作为最小值的个数
                dot -= x_val * x_cnt        # x_val 不再作为最小值, 删除其和
                
            stack.append((y_val, y_cnt))    # 重要点的编码 (val, count) 入栈
            dot += y_val * y_cnt            # 最小值 * 个数 (同时包含之前和当前的最小值部分)
            ans += dot                      # 和
            
            # [3,1,2,4]
            #print(f"y_val: {y_val}, y_cnt: {y_cnt}, stack: {stack}, dot: {dot}, ans: {ans}")
            # y_val: 3, y_cnt: 1, stack: [(3, 1)], dot: 3, ans: 3
            # y_val: 1, y_cnt: 2, stack: [(1, 2)], dot: 2, ans: 5
            # y_val: 2, y_cnt: 1, stack: [(1, 2), (2, 1)], dot: 4, ans: 9
            # y_val: 4, y_cnt: 1, stack: [(1, 2), (2, 1), (4, 1)], dot: 8, ans: 17
            
        return ans % MOD

参考资料:

力扣

力扣


八、LC 403. 青蛙过河

8.1 题求

8.2 求解

法一:动态规划

# 164ms - 81.37%
class Solution:
    def canCross(self, stones: List[int]) -> bool:

        # dp[i] 表示到达石头 i 的步长 k 的集合
        dp = defaultdict(set)  # 缺省字典 - 默认类型 set
        dp[0] = {0}
        stones_set = set(stones)  # 石头集合
        
        for i in stones:
            for k in dp[i]:
                for j in [-1, 0, 1]:
                    step = k + j
                    dist = i + step
                    if (step > 0) and dist in stones_set:
                        dp[dist].add(step)  # 以 setp 步到达 dist

        return dp[stones[-1]] != set()

官方说明

# 64ms - 96.18%
class Solution:
    def canCross(self, stones: List[int]) -> bool:

        # import functools
        # @functools.lru_cache(maxsize=128, typed=False) 
        #     maxsize 是缓存的最大结果数目, 当 maxsize=None 时会变成简单的 cache, 就不具备 LRU特性了  
        #     typed 表示是否根据传入参数类型的不同, 缓存不同的结果

        @lru_cache(None)
        def dfs(pos, step):
            ''' dfs(pos, step): 若青蛙经 step 步跳跃至 pos 位置的石头, 它能否跳到终点 '''
            # 可达终点
            if pos == stones[-1]: 
                return True
            # 三种步长
            for d in {-1, 0, 1}:
                s = step + d  # 新步长
                p = pos + s   # 新步长跳达的位置
                if (s > 0) and (p in stones_set):
                    if dfs(p, s):
                        return True
            # 不可达终点
            return False
        
        stones_set = set(stones)  # 石头距离集合
        pos, step = 0, 0  # 起点, 步长
        return dfs(pos, step)

参考资料:

力扣

力扣

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值