算法分析之哈希表

算法相关数据结构总结:

序号数据结构文章
1动态规划动态规划之背包问题——01背包
动态规划之背包问题——完全背包
动态规划之打家劫舍系列问题
动态规划之股票买卖系列问题
动态规划之子序列问题
算法(Java)——动态规划
2数组算法分析之数组问题
3链表算法分析之链表问题
算法(Java)——链表
4二叉树算法分析之二叉树
算法分析之二叉树遍历
算法分析之二叉树常见问题
算法(Java)——二叉树
5哈希表算法分析之哈希表
算法(Java)——HashMap、HashSet、ArrayList
6字符串算法分析之字符串
算法(Java)——字符串String
7栈和队列算法分析之栈和队列
算法(Java)——栈、队列、堆
8贪心算法算法分析之贪心算法
9回溯Java实现回溯算法入门(排列+组合+子集)
Java实现回溯算法进阶(搜索)
10二分查找算法(Java)——二分法查找
11双指针、滑动窗口算法(Java)——双指针
算法分析之滑动窗口类问题

一、哈希表理论基础

1. 哈希表的介绍

哈希表(Hash table,也叫散列表), 是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

哈希表是由 HashTable 实现的,相关的实现有HashMapHashSet

HashMap 和 HashTable 的区别?请看这里Java面试手册——高频问题总结(二)。这里主要介绍HashMap和HashSet的算法实现。

2. 哈希表的实现

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。

HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。

HashMap 是无序的,即不会记录插入的顺序。

HashMap的创建

HashMap<String,String> hm1 = new HashMap<String,String>();

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。

HashSet 允许有 null 值。

HashSet 是无序的,即不会记录插入的顺序。

HashSet的创建

HashSet<String> sites = new HashSet<String>();
3. 哈希表的函数

在使用HashMap时,需要熟练掌握相关函数:

put(); //将键/值对添加到 hashMap 中
putAll();  //将所有键/值对添加到 hashMap 中
remove(); //删除 hashMap 中指定键 key 的映射关系
containsKey(); 	//检查 hashMap 中是否存在指定的 key 对应的映射关系。
containsValue(); //检查 hashMap 中是否存在指定的 value 对应的映射关系。
size(); //计算 hashMap 中键/值对的数量
get(); //获取指定 key 对应对 value
getOrDefault(); // 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
isEmpty(); //判断 hashMap 是否为空
replace(); //替换 hashMap 中是指定的 key 对应的 value。
replaceAll(); //将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。
4. 哈希表的常用方法

(1)统计字符出现的次数

方法一:(使用containsKey判断key是否已经记载过)

for(int i = 0; i<nums.length;i++){
    if(hash.containsKey(nums[i])){
        hash.put(nums[i], hash.get(nums[i])+1);
    }
    else{ // 如果hash表里没有出现,则将该值加入key,value置为1
        hash.put(nums[i], 1);
    }
}

方法二:(使用getOrDefault判断key如果记载过,则返回对应的value,否则返回默认值0)

for(int i = 0; i < s.length(); i++) {
    char ch = s.charAt(i);
    hash.put(ch, hash.getOrDefault(ch, 0) + 1);
}

(2)遍历hash表:

for (Integer i : hash.keySet()) {
           
}           

首先学习一下Map.Entry:

Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对。通过这个集合的迭代器,获得每一个条目(唯一获取方式)的键或值并对值进行更改。Map.Entry中的常用方法如下所示:

(1) Object getKey(): 返回条目的关键字
(2) Object getValue(): 返回条目的值
(3) Object setValue(Object value): 将相关映像中的值改为value,并且返回旧值

Map.Entry是为了更方便的输出map键值对。一般情况下,要输出Map中的key 和 value 是先得到key的集合keySet(),然后再迭代(循环)由每个key得到每个value。values()方法是获取集合中的所有值,不包含键,没有对应关系。而Entry可以一次性获得这两个值。

常用的遍历Map的方法:

