查找算法2

对撞指针


对撞指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。

换言之,双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。将指向最左侧的索引定义为左指针(left),最右侧的定义为右指针(right),然后从两头向中间进行数组遍历。

两数之和

题目描述:
给出一个整型数组 nums,返回这个数组中两个数字的索引值 i 和 j,使得 nums[i] + nums[j] 等于一个给定的 target 值,两个索引不能相等。

示例:
nums= [2,7,11,15],target=9 
返回[0,1]

解题思路:

  • 暴力法

    时间复杂度为 O ( n 2 ) O(n^2) O(n2),第一遍遍历数组,第二遍遍历当前遍历值之后的元素,其和等于target则return。

    代码:

    class Solution:
    	def twoSum(self, nums: List[int], target: int) -> List[int]:
        	for i in range(len(nums)):
            	for j in range(i+1,len(nums)):
                	if nums[i]+nums[j] == target:
                    	return i,j
    
  • 排序+指针对撞

    时间复杂度为 ( O ( n ) + O ( n l o g n ) = O ( n ) ) (O(n)+O(nlogn)=O(n)) (O(n)+O(nlogn)=O(n)),先对 nums 进行备份, 然后对 nums 进行排序,然后利用对撞指针(类似于二分法)找到在排序后数组中和等于 target 的两个数,然后再在 nums 的备份中找到这两个元素对应的位置。

    代码:

    class Solution:
    	def twoSum(self, nums: List[int], target: int) -> List[int]:
        	# 备份
        	nums_copy = nums.copy()
        	# 排序
        	nums.sort()
        	# 利用对撞指针查找
        	l,r = 0,len(nums)-1
        	for i in range(len(nums)):
            	if nums[l] + nums[r] == target:
                	break
            	elif nums[l] + nums[r] > target:
                	r -= 1
            	elif nums[l] + nums[r] < target:
                	l += 1
        	res = []
        	same = True
        	# 查找nums[l]和nums[r]在原数组的位置
        	for i in range(len(nums)):
            	if same and nums_copy[i] == nums[l]:
                	same = False
                	res.append(i)
            	elif nums_copy[i] == nums[r]:
                	res.append(i)
        	return res 
    

    此外也可以利用 list(enumerate(nums)) 实现下标和值的绑定。

    class Solution:
    	def twoSum(self, nums: List[int], target: int) -> List[int]:
        	nums = list(enumerate(nums))
        	nums.sort(key = lambda x:x[1])
        	i,j = 0,len(nums)-1
        	while i < j:
            	if nums[i][1] + nums[j][1] > target:
                	j -= 1
            	elif nums[i][1] + nums[j][1] < target:
                	i += 1
            	else:
                	return min(nums[i][0],nums[j][0]),max(nums[i][0],nums[j][0])
    
  • 查找表:

    时间复杂度为 O ( n ) O(n) O(n),在遍历数组过程中,当遍历到元素 v 时,可以只看 v 前面的元素,是否含有 target-v 的元素存在:

    • 如果查找成功,就返回解
    • 如果没有查找成功,就把 v 放在查找表中,继续查找下一个解

    代码:

    class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = {}
        for i in range(len(nums)):
            t = target - nums[i]
            # 查找 target - nums[i] 是否已经存在
            if record.get(t) is not None:
                return record[t],i
            else:
                record[nums[i]] = i
    

三数之和

题目描述:

给出一个整型数组,寻找其中的所有不同的三元组 (a,b,c),使得a+b+c=0

注意:答案中不可以包含重复的三元组。

示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

解题思路:

  • 对于数组长度 n,如果 n 小于 3,则返回空
  • 对数组按照从小到大的顺序进行排序,遍历排序后数组:
    • 若 nums[i]>0,因为已经排序好,所以后面不可能有三个数加和等于 0,返回结果
    • 对于重复元素:跳过,避免出现重复解
      例如 [-1,-1,0,1],如果不跳过的话会输出两个值:nums[0],nums[2],nums[3] 和 nums[1],nums[2],nums[3]. 但两者都是[-1,0,1],与题目要求不符
    • 令左指针 l=i+1,右指针 r=n-1,当 l<r 时,执行循环:
      • 当 nums[i]+nums[l]+nums[r] = 0 时,执行循环,判断左界 nums[l] 和右界 nums[r] 是否和下一位置重复,去除重复解,将 l,r 移到下一位置,寻找新的解
      • 若和大于 0,说明 nums[r] 太大,r 左移
      • 若和小于 0,说明 nums[l] 太小,l 右移

