力扣方法总结:查找类

二分查找模板

查找递增数组num中第一个大于等于target的元素下标。
写法采用闭区间写法,即

  • [0, left - 1]始终小于target,即下图中红色区域
  • [right + 1, nums.size() - 1]始终大于等于target,即下图中蓝色区域
  • 遍历结束的条件是left > right
  • 最后left指向的元素(也是right + 1指向的元素)就是第一个大于等于target的元素
    在这里插入图片描述
    在这里插入图片描述
int lower_bound(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;
    // 闭区间
    while (left <= right) {
        int mid = (left + right) / 2;
        if (nums[mid] < target)
            left = mid + 1;
        else right = mid - 1;
    }
    return left;
}

上述模板函数返回第一个大于等于target的元素下标。对于其他三种情况(前提是整数数组):

  • 大于target:等价于大于等于target + 1
  • 小于target:等价于大于等于target的前一个元素
  • 小于等于target:等价于大于等于target + 1的前一个元素

34. 在排序数组中查找元素的第一个和最后一个位置 二分查找 2023/2/7

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

使用上述代码模板轻松求解,start为大于等于target的第一个元素,end为小于等于target的第一个元素。

class Solution {
public:
    // 二分模板
    int lower_bound(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        // 闭区间
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target)
                left = mid + 1;
            else right = mid - 1;
        }
        return left;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int start = lower_bound(nums, target); 
        if (start == nums.size() || nums[start] != target) {
            return {-1, -1};
        }
        int end = lower_bound(nums, target + 1) - 1;
        return {start, end};
    }
};

162. 寻找峰值 类二分查找 2023/2/7

峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
对于所有有效的 i 都有 nums[i] != nums[i + 1]
示例:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。

峰值元素可能不止一个,只需找出一个即可。

每次选取中间的一个元素mid,并与其相邻元素mid + 1比较。

  • 如果元素mid较大,一定有一个峰顶在[left, mid]之间,将右区间置为mid
  • 如果元素mid + 1较大,一定有一个峰顶在[mid + 1, right]之间,将左区间置为mid + 1
  • 当只剩一个元素时,必定为峰顶,跳出循环
class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        // 二分查找写法,峰值一定在闭区间[left, right]内
        int left = 0;
        int right = nums.size() - 1;
        // 当左右区间相等时跳出
        while (left < right) {
            // 类似二分每次消除一半元素
            int mid = (left + right) / 2;
            // 一定有一个峰顶在[mid + 1, right]之间,区间改写
            if (nums[mid] < nums[mid + 1])
                left = mid + 1;
            // 一定有一个峰顶在[left, mid]之间,区间改写
            else right = mid;
        }
        return left;
    }
};

153. 寻找旋转排序数组中的最小值 类二分查找 2023/2/7

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

每次选取中间的一个元素mid,并与其结尾元素right比较。

  • 逐步缩减[left, right]范围,直到找到最小元素
  • 比较前看看left元素是否小于right元素,如果是,则序列已经为升序序列了,左端元素一定为最小元素
  • 如果元素right较大,[mid, right]为升序数组,最小值一定在[left, mid]之间,舍弃右半边元素
  • 如果元素mid较大,[left, mid]为升序数组,最小值一定在[mid + 1, right]之间,舍弃左半边元素
  • 当只剩一个元素时,必定最小元素,跳出循环
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        while (left < right) {
            // 此时整个数组一定为升序数组,最小值即为left
            if (nums[left] < nums[right]) return nums[left];
            int mid = (left + right) / 2;
            // [mid, right]为升序数组,最小值一定在[left, mid]之间,舍弃右半边元素
            if (nums[mid] < nums[right]) {
                right = mid;
            }
            // [left, mid]为升序数组,最小值一定在[mid + 1, right]之间,舍弃左半边元素
            else left = mid + 1;
        }
        return nums[left];
    }
};

33. 搜索旋转排序数组 类二分查找 2023/2/7

