leetcode-单调栈/单调队列

栈和队列都是为了保证元素的一种顺序性。(其实递归也是有顺序性的(运行到最后一步,再往前走),所以可用stack来模拟递归过程)

单调队列:队列的性质是先进先出,为什么滑动窗口的最大值这个题用单调递减队列?因为窗口往右边滑动的时候,队列前端的值不在窗口里了,把队列前端的值弹出。右侧的大值,一定可以屏蔽左侧的小值。

为什么862用单调递增队列?因为根据前缀和性质,用队列尾减去队列前,得到的就是这中间值之和,本质上是比较窗口的两端元素。

单调栈:栈的性质是先后进先出,为什么接雨水这个题用单调递减栈?因为需要比较的是相邻(不一定是挨着)的元素,而不是比较窗口两端元素。

规律:求s[i]的左边界和右边界。

力扣

目录

42 接雨水

84 柱状图的最大矩形

85 最大矩形

221 最大正方形

239 滑动窗口最大值

739. 每日温度

5614. 找出最具竞争力的子序列

678. 有效的括号字符串



42 接雨水

接雨水和柱形图最大面积,一个是凹槽,一个是凸起,凹槽是找左边的最大值和右边的最大值,所以不需要哨兵节点。凸起找左边的最小值和右边的最小值,需要哨兵节点。

接雨水问题(python实现单调栈问题)_meng_xin_true的博客-CSDN博客

代码随想录代码随想录PDF,代码随想录百度网盘,代码随想录知识星球,代码随想录八股文PDF,代码随想录刷题路线,代码随想录知识星球八股文https://programmercarl.com/0042.%E6%8E%A5%E9%9B%A8%E6%B0%B4.html#%E5%8D%95%E8%B0%83%E6%A0%88%E8%A7%A3%E6%B3%95

class Solution(object):
    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        #维护一个单调递减栈
        ans=0
        stack=[]
        for i in range(len(height)):
            while len(stack) and height[i]>height[stack[-1]]:
                cur_index=stack.pop()
                if len(stack)==0:
                    break
                left = height[stack[-1]]
                right = height[i]
                distans = i - stack[-1] -1
                cur_ans= (min(left,right)-height[cur_index])*distans
                ans=ans+cur_ans
            stack.append(i)
        return ans
class Solution:
    def trap(self, height: List[int]) -> int:
        #单调递减栈。栈顶存储最小元素。
        #栈顶元素是凹槽,栈顶前一个元素是左边挡板,当前元素(如果比栈顶大),是右边挡板
        ans=0
        stack=[]
        for i in range(len(height)):
            #如果当前元素大于栈顶,当前元素是右挡板
            right=height[i]
            while stack and right>height[stack[-1]]:
                mid_index=stack.pop() #凹槽
                if not stack:
                    break
                left=height[stack[-1]] #倒数第二个元素是左挡板
                h=min(left,right)-height[mid_index]
                w=i-stack[-1]-1
                cur_ans=h*w
                ans+=cur_ans
            stack.append(i)
        return ans

84 柱状图的最大矩形

接雨水和柱形图最大面积,一个是凹槽,一个是凸起,凹槽是找左边的最大值和右边的最大值,所以不需要哨兵节点。凸起找左边的最小值和右边的最小值,需要哨兵节点。

https://segmentfault.com/a/1190000022791301

单调递增栈。遍历数组,把数组下标放到stack中,如果当前元素小于stack的栈顶元素,弹出stack栈顶元素作为高度,宽度就是留守的Stack栈顶元素和当前元素下表的差值。

注意哨兵节点的使用。