代码:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        res = []
        if n < 3:
            return res 
        nums.sort()
        for i in range(n):
            if nums[i] > 0:
                return res
            if i > 0 and nums[i] == nums[i-1]:
                continue
            l,r = i+1,n-1
            while l < r:
                if nums[l] + nums[i] + nums[r] == 0:
                    res.append([nums[i],nums[l],nums[r]])
                    while l<r and nums[l] == nums[l+1]:
                        l = l+1
                    while l<r and nums[r] == nums[r-1]:
                        r = r-1
                    l = l+1
                    r = r-1
                elif nums[l] + nums[i] + nums[r] > 0:
                    r = r-1
                else:
                    l = l+1
        return res     

对撞指针模板:

# 对撞指针套路
l,r = 0, len(nums)-1
while l < r:
    if nums[l] + nums[r] == target:
        return nums[l],nums[r]
    elif nums[l] + nums[r] < target:
        l += 1
    else:
        r -= 1

处理重复值的模板:

# 1.
for i in range(len(nums)):
    if i > 0 and nums[i] == nums[i-1]: continue
# 2.
while l < r:
    while l < r and nums[l] == nums[l-1]: l += 1

四数之和

题目描述:

给出一个整形数组,寻找其中的所有不同的四元组(a,b,c,d),使得a+b+c+d等于一个给定的数字target。

示例:
nums = [1, 0, -1, 0, -2, 2],target = 0
结果为:
[[-1,  0, 0, 1],[-2, -1, 1, 2],[-2,  0, 0, 2]]

解题思路:

  • 设指针从左至右依次为 p,k,i,j,如果 nums[p] + 3 * nums[p + 1] > target,因为 nums 按升序排列,所以之后的数肯定都大于 target ,直接 break

  • 如果 nums[p] + 3 * nums[-1] < target,那么当前的 nums[p] 加其余三个数一定小于 target,故 p 直接下一位即可,continue

  • k 和 p 判断完全一样,只是将 3 变成了 2,target 变成了 target - nums[p]

  • 同样地,为了避免结果重复,某个指针遇到相同的数需要直接跳过,这与三数之和是一样的

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        n = len(nums)
        nums.sort() 
        res = []
        p = 0
        while p < n-3:
            if nums[p] + 3*nums[p+1] > target: # p太大,直接break
                break
            if nums[p] + 3*nums[-1] < target: # p太小
                while p < n - 4 and nums[p] == nums[p+1]: # 处理重复情形,避免p = n-3 越界
                    p += 1
                p += 1
                continue
            # 对于固定的p,讨论k
            k = p+1
            while k < n-2:
                if nums[p] + nums[k] + 2*nums[k+1] > target: # k太大,直接break
                    break
                if nums[p] + nums[k] + 2*nums[-1] < target: # k太小
                    while k < n-3 and nums[k] == nums[k+1]:
                        k += 1
                    k += 1
                    continue
                # 对于固定的p,k,讨论i,j
                new_target = target - nums[p] - nums[k]
                i = k+1
                j = n-1
                while i < j:
                    if nums[i] + nums[j] > new_target: # j太大
                        j -= 1
                    elif nums[i] + nums[j] < new_target: # i太小
                        i += 1
                    else:
                        res.append([nums[p],nums[k],nums[i],nums[j]])
                        i += 1
                        j -= 1
                        while i < j and nums[i] == nums[i-1]:
                            i += 1
                        while i < j and nums[j] == nums[j+1]:
                            j -= 1
                while k < n-3 and nums[k] == nums[k+1]:
                    k += 1
                k += 1
            while p < n-4 and nums[p] == nums[p+1]:
                p += 1
            p += 1
        return res 

最接近的三数之和

题目描述:

给出一个整形数组,寻找其中的三个元素 a,b,c,使得 a+b+c 的值最接近另外一个给定的数字target。

示例:
nums = [-1,2,1,-4], target = 1.
与 target 最接近的三个数的和为 2. 
(-1 + 2 + 1 = 2).

解题思路:

开始时可以随机设定一个三个数的和为结果值,在每次比较中,先判断三个数的和是否和target相等,如果相等直接返回和。如果不相等,则判断三个数的和与target的差是否小于这个结果值时,如果小于则进行则进行替换,并保存和的结果值。

代码:

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        n =len(nums)
        nums.sort()
        # 初始化res和diff
        res = nums[0]+nums[1]+nums[2]
        diff = abs(res-target)
        for i in range(n):
            t = target-nums[i]
            # 对于固定的i,讨论j,k
            j,k = i+1,n-1
            while j < k:
                if nums[j] + nums[k] == t:
                    return target
                else:
                    # 更新res和diff
                    if abs(t-nums[j]-nums[k]) < diff:
                        diff = abs(t-nums[j]-nums[k])
                        res = nums[i] + nums[j] + nums[k]
                    if nums[j] + nums[k] > t: # k太大
                        k -= 1
                    elif nums[j] + nums[k] <t: # j太小
                        j += 1
        return res 

查找表


四数相加 II

题目描述:

给出四个整形数组 A,B,C,D,寻找有多少 i,j,k,l 的组合,使得 A[i]+B[j]+C[k]+D[l]=0. 其中 A,B,C,D 中均含有相同的元素个数N,且0<=N<=500.

示例:
输入:
A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2]
输出:2

解题思路:

先遍历 A,B 两个数组,并记录 A[i]+B[j] 的值以及该值出现的次数,然后再遍历 C,D 两个数组,并在 A[i]+B[j] 的结果中寻找等于 0-C[k]-D[l] 的记录,并将该值对应的次数相加。

代码:

class Solution:
    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
        from collections import Counter
        record = Counter()
        res = 0
        n = len(A)
        for i in range(n):
            for j in range(n):
                record[A[i]+B[j]] += 1
        for k in range(n):
            for l in range(n):
                find = 0-C[k]-D[l]
                if record.get(find):
                    res += record.get(find)
        return res

更简洁的写法:

class Solution:
    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
        from collections import Counter
        record = Counter([a+b for a in A for b in B])
        return sum(record.get(- c - d,0) for c in C for d in D)
        

字母异位词分组

题目描述:

给出一个字符串数组,将其中所有可以通过颠倒字符顺序产生相同结果的单词进行分组。

示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:[["ate","eat","tea"],["nat","tan"],["bat"]]

说明:
所有输入均为小写字母。
不考虑答案输出的顺序。

解题思路:

如果将字符串统一排序,异位词排序后的字符串,显然都是相同的。因此可以把排序后的字符串当作 key,把异位词当作 value,对字典进行赋值,进而遍历字典的value,得到结果 list。

代码:

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        from collections import defaultdict
        dic = defaultdict(list)
        # 指定字典value的值的类型
        res = []
        for i in strs:
            key = ''.join(sorted(list(i)))
            dic[key] += i.split(',')
            # str.split()把字符串转换为list
        for v in dic.values():
            res.append(v)
        return res

回旋镖的数量

题目描述:

给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等需要考虑元组的顺序)。

找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。

输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个结果为: [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]

解题思路:

  • 构造查找表,取点之间的距离为 key,value 为距离等于 key 的个数
  • 因为要求 i,j 之间的距离等于 i,k 之间的距离,所以 j,k 的选择不唯一
  • 当距离为 x 的值有 n 个时,选择 j,k 的可能情况有:j 的选择有 n 种,k 的选择有 n-1 种,因此共有 n(n-1) 种可能
  • 对于距离值的计算,按照欧式距离的方法进行计算的话,容易产生浮点数,可以将根号去掉,用差的平方和来进行比较距离

代码:

class Solution:
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        res = 0
        from collections import Counter
        for i in points:
            # 固定i,讨论 j和k
            record = Counter()
            for j in points:
                d = (i[0]-j[0])**2+(i[1]-j[1])**2
                record[d] += 1
            for v in record.values():
                res += v*(v-1)
        return res 