整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

本题与上题非常类似,一种方法是先使用上题类二分法找到最小值对应下标,将原数组分解成两个有序数组,接着对两个有序数组进行二分查找即可。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            // [mid, right]有序
            if (nums[mid] < nums[right])
                right = mid;
            // [left, mid]有序
            else left = mid + 1;
        }
        // 在[0, left - 1]和[left, nums.size() - 1]区间分别使用二分
        auto it1 = lower_bound(nums.begin(), nums.begin() + left, target);
        if (it1 != nums.end() && *it1 == target) return it1 - nums.begin();
        auto it2 = lower_bound(nums.begin() + left, nums.end(), target);
        if (it2 != nums.end() && *it2 == target) return it2 - nums.begin();
        return -1;
    }
};

还有一种方法,取完mid后可以得到一个有序区间和一个无序区间,若该有序区间左右端点包括target,直接在该有序区间进行二分查找,否则缩小查找范围。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            // [mid, right]有序
            if (nums[mid] < nums[right]) {
                // 看看该有序区间左右端点是否包括target
                if (nums[mid] <= target && target <= nums[right]) {
                    auto it = lower_bound(nums.begin() + mid, nums.begin() + right, target);
                    return *it == target ? it - nums.begin() : -1;
                }
                else right = mid - 1;
            }
            // [left, mid]有序
            else {
                // 看看该有序区间左右端点是否包括target
                if (nums[left] <= target && target <= nums[mid]) {
                    auto it = lower_bound(nums.begin() + left, nums.begin() + mid, target);
                    return *it == target ? it - nums.begin() : -1;
                }
                else left = mid + 1;
            }
        }
        return nums[left] != target ? -1 : left;
    }
};

436. 寻找右区间 Medium lower_bound方法 2021/11/10

给你一个区间数组 intervals ,其中 intervals[i] = [starti, endi] ,且每个 starti 都 不同 。
区间 i 的 右侧区间 可以记作区间 j ,并满足 startj >= endi ,且 startj 最小化 。
返回一个由每个区间 i 的 右侧区间 在 intervals 中对应下标组成的数组。如果某个区间 i 不存在对应的 右侧区间 ,则下标 i 处的值设为 -1 。
示例:
输入:intervals = [[3,4],[2,3],[1,2]]
输出:[-1,0,1]
解释:对于 [3,4] ,没有满足条件的“右侧”区间。
对于 [2,3] ,区间[3,4]具有最小的“右”起点;
对于 [1,2] ,区间[2,3]具有最小的“右”起点。

利用map容器插入时自动按key排序的性质,使用map容器存储intervals中的左区间值和对应下标,遍历各区间时,使用map容器中的lower_bound方法找到key中首个>=其右区间值的迭代器,其second值即为其下标。

class Solution {
public:
    vector<int> findRightInterval(vector<vector<int>>& intervals) {
        map<int, int> mp_start;
        // 将各个区间的左区间值和其对应下标放到map里
        for (int i = 0; i < intervals.size(); i++)
            mp_start[intervals[i][0]] = i;
        vector <int> res;
        for (int i = 0; i < intervals.size(); i++)
        {
            // 二分查找mp_start中最近的key>=其右区间值,返回该迭代器
            auto p = mp_start.lower_bound(intervals[i][1]);
            if (p == mp_start.end()) res.emplace_back(-1);
            // 其second就是对应下标
            else res.emplace_back(p->second);
        }
        return res;
    }
};

函数补充 map<key, value>::lower_bound 前提必须有序

//  返回一个迭代器,指向键值 >= _Keyval 的第一个元素;
iterator lower_bound(const key_type& _Keyval)
//  返回一个迭代器,指向键值 > _Keyval 的第一个元素;
iterator upper_bound(const key_type& _Keyval)