class Solution(object):
    def largestRectangleArea(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        size = len(heights)
        # 这里我们使用哨兵节点,这样就能够避免非空判断
        heights = [0] + heights+[0]
        # 数组长度发生了变化
        size += 2
        # 将哨兵先放入栈中
        stack = [0]
        ans = 0
        for i in range(1, size):
            # 与栈顶元素比较,进行判断是否要入栈
            # 出栈的逻辑
            while heights[i] < heights[stack[-1]]:
                cur_height = heights[stack.pop()]
                # 确定宽度
                cur_width = i - stack[-1] - 1
                ans = max(ans, cur_height * cur_width)
            stack.append(i)
        return ans
class Solution(object):
    def largestRectangleArea(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        ans=0
        heights=[0]+heights+[0]
        stack=[]
        for i in range(len(heights)):
            while len(stack) and heights[i]<heights[stack[-1]]:
                cur_index=stack.pop()
                if len(stack)==0:
                    break
                cur_height=heights[cur_index]
                distance=i-stack[-1]-1
                cur_ans=cur_height*distance
                ans=max(ans,cur_ans)
            stack.append(i)
        return ans

85 最大矩形

遍历每行,以每行为底边,可以看作是求最大柱状图的面积。

当这行中,元素为0的时候,柱状图列表为0,元素为1的时候,直接和上一行的进行累加。

class Solution(object):
    def maxarea(self,height):
        stack=[]
        height=[0]+height+[0]
        ans=0
        for i in range(len(height)):
            while len(stack) and height[i]<height[stack[-1]]:
                cur_index=stack.pop()
                cur_height=height[cur_index]
                distnce=i-stack[-1]-1
                cur_ans=cur_height*distnce
                ans=max(ans,cur_ans)
            stack.append(i)
        return ans
    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if len(matrix)==0:
            return 0
        res=0
        height=[0 for _ in range(len(matrix[0]))]
        for i in range(len(matrix)):
            cur=matrix[i]
            for j in range(len(cur)):
                if cur[j]=="1":
                    height[j]=height[j]+1
                elif cur[j]=="0":
                    height[j]=0
            print(height)
            cur_ans=self.maxarea(height)
            res=max(res,cur_ans)
        return res

221 最大正方形

最大矩形加一行代码。在计算面积的时候限制一下distance和height的大小。

class Solution(object):
    def maxarea(self,height):
        stack=[]
        height=[0]+height+[0]
        ans=0
        for i in range(len(height)):
            while len(stack) and height[i]<height[stack[-1]]:
                cur_index=stack.pop()
                cur_height=height[cur_index]
                distnce=i-stack[-1]-1
                if distnce>=cur_height:
                    cur_ans=cur_height*cur_height
                    ans=max(ans,cur_ans)
            stack.append(i)
        return ans
    def maximalSquare(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if len(matrix)==0:
            return 0
        res=0
        height=[0 for _ in range(len(matrix[0]))]
        for i in range(len(matrix)):
            cur=matrix[i]
            for j in range(len(cur)):
                if cur[j]=="1":
                    height[j]=height[j]+1
                elif cur[j]=="0":
                    height[j]=0
            print(height)
            cur_ans=self.maxarea(height)
            res=max(res,cur_ans)
        return res

239 滑动窗口最大值

暴力解法,时间复杂度o(nk).超时。

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        res=[]
        for i in range(len(nums)-k+1):
            left=i
            right=i+k
            cur_max=nums[left]
            for j in range(left+1,right):
                cur_max=max(cur_max,nums[j])
            res.append(cur_max)
        return res

单调队列

空间换时间,时间复杂度o(n)

力扣

import collections
#单调递减队列
class Solution:
    def maxSlidingWindow(self, nums, k):
        if not nums or k == 0: return []
        deque = collections.deque()
        for i in range(k):
            #前一个数字,小于当前数字。把前一个数字弹出
            while deque and deque[-1] < nums[i]:
                deque.pop()
            deque.append(nums[i])
        print(deque)
        res = [deque[0]]
        #i表示滑动窗口最右边
        for i in range(k, len(nums)):
            #如果队列头部值,是上一个窗口的值,就弹出去。
            if deque[0] == nums[i - k]:
                deque.popleft()
            while deque and deque[-1] < nums[i]:
                deque.pop()
            deque.append(nums[i])
            res.append(deque[0])
            print(i,res,deque)
        return res
nums=[2,3,4,2,6,2,5,1]
k=3
result=Solution().maxSlidingWindow(nums,k)
print(result)

739. 每日温度

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        stack = []
        res = [0] * len(T)
        for i in range(len(T)):

            while stack and T[stack[-1]] < T[i]:
                res[stack[-1]] = i - stack[-1]
                stack.pop()
            
            stack.append(i)
        return res


作者:xiao-xue-66
链接:https://leetcode-cn.com/problems/daily-temperatures/solution/pythonti-jie-dan-diao-di-jian-zhan-by-xiao-xue-66/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution(object):
    def dailyTemperatures(self, T):
        """
        :type T: List[int]
        :rtype: List[int]
        """
        #单调递减栈
        stack=[]
        res=[0 for _ in range(len(T))]
        for i in range(len(T)):
            while len(stack) and T[stack[-1]]<T[i]:
                cur_index=stack.pop()
                cur_res=i-cur_index
                res[cur_index]=cur_res
            stack.append(i)
        return res

5614. 找出最具竞争力的子序列

 https://leetcode-cn.com/problems/find-the-most-competitive-subsequence/solution/python3-shuang-bai-jie-fa-by-ni-jiao-wo-dpma/

class Solution(object):
    def mostCompetitive(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        #维护一个单调递增栈
        stack=[]
        n=len(nums)
        for i in range(n):
            while len(stack) and stack[-1]>nums[i] and (n-i-1)>= k-len(stack):
                stack.pop()
            stack.append(nums[i])
        return stack[:k]

678. 有效的括号字符串

力扣

 题解:开始的是我是用的,在一开始,我只是单纯的记录左右括号数量的差值和星的个数。这样做忽略了第三个条件。

要在栈里记录位置信息。

先看一下我最开始的题解

class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack=[]
        cnt=0
        for i in range(len(s)):
            cur=s[i]
            if cur=='(':
                stack.append(cur)
            elif cur==')':
                if len(stack)==0:
                    #用星号模拟左括号
                    if cnt>0:
                        cnt=cnt-1
                else:
                    stack.pop()
            elif cur=="*":
                cnt=cnt+1
        if len(stack)==0:
            return True
        #用星号模拟右括号
        if len(stack)==cnt:
            return True
        return False
class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack=[]
        stack_star=[]
        cnt=0
        for i in range(len(s)):
            cur=s[i]
            if cur=='(':
                stack.append(i)
            elif cur==')':
                if len(stack)==0:
                    #用星号模拟左括号
                    if len(stack_star)>0:
                        stack_star.pop()
                    else:
                        return False
                else:
                    stack.pop()
            elif cur=="*":
                stack_star.append(i)
        if len(stack)==0:
            return True
        #stack不为空的时候
        while stack:
            if not stack_star:
                return False
            elif stack[-1]>stack_star[-1]:
                return False
            else:
                stack.pop()
                stack_star.pop()
        return True
  

32. 最长有效括号

//最长的有效括号--------------->括号子串,括号子串的结尾一定是')',根据右括号为右端点去寻找合法的最长子串对应的左括号
    //只包含'('和')'
    public int longestValidParentheses(String s) {
        int n = s.length(), maxLen = 0;
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(-1);
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            // 如果是左括号直接入栈即可
            if (ch == '(')
                stack.push(i);
            else {
                // 如果是右括号,存在两种情况
                // 1.如果前面可以有左括号和它进行匹配,那么就存在一个由左括号、右括号组成的子串
                // 2.如果前面没有左括号和它进行匹配,那么这个右括号就形成了新的边界。新的子串匹配时,起点必须在该边界右边
                if (stack.size() == 1) {
                    // 如果栈的大小为1,说明只存放了边界
                    // 说明存放的内容为:边界
                    // 相应的做法为边界替换,将旧的边界出栈,然后放入新的边界
                    // 说明该右括号找不到和它可以匹配的左括号,那么该子串长度无效。同时这个右括号形成了新的边界(参照物)
                    stack.pop();
                    stack.push(i);
                } else {
                    // 如果栈的大小不为1
                    // 说明存放的内容为:边界+左括号索引+左括号索引+左括号索引......
                    // 说明可以找到和它进行匹配的左括号
                    stack.pop();
                    maxLen = Math.max(maxLen,i-stack.peek());
                }
            }
        }
        return maxLen;
    }
2
踩
查看 1 条回复
回复

907. 子数组的最小值之和

 单调栈+组合数学

力扣

496. 下一个更大元素 I

 力扣

单调递减栈

class Solution(object):
    def nextGreaterElement(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        #找右的下一个更大元素,从后往前遍历,维护一个单调递减栈
        #为什么要从后往前遍历,因为是要找右侧,只有从后往前遍历才能比较到当前元素和右侧元素

        #先用单调递减栈找到nums2的下一个更大元素
        stack=[]
        next_max=[-1 for _ in range(len(nums2))]
        for i in range(len(nums2)-1,-1,-1):
            #判断当前元素和右侧元素(右侧元素存储在单调栈中,栈顶是挨着最近的)
            while stack and nums2[i]>stack[-1]:
                stack.pop()
            if stack and nums2[i]<stack[-1]:
                next_max[i]=stack[-1]
            stack.append(nums2[i])
        res=[]
        for i in range(len(nums1)):
            num=nums1[i]
            num_index=nums2.index(num)
            cur_ans=next_max[num_index]
            res.append(cur_ans)
        return res

503. 下一个更大元素 II

环形数组+单调递减栈

力扣

class Solution(object):
    def nextGreaterElements(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        #用取模来模拟环形数组
        stack=[]
        n=len(nums)
        res=[-1 for _ in range(len(nums))]
        for i in range(n*2-1,-1,-1):
            #如果当前元素大于栈顶元素
            while stack and nums[i%n]>=stack[-1]:
                stack.pop()
            if stack and nums[i%n]<stack[-1]:
                res[i%n]=stack[-1]
            stack.append(nums[i%n])
        return res

2104. 子数组范围和

这个题是找的下一个更大/更小元素,和最大宽度坡不一样,最大宽度坡找的不是下一个更大/更小元素。

 计算 右侧比5小的第一个数,从右往左遍历,单调递增栈。

入栈:[2],1比2小,2出栈,1入栈[1],4入栈,[1,4],7入栈,[1,4,7],6比7小,7出栈,6入栈,[1,4,6]

为什么是单调递增栈?因为要求的是右边第一个比cur小的数,假设右侧j小于i,i出栈,j入栈,假如i比cur小,那么j也一定比cur小,j更靠近cur,所以可用小数屏蔽大数。

 力扣

 力扣

class Solution:
    def subArrayRanges(self, nums: List[int]) -> int:
        # nums[i]是多少子数组的最大值,以及多少子数组的最小值。相加
        # 求nums[i]是多少子数组的最大值,找到nums[i]左边和右边的边界,2个单调栈
        # 求nums[i]是多少子数组的最小值,找到nums[i]左边和右边的边界,2个单调栈。共4个单调栈

        left_max = [-1 for i in range(len(nums))]
        right_max = [-1 for i in range(len(nums))]
        left_min = [-1 for i in range(len(nums))]
        right_min = [-1 for i in range(len(nums))]

        # 最大值,往右边可以辐射到哪里,单调递减栈
        stack = []
        for i in range(len(nums) - 1, -1, -1):
            while stack and nums[i] > nums[stack[-1]]:
                stack.pop()
            #当前元素比stack[-1]小,说明从stack[-1]开始已经不是最大值了,右边的边界就是stack[-1]
            if stack and nums[i] <= nums[stack[-1]]:
                right_max[i] = stack[-1]
            else:
                right_max[i] = len(nums)
            stack.append(i)
        # 最大值,往左边可以辐射到哪里,单调递减栈
        stack = []
        for i in range(len(nums)):
            while stack and nums[i] >= nums[stack[-1]]:
                stack.pop()
            # 当前元素比stack[-1]小,说明从stack[-1]开始已经不是最大值了,右边的边界就是stack[-1]
            if stack and nums[i] < nums[stack[-1]]:
                left_max[i] = stack[-1]
            else:
                left_max[i] = -1
            stack.append(i)
        # 最小值,往右边可以辐射到哪里,单调递递增
        stack = []
        for i in range(len(nums) - 1, -1, -1):
            while stack and nums[i] < nums[stack[-1]]:
                stack.pop()
            # 当前元素比stack[-1]大,说明从stack[-1]开始已经不是最小值了,右边的边界就是stack[-1]
            if stack and nums[i] >= nums[stack[-1]]:
                right_min[i] = stack[-1]
            else:
                right_min[i] = len(nums)
            stack.append(i)
        # 最小值,往左边边可以辐射到哪里,单调递增
        stack = []
        for i in range(len(nums)):
            while stack and nums[i] <= nums[stack[-1]]:
                stack.pop()
            # 当前元素比stack[-1]大,说明从stack[-1]开始已经不是最小值了,左边的边界就是stack[-1]
            if stack and nums[i] > nums[stack[-1]]:
                left_min[i] = stack[-1]
            else:
                left_min[i] = -1
            stack.append(i)
        print(left_max)
        print(right_max)
        print(left_min)
        print(right_min)
        ans = 0
        for i in range(len(nums)):
            ans += nums[i] * (i - left_max[i]) * (right_max[i] - i)
            ans -= nums[i] * (i - left_min[i]) * (right_min[i] - i)
        return ans

962. 最大宽度坡

贪心+单调栈

左边界尽可能靠左,右边界尽可能靠右。

左边界尽可能靠左,所以单调递减栈从0下标开始。

右边界尽可能靠右,所以从右往左遍历

 

 

class Solution:
    def maxWidthRamp(self, A: List[int]) -> int:
        stack = []
        n = len(A)
        for i in range(n):
            if not stack or A[stack[-1]] > A[i]:
                stack.append(i)

        res = 0
        i = n - 1
        while i > res:  # 当res大于等于i时没必要继续遍历了 
            while stack and A[stack[-1]] <= A[i]:
                res = max(res, i - stack[-1])
                stack.pop()
            i -= 1

        return res

作者:Elmer
链接:https://leetcode-cn.com/problems/maximum-width-ramp/solution/dan-diao-zhan-python-yi-kan-jiu-dong-by-elmer/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1124. 表现良好的最长时间段

class Solution:
    def longestWPI(self, hours: List[int]) -> int:
        n = len(hours)
        score = [1 if h >8 else -1 for h in hours]
        presum = [0] * (n + 1)
        for i in range(1, n+1):
            presum[i] = presum[i-1] + score[i-1]
        
        stack = []
        for i in range(n+1):
            if not stack or presum[stack[-1]] > presum[i]:
                stack.append(i)
        ans = 0
        i = n
        while i > ans:
            while stack and presum[stack[-1]] < presum[i]:
                ans = max(ans, i - stack.pop())
            i -= 1
        return ans



作者:shzi
链接:https://leetcode-cn.com/problems/longest-well-performing-interval/solution/dan-diao-zhan-by-shzi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
    def longestWPI(self, hours: List[int]) -> int:
        #前缀和求出来当前i以及以前,劳累的天数是不是严格大于不劳累的天数
        #如果不把presum的长度+1,那么要计算j到i之间的天数,应该是presum[i]-presum[j-1],不好处理边界
        #为什么是presum[i]-presum[j-1],而不是presum[i]-presum[j],因为要求的区间包括j,要从j的前一位开始减。
        n = len(hours)
        score = [1 if h >8 else -1 for h in hours]
        presum = [0] * (n + 1)
        for i in range(1, n+1):
            presum[i] = presum[i-1] + score[i-1]
        print(presum)
        #presum[i] - presum[j] > 0,表示「劳累的天数」是严格 大于「不劳累的天数」
        #j小于i且presum[j]小于presum[i],变成了962
        #单调递减栈
        stack=[]
        for i in range(len(presum)):
            if not stack or presum[i] < presum[stack[-1]]:
                stack.append(i)
        print(stack)
        res=0
        for i in range(len(presum)-1,-1,-1):
            while stack and presum[i] > presum[stack[-1]]:
                res=max(res,i-stack[-1])
                stack.pop()
        return res

2334. 元素值大于变化阈值的子数组

 

计算器

力扣

224. 基本计算器

 双栈

class Solution(object):
    def calculate(self, s):
        """
        :type s: str
        :rtype: int
        """
        #去掉空格
        s = s.replace(" ","")
        from collections import deque
        # 运算符号优先级
        hashmap = {'-': 1,
                   '+': 1,
                   '*': 2,
                   '/': 2,
                   '%': 2,
                   '^': 3,
                   }

        # 存放所有数字
        nums = deque([0])  # 为了防止第一个数为负数,先往nums加个0
        # 存放所有的操作
        ops = deque([])

        def calc(nums, ops):
            if not nums or len(nums) < 2 or not ops:
                return
            b, a = nums.pop(), nums.pop()
            op = ops.pop()
            ans = 0
            if op == '+':
                ans = a + b
            elif op == '-':
                ans = a - b
            elif op == '*':
                ans = a * b
            elif op == '/':
                ans = a // b
            elif op == '^':
                ans = int(pow(a, b))
            elif op == '%':
                ans = a % b
            nums.append(ans)

        n = len(s)
        i = 0
        while i < n:
            c = s[i]
            #左括号入栈ops
            if c == '(':
                ops.append(c)
            #右括号计算
            elif c == ')':
                while ops:
                    if ops[-1] != '(':
                        calc(nums, ops)
                    else:
                        ops.pop()
                        break
            else:
                #如果是数字,入栈nums
                if c.isdigit():
                    tmp = c
                    j = i + 1
                    #比如几位连在一起的,“150”
                    while j < n and s[j].isdigit():
                        tmp += s[j]
                        j += 1
                    nums.append(int(tmp))
                    i = j - 1
                else:
                    #处理特殊case,如果当前是,加号或者减号,但是前面不是数字的时候
                    if i > 0 and s[i - 1] in '(+-':
                        nums.append(0)
                    #如果是 加号,或者减号
                    while ops and ops[-1] != '(':
                        #前一位如果小于等于当前的运算优先级,就计算出来。
                        #例子:s="2-1+2"
                        prev = ops[-1]
                        if hashmap[prev] >= hashmap[c]:
                            calc(nums, ops)
                        else:
                            break
                    ops.append(c)
            i += 1

        while ops and ops[-1] != '(':
            calc(nums, ops)
        return nums[-1]

6202. 使用机器人打印字典序最小的字符串

 

class Solution:
    def robotWithString(self, s: str) -> str:
        ans = []
        cnt = Counter(s)
        min = 0  # 剩余最小字母
        st = []
        for c in s:
            cnt[c] -= 1
            while min < 25 and cnt[ascii_lowercase[min]] == 0:
                min += 1
            st.append(c)
            while st and st[-1] <= ascii_lowercase[min]:
                ans.append(st.pop())
        return ''.join(ans)

作者:endlesscheng
链接:https://leetcode.cn/problems/using-a-robot-to-print-the-lexicographically-smallest-string/solution/tan-xin-zhan-by-endlesscheng-ldds/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值