更简洁的写法:

class Solution:
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        from collections import Counter
        def f(i):
            # 对一个i下j,k的距离值求和
            d = Counter((i[0]-j[0]) ** 2 + (i[1]-j[1]) ** 2 for j in points)
            return sum(v*(v-1) for v in d.values())
        # 对每个i的距离进行求和
        return sum(f(i) for i in points)

直线上最多的点数

题目描述:

给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上

示例1:
输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
|        o
|     o
|  o  
+------------->
0  1  2  3  4

示例2:
输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
|  o
|     o        o
|        o
|  o        o
+------------------->
0  1  2  3  4  5  6

解题思路:

  • 判断点 i,j,k 是否在一条直线上,等价于判断 i,j 两点的斜率是否等于 i,k 两点的斜率
  • 遍历数组,查找过 i 点且斜率 (key) 相同的点的个数 (value)
  • 在遍历数组对每个 i 查找过 i 且具有相同斜率的点之后,取查找表中的最大值
  • 如果过 i 且斜率相同的点有 k 个,那么有 k+1 个点在同一条直线上

代码:

class Solution:
    def maxPoints(self,points):
        if len(points) <= 1:
            return len(points)
        res = 0
        from collections import defaultdict 
        for i in range(len(points)):
            record = defaultdict(int)
            samepoint = 0
            for j in range(len(points)):
                if points[i] == points[j]:
                    samepoint += 1
                else:
                    record[self.get_Slope(points,i,j)] += 1
            for v in record.values():
                res = max(res, v+samepoint)
            res = max(res, samepoint)
        return res
    # 求最大公约数
    def gcd(x, y):
        if y == 0:
            return x
        else:
            return gcd(y, x % y)
    # 计算斜率
    def get_Slope(self,points,i,j):
        if points[i][1] - points[j][1] == 0:
            return 'Inf'
        else:
            g = gcd((points[i][1] - points[j][1]),(points[i][0] - points[j][0]))
            if g != 0:
                k = str((points[i][1] - points[j][1])/g)+'/'+str((points[i][0] - points[j][0])/g)
            else:
                k = str((points[i][1] - points[j][1]))+'/'+str((points[i][0] - points[j][0]))
            return k

滑动数组


滑动数组可以想象成显示屏,每次只显示有限个的数字,用完(显示完)后就向后移动一位,显示的数量不变。每次只储存数组中的几个元素,以起到压缩,节省存储空间的作用。

存在重复元素 II

题目描述:

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值至多为 k.

示例1:
输入: nums = [1,2,3,1], k = 3
输出: true

示例 2:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false

解题思路:

利用滑动数组,对于数组中的元素 i 判断其后面的 k 个元素中是否存在等于 i 的元素。

代码:

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        n = len(nums)
        if n == 0 or k == 0:
            return False
        for i in range(0,n):
            if nums[i] in nums[i+1:min(i+k+1,n)]:
                return True
        return False

使用 set,固定滑动数组的长度为 k+1,当这个滑动数组内如果能找到两个元素的值相等,就可以保证两个元素的索引的差小于等于 k. 如果当前的滑动数组中没有元素相同,就右移滑动数组的右边界 r ,同时将左边界 l 右移。

代码:

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        record = set()
        for i in range(len(nums)):
            if nums[i] in record:
                return True
            record.add(nums[i])
            if len(record) == k+1:
                record.remove(nums[i-k])
        return False

存在重复元素 III

题目描述:

在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ. 如果存在则返回 true,不存在返回 false.

示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

解题思路:

和上题类似,使用循环。

代码:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        n = len(nums)
        for i in range(n):
            for j in range(i+1,min(n,i+1+k)):
                if abs(nums[i]-nums[j]) <= t:
                    return True
        return False

另一种写法:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        if t == 0 and len(nums) == len(set(nums)):
            return False
        for i in range(len(nums)):
            for j in range(1,k+1):
                if i+j > len(nums)-1:
                    break
                if abs(nums[i]-nums[i+j]) <= t:
                    return True
        return False