本题也可以使用vector容器存放pair类型数据,再使用sort函数对其pair的第一个元素排序(自定义排序规则参考“C++提高编程”文件),最后使用lower_bound函数找到最近>=其右区间值的pair。

class Solution {
public:
    vector<int> findRightInterval(vector<vector<int>>& intervals) {
        vector<pair<int, int>> v_start;
        // 将各个区间的左区间值和其对应下标放到vector里
        for (int i = 0; i < intervals.size(); i++)
            v_start.emplace_back(intervals[i][0], i);
        // 将v_start排序(默认按照前一个元素从大到小排序)
        sort(v_start.begin(), v_start.end());
        vector <int> res;
        for (int i = 0; i < intervals.size(); i++)
        {
            // 二分查找v_start中最近的>=其右区间值,返回该迭代器
            auto p = lower_bound(v_start.begin(), v_start.end(), make_pair(intervals[i][1], 0));
            if (p == v_start.end()) res.emplace_back(-1);
            // 其second就是对应下标
            else res.emplace_back(p->second);
        }
        return res;
    }
};

函数补充 lower_bound 前提必须有序
区别于上文中map类中的方法,这里的lower_bound是algorithm头文件中的函数。

//在 [first, last) 区域内查找不小于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);

6356.子字符串异或查询 位运算、多次查询、查询预处理 2023/2/12

给你一个 二进制字符串 s 和一个整数数组 queries ,其中 queries[i] = [firsti, secondi] 。
对于第 i 个查询,找到 s 的 最短子字符串 ,它对应的 十进制值 val 与 firsti 按位异或 得到 secondi ,换言之,val ^ firsti == secondi 。
第 i 个查询的答案是子字符串 [lefti, righti] 的两个端点(下标从 0 开始),如果不存在这样的子字符串,则答案为 [-1, -1] 。如果有多个答案,请你选择 lefti 最小的一个。
请你返回一个数组 ans ,其中 ans[i] = [lefti, righti] 是第 i 个查询的答案。
子字符串 是一个字符串中一段连续非空的字符序列。
示例:
输入:s = “101101”, queries = [[0,5],[1,2]]
输出:[[0,2],[2,3]]
解释:第一个查询,端点为 [0,2] 的子字符串为 “101” ,对应十进制数字 5 ,且 5 ^ 0 = 5 ,所以第一个查询的答案为 [0,2]。第二个查询中,端点为 [2,3] 的子字符串为 “11” ,对应十进制数字 3 ,且 3 ^ 1 = 2 。所以第二个查询的答案为 [2,3] 。

本题首先需要熟悉异或运算的性质:A⊕B=C ⇒ A=B⊕C

最容易想到的是首先对first和second进行异或运算,然后将其转化为字符串,再使用find方法找出当前字符串在二进制字符串中的位置即可。
可使用map进行优化,避免重复元素查找,但这样的做法往往对于多次查询很难通过,即queries比较长时,每次调用一次find时间复杂度较高。

class Solution {
public:
    vector<vector<int>> substringXorQueries(string s, vector<vector<int>>& queries) {
        vector<vector<int>> res(queries.size(), vector<int>());
        unordered_map<int, vector<int>> mm;
        for (int i = 0; i < queries.size(); i++) {
            int num = queries[i][0] ^ queries[i][1];
            if (mm.find(num) != mm.end()) {
                res[i] = mm[num];
                continue;
            }
            string str = convert(num);
            int index = s.find(str);
            if (index != -1) {
                res[i] = {index, int(index + str.size() - 1)};
                mm[num] = res[i];
            }
            else res[i] = {-1, -1};
        }
        return res;
    }
    // 将十进制转化为二进制字符串
    string convert(int num) {
        if (num == 0) return "0";
        string str;
        int i = 0;
        while (num) {
            str = to_string(num % 2) + str;
            num /= 2;
            i++;
        }
        return str;
    }
};