第二种和第三种可以直接获取key和value。Map.entrySet迭代器会生成EntryIterator,其返回的实例是一个包含key/value键值对的对象。

  Map<String, String> map = new HashMap<String, String>();  
  map.put("1", "value1");  
  map.put("2", "value2");  
  map.put("3", "value3");  
    
  //第一种:普遍使用,由于二次取值,效率会比第二种和第三种慢一倍
  System.out.println("通过Map.keySet遍历key和value:");  
  for (String key : map.keySet()) {  
   System.out.println("key= "+ key + " and value= " + map.get(key));  
  }  
    
  //第二种  
  System.out.println("通过Map.entrySet使用iterator遍历key和value:");  
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();  
  while (it.hasNext()) {  
   Map.Entry<String, String> entry = it.next();  
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());  
  }  
    
  //第三种:无法在for循环时实现remove等操作  
  System.out.println("通过Map.entrySet遍历key和value");  
  for (Map.Entry<String, String> entry : map.entrySet()) {  
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());  
  }  
  
  //第四种:只能获取values,不能获取key 
  System.out.println("通过Map.values()遍历所有的value,但不能遍历key");  
  for (String v : map.values()) {  
   System.out.println("value= " + v);  
  } 

二、leetcode例题讲解哈希表问题

1. 数组作为哈希表
242. 有效的字母异位词

leetcode题目链接:242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例一:

输入: s = "anagram", t = "nagaram"
输出: true

示例二:

输入: s = "rat", t = "car"
输出: false

解题思路:

方法一:排序

有效的字母异位词就是排序后相等的字符串。

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length()) return false;

        // 排序
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }
}

方法二:数组作为哈希表

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length()) return false;

        // 数组
        int[] res = new int[26];
        for(int i = 0; i < s.length(); i++) {
            res[s.charAt(i) - 'a']++;
        }
        for(int i = 0; i < t.length(); i++) {
            res[t.charAt(i) - 'a']--;
            if(res[t.charAt(i) - 'a'] < 0) {
                return false;
            }
        }
        // for(int i = 0; i< res.length; i++) {
        //     if(res[i] != 0) {
        //         return false;
        //     }
        // }
        return true;
    }
}

方法三:映射作为哈希表

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length()) return false;

        // 哈希表
        HashMap<Character, Integer> hash = new HashMap<Character, Integer>();
        for(int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            hash.put(ch, hash.getOrDefault(ch, 0) + 1);
        }
         for(int i = 0; i < t.length(); i++) {
             char ch = t.charAt(i);
             hash.put(ch, hash.getOrDefault(ch, 0) - 1);
             if(hash.get(ch) < 0) {
                 return false;
             }
         }
         return true;
    }
}
383. 赎金信

leetcode题目链接:383. 赎金信

为了不在赎金信中暴露字迹,从杂志上搜索各个需要的字母,组成单词来表达意思。

给你一个赎金信 (ransomNote) 字符串和一个杂志(magazine)字符串,判断 ransomNote 能不能由 magazines 里面的字符构成。

如果可以构成,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例一:

输入:ransomNote = "a", magazine = "b"
输出:false

示例二:

输入:ransomNote = "aa", magazine = "ab"
输出:false

解题思路:

本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。

  1. 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用。
  2. 第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要。

Java代码实现:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 统计ransomNote和magazine出现的字符次数
        if (ransomNote.length() > magazine.length()) {
            return false;
        }
        int[] cnt = new int[26];
        for (char c : magazine.toCharArray()) {
            cnt[c - 'a']++;
        }
        for (char c : ransomNote.toCharArray()) {
            cnt[c - 'a']--;
            if(cnt[c - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }
}
2. Map(映射)作为哈希表
1. 两数之和

leetcode题目链接:1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例一:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

解题思路

本题使用HashMap来存储target-nums[i] 与 i 的值,然后在遍历过程中,如果发现key已经存在,则输出对应的value。

在这里插入图片描述
Java代码实现:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        HashMap<Integer, Integer> hashmap = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums.length; i++){
            if(hashmap.containsKey(nums[i])){
                res[0] = i;
                res[1] = hashmap.get(nums[i]);
                return res;
            }
            hashmap.put(target - nums[i], i);
        }
        return res;
    }
}
454. 四数相加 II