固定滑动窗口的长度为 k+1,判断滑动窗口内是否存在两个数 v 和 w,满足 a b s ( v − w ) ≤ t abs(v-w) \le t abs(vw)t. 对于给定元素 v,该条件等价于在滑动窗口内是否存在元素 w 满足 v − t ≤ w ≤ v + t v-t \le w \le v+t vtwv+t. 因此只需要找到滑动窗口内大于等于v-t 的最小元素,并判断该元素是否小于等于 v+t 即可。

代码:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums, k, t) -> bool:
        record = set()
        for i in range(len(nums)):
            if len(record) != 0:
                rec = list(record)
                # 查找rec中大于等于nums[i]-t的最小元素的索引
                find_index = self.lower_bound(rec,nums[i]-t)
                if find_index != -1 and rec[find_index] <= nums[i] + t:
                # 如果rec中大于等于nums[i]-t的最小元素(记为w) < num[i]+t
                # 那么,nums[i]-t <= w <= nums[i]+t,即 abs(nums[i]-w)<=t
                    return True
            record.add(nums[i])
            if len(record) == k + 1:
                record.remove(nums[i - k])
        return False
    # 利用二分法查找nums中大于等于target的最小元素的索引
    def lower_bound(self, nums, target):
        low, high = 0, len(nums)-1
        while low<high:
            mid = int((low+high)/2)
            if nums[mid] < target:
                low = mid+1
            else:
                high = mid
        return low if nums[low] >= target else -1

二分查找


模板:

class Solution:
    def BinarySearch(self, arr):
        l, h = 0, len(arr)-1
        while l < h:
            mid = (l+h) // 2 # 整除
            if f(x):
                l = mid + 1
            else:
                h = mid
        return l

搜索插入位置

题目描述:

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

示例 1:
输入: [1,3,5,6], 5
输出: 2

示例 2:
输入: [1,3,5,6], 2
输出: 1

示例 3:
输入: [1,3,5,6], 7
输出: 4

解题思路:

需要注意的是 h 要设置为 len(nums),而不是 len(nums)-1,因为可能会出现插入的位置在数组结尾(如示例 3)的情况,需要 l 能取到 len(nums)-1.

代码:

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:        
        l, h = 0, len(nums)
        while l < h:
            mid = (l + h) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                l = mid + 1
            else:
                h = mid
        return l

有序数组中的单一元素

题目描述:

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:
输入: [3,3,7,7,10,11,11]
输出: 10

解题思路:

  • 因为每个元素都会出现两次,唯有一个数只会出现一次,所以 len(nums) 一定是奇数
  • 如果mid是偶数,那么和 1 异或得到 mid+1,如果 mid 和 1 异或得到的是 mid-1
  • 若 nums[mid] 和 nums[mid ^ 1] 相等,那么唯一的元素在 nums[mid] 的右半部分 (l 右移),若 nums[mid] 和 nums[mid ^ 1] 不相等,那么唯一的元素在 nums[mid] 的左半部分 (h 左移)

代码:

class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        l,h = 0,len(nums)-1
        while l < h:
            mid = (h+l)//2
            if nums[mid] == nums[mid^1]:
                l = mid + 1
            else:
                h = mid 
        return nums[l] 

分割数组的最大值

题目描述:

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

数组长度 n 满足以下条件:

  • 1 ≤ n ≤ 1000
  • 1 ≤ m ≤ min(50, n)
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18

解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

解题思路:

二分法+贪心算法:假设 x 为连续子数组之和的最大值,即划分的每一个子数组之和都小于等于 x,若超过 x 则把当前值划分到新的序列。然后利用二分法求出满足划分组数小于等于 m 条件的最小 x.

代码:

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        def check(x): # 判断以x为上界将nums划分的组数是否<=m
            cut = 1
            sums = 0
            for i in nums:
                if sums+i > x:
                    sums = i
                    cut += 1
                else:
                    sums += i 
            return cut <= m
        # 二分法
        left = max(nums)
        right = sum(nums)
        while left < right:
            mid = (left + right) // 2
            if check(mid): 
                right = mid
            else:
                left = mid+1
        return left 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值