对于这种需要多次查询的问题,一种通用的解决方法是进行预处理,本题中计算以s[i]开始的1位数,2位数… 30位数的值(超过30位的数据其值将大于10^9),并使用unordered_map<int, pair<int, int>>进行记录,每次窗口右滑时,将当前val左移一位并与s[i + len] - '0'进行或运算即可,非常巧妙,如果当前窗口对应的十进制值没有被记录,则存储。
这样每次查询都只有O(1)的时间复杂度。

class Solution {
public:
    vector<vector<int>> substringXorQueries(string s, vector<vector<int>>& queries) {
        int n = s.size();
        vector<vector<int>> ans;
        unordered_map<int, pair<int, int>> mp;    //  <值,<开始位置,结束位置>>

        //  预处理: 计算以s[i]开始的1位数,2位数... 30位数的值
        for (int i = 0; i < n; ++i) {
            if (s[i] == '0') {    //  当前位为0,跳过
                if (!mp.count(0)) mp[0] = pair<int, int>(i, i);
                continue;
            }
            for (int len = 0, val = 0; len < 30 && i + len < n; ++len) {
                val = val << 1 | (s[i + len] - '0'); // 窗口右滑,计算当前窗口二进制值
                if (!mp.count(val))    // 之前没有出现过才进行保存
                    mp[val] = pair<int, int>(i, i + len);
            }
        }
        for (auto& q : queries) {
            int x = q[0] ^ q[1];
            if (!mp.count(x)) ans.push_back({-1, -1});
            else ans.push_back({mp[x].first, mp[x].second});
        }
        return ans;
    }
};

剑指 Offer II 057.值和下标之差都在给定的范围内 滑动窗口 set 2023/3/11

给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
示例:
输入:nums = [1,2,3,1], k = 3, t = 0
输出:true

维持一个长度为k的滑动窗口,并使用数组存储,每次使用std::upper_bound方法找到不小于该元素的下一个元素,并与之比较,并找到前一个元素与之比较。这里使用数组较慢的一点是删除时仍需用std::upper_bound方法或std::equal_range方法找到删除的位置。

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        vector<int> v;
        for (int i = 0; i < nums.size(); i++) {
            auto p = lower_bound(v.begin(), v.end(), nums[i]);
            if (p != v.end())
                if ((long long)*p - nums[i] <= t) return true;
            if (p != v.begin())
                if ((long long)nums[i] - *(p - 1) <= t) return true;
            v.insert(p, nums[i]);
            if (v.size() > k)
                v.erase(lower_bound(v.begin(), v.end(), nums[i - k]));
        }
        return false;
    }
};

使用set有序存储即可解决上述问题,在删除时直接删除该元素即可。但在索引时找到前一个元素不能使用*(p-1)因为set的迭代器不支持,只能使用*next方法。
set自带lower_bound方法,无需指定开始和结束迭代器。但其实也有std::upper_bound方法,两种择一使用即可。

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        set<int> st;
        for (int i = 0; i < nums.size(); i++) {
            auto p = st.lower_bound(nums[i]);
            if (p != st.end())
                if ((long long)*p - nums[i] <= t) return true;
            if (p != st.begin())
                if ((long long)nums[i] - *next(p, -1) <= t) return true;
            st.insert(p, nums[i]);
            if (st.size() > k)
                st.erase(nums[i - k]);
        }
        return false;
    }
};

剑指 Offer II 058.日程表 自定义数据类型 运算符重载 2023/3/11

请实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内没有其他安排,则可以存储这个新的日程安排。
MyCalendar 有一个 book(int start, int end)方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end。
当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生重复预订。
每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。
示例:
输入:
[“MyCalendar”,“book”,“book”,“book”]
[[],[10,20],[15,25],[20,30]]
输出: [null,true,false,true]
解释:
MyCalendar myCalendar = new MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(15, 25); // returns false ,第二个日程安排不能添加到日历中,因为时间 15 已经被第一个日程安排预定了
MyCalendar.book(20, 30); // returns true ,第三个日程安排可以添加到日历中,因为第一个日程安排并不包含时间 20