leetcode题目链接:454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例一:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

解题思路:

这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!

我们可以将四个数组分成两部分,A 和 B 为一组,C 和 D 为另外一组。

对于 A 和 B,我们使用二重循环对它们进行遍历,得到所有 A[i]+B[j] 的值并存入哈希映射中。对于哈希映射中的每个键值对,每个键表示一种 A[i]+B[j],对应的值为 A[i]+B[j] 出现的次数。

对于 C 和 D,我们同样使用二重循环对它们进行遍历。当遍历到 C[k]+D[l] 时,如果 −(C[k]+D[l]) 出现在哈希映射中,那么将 −(C[k]+D[l]) 对应的值累加进答案中。

最终即可得到满足 A[i]+B[j]+C[k]+D[l]=0 的四元组数目。

Java代码实现:

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {

        HashMap<Integer, Integer> hash = new HashMap<Integer, Integer>();
        for(int a : nums1) {
            for(int b : nums2) {
                hash.put(a + b, hash.getOrDefault(a + b, 0) + 1);
            }
        }
        int res = 0;
        for(int c : nums3) {
            for(int d : nums4) {
                if(hash.containsKey(-c-d)) {
                    res += hash.get(-c-d);
                }
            }
        }
        return res;
    }
}
15. 三数之和

leetcode题目链接:15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

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

示例一:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例二:

输入:nums = []
输出:[]

解题思路

这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。

而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。

这道题目使用双指针法 要比哈希法高效一些。

在这里插入图片描述

拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i], b = nums[left], c = nums[right]

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

Java代码实现:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 双指针,计算两数之和的基础上将target设置为-nums[i],然后计算left和right
        List<List<Integer>> res = new ArrayList<>();
        if(nums == null || nums.length <= 2) return res;
        Arrays.sort(nums); // 先排序

        for(int i = 0; i < nums.length - 2; i++){
            if(nums[i] > 0) break; 
            if(i > 0 && nums[i] == nums[i-1]) continue; //去重, 用过一次的数字不能用第二次
            int target = -nums[i];
            int left = i+1, right = nums.length-1;
            while(left < right){
                if(nums[left] + nums[right] == target){
                    res.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right]))); //将数组转化为list,省去add过程
                    // 先进行加减操作
                    left++;
                    right--;
                    // 然后去掉重复的,如[-2, -1, -1, -1, 3, 3, 3]
                    while(left < right && nums[left] == nums[left-1]) left++;
                    while(left < right && nums[right] == nums[right+1]) right--;
                }else if(nums[left] + nums[right] < target){
                    left++;
                }else{
                    right--;
                }
            }
        }
        return res;
    }
}
18. 四数之和

leetcode题目链接:18. 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例一:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例二:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

解题思路:

四数之和,和15.三数之和 是一个思路,都是使用双指针法, 基本解法就是在15.三数之和 的基础上再套一层for循环。

但是有一些细节需要注意,例如: 不要判断nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。

15.三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^ 2),四数之和的时间复杂度是O(n^3) 。

Java代码实现

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        // 参考三数之和,使用双指针,多加一些判断
        // 测试用例中有较大的数,则使用long类型
        List<List<Integer>> res = new ArrayList<>();
        if(nums == null || nums.length <= 3) return res;
        Arrays.sort(nums); // 先排序
        int length = nums.length; 
        for(int i = 0; i < nums.length - 3; i++){
            if(i > 0 && nums[i] == nums[i-1]) continue; //去重, 用过一次的数字不能用第二次
            if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) { // 和超过则直接退出循环
                break;
            }
            if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }
            // 三数之和时,只要确定一个i,就可以使用双指针找target-nums[i]
            // 四数之和时,需要确定两个数,故多加一层循环j,然后双指针找另外两个数
            for (int j = i + 1; j < length - 2; j++) {
                // 和上一层循环一样的判断条件
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }
                if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }
                // 双指针查找
                int left = j + 1, right = length - 1;
                while (left < right) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target) {
                        res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        // 去掉重复的后,加加减减
                        while (left < right && nums[left] == nums[left + 1]) {
                            left++;
                        }
                        left++;
                        while (left < right && nums[right] == nums[right - 1]) {
                            right--;
                        }
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        right--;
                    }
                }
            }
        }
        return res;
    }
}
3. Set(集合)作为哈希表
349. 两个数组的交集

leetcode题目链接:349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例一:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例二:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

解题思路:

题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序。

使用set1遍历数组1,然后遍历数组2,将相同元素加入set2,最后将set2的结果输出到数组。

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
            return new int[0];
        }
        Set<Integer> hash1 = new HashSet<>();
        Set<Integer> hash2 = new HashSet<>();
        for(int i : nums1) {
            hash1.add(i);
        }
        for(int i : nums2) {
            if(hash1.contains(i)) {
                hash2.add(i);
            }
        }
        int[] res = new int[hash2.size()];
        int index = 0;
        for(int i : hash2) {
            res[index++] = i;
        }
        return res;
    }
}
202. 快乐数

leetcode题目链接:202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果 可以变为  1,那么这个数就是快乐数。

如果 n 是快乐数就返回 true ;不是,则返回 false 。

示例一:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例二:

输入:n = 2
输出:false

解题思路

方法一:哈希表HashSet

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。

所以这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> res = new HashSet<>();
        // 判断数字是否为1,或者是否该数字重复出现即无限循环
        while(n != 1 && !res.contains(n)) {
            res.add(n);
            n = getNextNumber(n);
        }
        return n == 1;
    }
    // 重组的各个位置的平方和的数字
    public int getNextNumber(int n) {
        int ans = 0;
        while(n > 0) {
            int tmp = n % 10;
            ans += tmp * tmp;
            n = n / 10;
        }
        return ans;
    }
}

方法二:快慢指针法

通过反复调用 getNext(n) 得到的链是一个隐式的链表。隐式意味着我们没有实际的链表节点和指针,但数据仍然形成链表结构。起始数字是链表的头 “节点”,链中的所有其他数字都是节点。next 指针是通过调用 getNext(n) 函数获得。

意识到我们实际有个链表,那么这个问题就可以转换为检测一个链表是否有环。

在这里插入图片描述
我们不是只跟踪链表中的一个值,而是跟踪两个值,称为快跑者和慢跑者。在算法的每一步中,慢速在链表中前进 1 个节点,快跑者前进 2 个节点(对 getNext(n) 函数的嵌套调用)。

如果 n 是一个快乐数,即没有循环,那么快跑者最终会比慢跑者先到达数字 1。

如果 n 不是一个快乐的数字,那么最终快跑者和慢跑者将在同一个数字上相遇。

与双指针判断循环链表是否有环相同的方法:算法分析之链表问题:142. 环形链表Ⅱ

Java代码实现:

class Solution {
    public boolean isHappy(int n) {

        // 快慢双指针,判断是否有环
        int slow = n;
        int fast = getNextNumber(n);
        while(fast != 1 && slow != fast) {
            slow = getNextNumber(slow);
            fast = getNextNumber(getNextNumber(fast));
        }
        return fast == 1;
    } 
    // 重组的各个位置的平方和的数字
    public int getNextNumber(int n) {
        int ans = 0;
        while(n > 0) {
            int tmp = n % 10;
            ans += tmp * tmp;
            n = n / 10;
        }
        return ans;
    }
}

三、其它算法分析

1. 动态规划之背包问题——01背包

动态规划之背包问题——01背包

2. 动态规划之背包问题——完全背包

动态规划之背包问题——完全背包

3. 动态规划之子序列问题

动态规划之子序列问题

4. 算法分析之数组问题

算法分析之数组问题

5. 算法分析之链表问题

算法分析之链表问题

6. 算法分析之哈希表

算法分析之哈希表

7. 算法分析之字符串

算法分析之字符串

参考:

算法(Java)——HashMap、HashSet、ArrayList

代码随想录:哈希表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值