本题说白了就是有多个左闭右开的区间,设计一种算法来判断加入下一个区间是否满足要求。
使用 set并自定义新数据类型+重载<运算符 可巧妙解决。
在这里插入图片描述
两个区间的重叠方式一共就上述三种,观察到若区间存在重叠,则必定有两个区间的左区间小于另一个区间的右区间,因此重载运算符时使用 e n d < = n . s t a r t end <= n.start end<=n.start
补充:set的元素插入时,将调用重载<运算符来比较元素是否相同,若有 A < B A<B A<B 成立,且有 B < A B<A B<A 成立,则判定为 A = B A=B A=B ,只要有一个满足,则判定两个元素不为相同元素。
插入方法的返回值为一个二元素,第一个元素为插入位置的迭代器,第二个元素为是否插入成功的布尔变量,本题恰好使用第二个元素。

class Node {
public:
    int start, end;
    Node(int _start, int _end): start(_start), end(_end) {}
    bool operator< (const Node& n) const {
        return end <= n.start;
    }
};
class MyCalendar {
public:
    set<Node> st;
    bool book(int start, int end) {
        return st.insert(Node(start, end)).second;
    }
};

剑指 Offer II 070.排序数组中只出现一次的数字 二分查找 位运算 2023/3/11

给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

要满足 O ( l o g n ) O(logn) O(logn) 的时间复杂度只能使用二分查找。数组中每次都是 2 n + 1 2n+1 2n+1 个元素,要判断中间的元素和左/右的元素是否相等来判断出现一次的元素在哪一堆,而且要分奇偶讨论,非常容易错。

class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] == nums[mid + 1]) {
                if ((right - mid) % 2 == 0)
                    left = mid + 2;
                else right = mid - 1;
            }
            else if (nums[mid] == nums[mid - 1]) {
                if ((right - mid) % 2 == 0)
                    right = mid - 2;
                else left = mid + 1;
            }
            else return nums[mid];
        }
        return nums[left];
    }
};

巧用异或运算符 ∧ \wedge 可以完美解决。与1进行异或,如果为 2 k − 1 2k-1 2k1 则会得到 2 k 2k 2k ,如果为 2 k 2k 2k 则会得到 2 k − 1 2k-1 2k1
n u m s [ m i d ] = = n u m s [ m i d ∧ 1 ] nums[mid] == nums[mid \wedge 1] nums[mid]==nums[mid1]说明问题出在右边 l e f t = m i d + 1 left=mid+1 left=mid+1,否则在左边 r i g h t = m i n ( m i d , m i d ∧ 1 ) right=min(mid,mid\wedge1) right=min(mid,mid1)

class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] == nums[mid ^ 1]) {
                left = mid + 1;
            } else {
                right = min(mid, mid ^ 1);
            }
        }
        return nums[left];
    }
};

剑指 Offer II 071.按权重生成随机数 二分查找 前缀和 2023/3/23

给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
也就是说,选取下标 i 的概率为 w[i] / sum(w) 。

首先构建前缀和数组,再从数组里随机挑选一个值,例如 [ 1 , 3 ] [1,3] [1,3] ,则前缀和数组为 [ 0 , 1 , 4 ] [0,1,4] [0,1,4] ,用rand方法比如返回的是2.5,向下取整得到2,再在前缀和数组中找到第一个大于2的元素,其前一个元素就是要找的下标。

class Solution {
public:
    vector<int> preSum;
    Solution(vector<int>& w) {
        preSum = vector<int>(w.size() + 1, 0);
        for (int i = 0; i < w.size(); i++) preSum[i + 1] = w[i] + preSum[i];
        srand(time(NULL));
    }

    int pickIndex() {
        int num = rand() % preSum.back();
        return lower_bound(preSum.begin(), preSum.end(), num + 1) - preSum.begin() - 1;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值