std::unordered_set

本文详细介绍了C++标准库中的unordered_set容器,包括其构造、元素访问、迭代器、容量、修改、查找、桶接口以及哈希策略。通过示例展示了如何初始化、操作和查询unordered_set,同时探讨了桶的数量、元素数量、最大容量以及哈希策略对容器性能的影响。此外,还涵盖了非成员函数如比较和交换操作。

std::unordered_set

介绍

成员函数

非成员函数

介绍

// unordered_set 模板定义
template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator<Key> class unordered_set; (C++11 起)
namespace pmr {
    template <class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
    using unordered_set = std::unordered_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>;(C++17 起)
}
  • std::unordered_set 介绍摘选自 cppreference.com 中文网 std::unordered_set 介绍
  • unordered_set 是含有 Key 类型唯一对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度
  • 在内部,元素并不以任何特别顺序排序,而是组织进桶中
  • 元素被放进哪个桶完全依赖其值的哈希
  • 这允许对单独元素的快速访问,因为哈希一旦确定,就准确指代元素被放入的桶
  • 不可修改容器元素(即使通过非 const 迭代器),因为修改可能更改元素的哈希,并破坏容器

成员函数

构造析构
#include <QCoreApplication>
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_set>

auto Print(const std::string &msg,
           const std::unordered_set<int, std::hash<int>, std::less<int>,
                                    std::allocator<int>> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1; //默认构造
  Print("s1", s1);

  uset_int s2(10); //设置桶数量
  Print("s2", s2);
  uset_int s3(10, std::hash<int>()); //设置桶数量,哈希散列
  Print("s3", s3);
  std::unordered_set<int, std::hash<int>, std::less<int>> s4(
      10, std::hash<int>(), std::less<int>{}); //设置桶数量,哈希散列,比较器
  Print("s4", s4);

  std::unordered_set<int, std::hash<int>, std::less<int>, std::allocator<int>>
      s5(10, std::hash<int>(), std::less<int>(),
         std::allocator<int>()); //设置桶数量,哈希散列,比较器,分配器
  Print("s5", s5);

  uset_int s6(10, std::allocator<int>()); //设置桶数量,分配器
  Print("s6", s6);

  uset_int s7{1, 2, 3, 4, 5, 6}; //初始化列表
  Print("s7", s7);

  uset_int s8(s7.begin(), s7.end()); //迭代器初始化
  Print("s8", s8);

  uset_int s9(s8); //拷贝构造
  Print("s9", s9);
  uset_int s10(s9, std::allocator<int>());
  Print("s10", s10);

  uset_int s11(std::move(s10)); //移动构造
  Print("s11", s11);
  uset_int s12(std::move(s11), std::allocator<int>());
  Print("s12", s12);

  //析构函数默认
  return 0; // a.exec();
}

输出结果:
s1 :
s2 :
s3 :
s4 :
s5 :
s6 :
s7 : 1 2 3 4 5 6
s8 : 1 2 3 4 5 6
s9 : 1 2 3 4 5 6
s10 : 1 2 3 4 5 6
s11 : 1 2 3 4 5 6
s12 : 1 2 3 4 5 6

元素访问
int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  std::cout << "s1 : ";
  for (const auto &val : s1) {
    std::cout << val << "\t";
  }
  std::cout << "\n";

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6

迭代器
int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  uset_int::iterator iter = s1.begin();
  std::cout << "s1 : ";
  for (; iter != s1.end(); ++iter) {
    std::cout << *iter << "\t";
  }
  std::cout << "\n";

  uset_int::const_iterator citer = s1.cbegin();
  std::cout << "s1 : ";
  for (; citer != s1.cend(); ++citer) {
    std::cout << *citer << "\t";
  }
  std::cout << "\n";

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6
s1 : 1 2 3 4 5 6

容量
auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  Print("s1", s1);

  //检查容器是否为空
  std::cout << std::boolalpha << "s1.empty() : " << s1.empty() << std::endl;

  std::cout << "s1.size() : " << s1.size() << std::endl; //返回容纳的元素数
  std::cout << "s1.max_size() : " << s1.max_size()
            << std::endl; //返回可容纳的最大元素数,和平台有关

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6
s1.empty() : false
s1.size() : 6
s1.max_size() : 768614336404564650

修改器
auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  Print("s1", s1);

  s1.clear(); //  清除内容
  Print("s1", s1);
  s1.insert(20); //插入元素或结点
  Print("s1", s1);
  s1.emplace(30); //原位构造元素
  Print("s1", s1);

  s1.emplace_hint(s1.end(), 40); //使用提示原位构造元素
  Print("s1", s1);

  s1.erase(s1.begin()); //擦除元素
  Print("s1", s1);

  uset_int s2;
  s2.swap(s1);
  Print("s1", s1);
  Print("s2", s2);

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6
s1 :
s1 : 20
s1 : 20 30
s1 : 20 30 40
s1 : 30 40
s1 :
s2 : 30 40

查找
auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  Print("s1", s1);

  // 返回匹配特定键的元素数量
  std::cout << "s1.count(1) : " << s1.count(1) << std::endl;
  std::cout << "s1.count(2) : " << s1.count(2) << std::endl;
  std::cout << "s1.count(3) : " << s1.count(3) << std::endl;

  // 寻找带有特定键的元素,返回 key 对应的任意一个值的迭代器
  uset_int::const_iterator iter = s1.find(3);
  if (iter != s1.cend()) {
    std::cout << "s1.find(3) : " << *iter << std::endl;
  } else {
    std::cout << "s1.find(3) : s1.cend()" << std::endl;
  }

  // 返回容器中所有键等于 key 的元素范围
  // 返回容器中所有键等于 key
  // 的元素范围。范围以二个迭代器定义,第一个指向所需范围的首元素,而第二个指向范围的尾后一位元素。
  // 返回含有容器中所有键等价于 x 的元素的范围。此重载只有在有限定标识
  // Hash::is_transparent 与 KeyEqual::is_transparent
  // 均合法并指代类型时才参与重载决议。这假设能用 K 和 Key 类型一起调用这种 Hash
  // 还有 KeyEqual 是通透的,进而允许不用构造 Key 的实例就调用此函数
  std::pair<uset_int::iterator, uset_int::iterator> pa = s1.equal_range(3);
  if (pa.first == s1.end())
    std::cout << "pa.first is empty" << std::endl;
  else
    std::cout << "s1.equal_range(3) first : " << *pa.first << std::endl;
  if (pa.second == s1.end())
    std::cout << "pa.second is empty" << std::endl;
  else
    std::cout << "s1.equal_range(3) second : " << *pa.second << std::endl;

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6
s1.count(1) : 1
s1.count(2) : 1
s1.count(3) : 1
s1.find(3) : 3
s1.equal_range(3) first : 3
s1.equal_range(3) second : 4

非成员函数

auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1{1, 2, 3, 4, 5, 6, 5};
  uset_int s2{1, 2, 3, 4, 5, 6, 5};
  Print("s1", s1);
  Print("s2", s2);

  std::cout << std::boolalpha << "s1 == s2 : " << (s1 == s2) << std::endl;

  uset_int s3;
  std::swap(s3, s2);
  Print("s2", s2);
  Print("s3", s3);

  return 0; // a.exec();
}

输出结果:
s1 : 1 2 3 4 5 6
s2 : 1 2 3 4 5 6
s1 == s2 : true
s2 :
s3 : 1 2 3 4 5 6

桶接口
auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1;
  s1.emplace(1);
  s1.emplace(2);
  s1.emplace(6);
  s1.emplace(9);
  s1.emplace(10);
  Print("s1", s1);

  //返回一个迭代器,指向指定的桶的开始
  for (int i = 0; i < s1.bucket_count(); ++i) {
    auto iter = s1.begin(i);
    if (iter == s1.end(i)) {
      std::cout << "s1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != s1.end(i); ++iter) {
        std::cout << "s1.begin(" << i << ") : " << *iter << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回一个迭代器,指向指定的桶的开始
  for (int i = 0; i < s1.bucket_count(); ++i) {
    auto iter = s1.cbegin(i);
    if (iter == s1.cend(i)) {
      std::cout << "s1.cend(" << i << ")"
                << "\t";
    } else {
      for (; iter != s1.cend(i); ++iter) {
        std::cout << "s1.cbegin(" << i << ") : " << *iter << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回桶数
  std::cout << "s1.bucket_count() : " << s1.bucket_count() << std::endl;
  //返回桶的最大数量
  std::cout << "s1.max_bucket_count() : " << s1.max_bucket_count() << std::endl;
  //返回在特定的桶中的元素数量
  for (int i = 0; i < s1.bucket_count(); ++i) {
    std::cout << "s1.bucket_size(" << i << ") : " << s1.bucket_size(i) << "\t";
  }
  std::cout << "\n";

  //返回带有特定键的桶,返回关键 key 的桶的下标。始终会在此桶中找到关键等于 key
  //的元素(若存在)。返回值仅对 bucket_count() 返回相同值的容器实例合法。
  //若 bucket_count() 为零则行为未定义
  std::cout << "s1.bucket(1) : " << s1.bucket(1) << std::endl;
  std::cout << "s1.bucket(100) : " << s1.bucket(100) << std::endl;

  // 键不存在,若存在会分配当前下标的桶
  std::cout << "s1.bucket(5000) : " << s1.bucket(5000) << std::endl;
  std::cout << "s1.bucket(2) : " << s1.bucket(2) << std::endl;

  return 0; // a.exec();
}

输出结果:
s1 : 9 1 10 2 6
s1.end(0) s1.end(1) s1.end(2) s1.begin(3) : 6 s1.begin(4) : 9 s1.begin(4) : 1 s1.end(5) s1.end(6) s1.begin(7) : 10 s1.begin(7) : 2
s1.cend(0) s1.cend(1) s1.cend(2) s1.cbegin(3) : 6 s1.cbegin(4) : 9 s1.cbegin(4) : 1 s1.cend(5) s1.cend(6) s1.cbegin(7) : 10 s1.cbegin(7) : 2
s1.bucket_count() : 8
s1.max_bucket_count() : 1152921504606846975
s1.bucket_size(0) : 0 s1.bucket_size(1) : 0 s1.bucket_size(2) : 0 s1.bucket_size(3) : 1 s1.bucket_size(4) : 2 s1.bucket_size(5) : 0 s1.bucket_size(6) : 0 s1.bucket_size(7) : 2
s1.bucket(1) : 4
s1.bucket(100) : 1
s1.bucket(5000) : 4
s1.bucket(2) : 7

哈希策略
auto Print(const std::string &msg, const std::unordered_set<int> &lst) {
  std::cout << msg << " : ";
  for (const auto &pa : lst) {
    std::cout << pa << "\t";
  }
  std::cout << "\n";
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  using uset_int = std::unordered_set<int>;
  uset_int s1;
  s1.emplace(1);
  s1.emplace(2);
  s1.emplace(6);
  s1.emplace(9);
  s1.emplace(10);
  Print("s1", s1);

  for (int i = 0; i < s1.bucket_count(); ++i) {
    auto iter = s1.begin(i);
    if (iter == s1.end(i)) {
      std::cout << "s1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != s1.end(i); ++iter) {
        std::cout << "s1.begin(" << i << ") : " << *iter << "\t";
      }
    }
  }
  std::cout << "\n";

  //返回每个桶的平均元素数量
  std::cout << "s1.load_factor() : " << s1.load_factor() << std::endl;

  for (int i = 0; i < s1.bucket_count(); ++i) {
  }

  //管理每个桶的平均元素数量的最大值
  //管理最大加载因子(每个桶的平均元素数)。若加载因子超出此阈值,则容器自动增加桶数
  //返回最大加载因子
  std::cout << "s1.max_load_factor() : " << s1.max_load_factor() << std::endl;
  //设置最大加载因子
  s1.max_load_factor(0.9);
  std::cout << "s1.max_load_factor() : " << s1.max_load_factor() << std::endl;

  //为至少为指定数量的桶预留存储空间并重新生成散列表
  s1.rehash(16);
  for (int i = 0; i < s1.bucket_count(); ++i) {
    auto iter = s1.begin(i);
    if (iter == s1.end(i)) {
      std::cout << "s1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != s1.end(i); ++iter) {
        std::cout << "s1.begin(" << i << ") : " << *iter << "\t";
      }
    }
  }
  std::cout << "\n";

  //为至少为指定数量的元素预留存储空间并重新生成哈希表
  s1.reserve(20);
  for (int i = 0; i < s1.bucket_count(); ++i) {
    auto iter = s1.begin(i);
    if (iter == s1.end(i)) {
      std::cout << "s1.end(" << i << ")"
                << "\t";
    } else {
      for (; iter != s1.end(i); ++iter) {
        std::cout << "s1.begin(" << i << ") : " << *iter << "\t";
      }
    }
  }
  std::cout << "\n";
  return 0; // a.exec();
}

输出结果:
s1 : 9 1 10 2 6
s1.end(0) s1.end(1) s1.end(2) s1.begin(3) : 6 s1.begin(4) : 9 s1.begin(4) : 1 s1.end(5) s1.end(6) s1.begin(7) : 10 s1.begin(7) : 2
s1.load_factor() : 0.625
s1.max_load_factor() : 1
s1.max_load_factor() : 0.9
s1.end(0) s1.end(1) s1.end(2) s1.begin(3) : 6 s1.begin(4) : 1 s1.end(5) s1.end(6) s1.begin(7) : 2 s1.end(8) s1.end(9) s1.end(10) s1.end(11) s1.begin(12) : 9 s1.end(13) s1.end(14) s1.begin(15) : 10
s1.end(0) s1.end(1) s1.end(2) s1.end(3) s1.begin(4) : 1 s1.end(5) s1.end(6) s1.end(7) s1.end(8) s1.end(9) s1.end(10) s1.end(11) s1.begin(12) : 9 s1.end(13) s1.end(14) s1.end(15) s1.end(16) s1.end(17) s1.end(18) s1.begin(19) : 6 s1.end(20) s1.end(21) s1.end(22) s1.begin(23) : 2 s1.end(24) s1.end(25) s1.end(26) s1.end(27) s1.end(28) s1.end(29) s1.end(30) s1.begin(31) : 10

起始

<think>首先,用户的问题是关于C++标准库中的unordered_set容器。他们想了解它的使用介绍。我需要根据系统级指令来构建我的回答。 系统级指令关键点: - 所有行内数学表达式必须用$...$格式。 - 独立公式必须用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考提供的引用,但用户的问题是基于这些引用,所以我需要整合相关信息。 提供的引用是关于unordered_setunordered_map的: - 引用[1]:讨论了unordered_setset的差异,包括模板参数。 - 引用[2]:类似,介绍了unordered_set的声明和模板参数。 - 引用[3]:解释了哈希函数的需求,以及unordered_set基于哈希表实现。 用户的问题:C++ std unordered_set 使用介绍。所以,我需要介绍unordered_set的基本使用、特性、模板参数、成员函数等。 结构我的回答: 1. **引言**:简要介绍unordered_set是什么。 2. **核心特性**:解释它基于哈希表,无序,高效查找等。 3. **模板参数**:根据引用,详细说明模板参数如Key, Hash, Pred, Alloc。 4. **基本使用**:包括如何声明、插入元素、查找元素等。 5. **与set的比较**:基于引用[1],讨论差异。 6. **哈希函数**:基于引用[3],解释哈希函数的重要性。 7. **示例代码**:提供简单代码示例来展示使用。 8. **相关问题**:生成几个相关问题。 确保使用中文回答。 生成相关问题: - 类似示例,如: 1. unordered_set的性能如何分析? 2. unordered_set的优化方法? 3. unordered_set的应用场景? 添加引用标识: - 当引用提供的文本时,在末尾添加[^1]、[^2]或[^3]。 回答结构: - 清晰、逐步:从基础到高级。 - 真实可靠:基于标准C++知识。 数学表达式:由于主题是编程,可能没有太多数学,但如果有,比如讨论时间复杂度,可以用行内数学,如$O(1)$。 现在,草拟回答大纲: 1. **介绍unordered_set**:定义和主要特点。 2. **模板参数详解**:使用引用中的信息。 3. **基本操作**:声明、插入、查找、删除等。 4. **哈希函数自定义**:如果需要自定义哈希。 5. **与set的区别**:无序 vs 有序,性能差异。 6. **代码示例**:简单C++代码。 7. **总结**:适用场景。 8. **相关问题**:列出几个。 最后,生成相关问题。</think>### C++ std::unordered_set 使用介绍 `std::unordered_set` 是 C++ 标准库中的一个无序关联容器,基于哈希表实现。它存储唯一元素(不允许重复),并提供高效的插入、删除和查找操作,平均时间复杂度为 $O(1)$,最坏情况下为 $O(n)$(取决于哈希冲突)。与 `std::set`(基于红黑树)不同,`std::unordered_set` 不保证元素的顺序,这使其在需要快速查找但无需排序的场景中更高效[^1][^3]。 #### 1. **核心特性** - **无序存储**:元素根据哈希值分布,无特定顺序。 - **唯一性**:所有元素必须唯一,重复元素会被忽略。 - **高效操作**:平均 $O(1)$ 的查找、插入和删除性能。 - **基于哈希表**:依赖哈希函数将键映射到存储位置,因此键类型必须支持哈希计算[^3]。 #### 2. **模板参数** `std::unordered_set` 的声明包含多个模板参数,允许自定义行为: ```cpp template < class Key, // 元素类型(如 int, string) class Hash = hash<Key>, // 哈希函数,默认为 std::hash<Key> class Pred = equal_to<Key>, // 键比较谓词,默认为 std::equal_to<Key> class Alloc = allocator<Key> // 分配器类型,默认为 std::allocator<Key> > class unordered_set; ``` - **Key**:存储的元素类型,例如 `int` 或 `std::string`。 - **Hash**:哈希函数对象。如果键类型无默认哈希函数(如自定义类),需自定义[^3]。 - **Pred**:用于比较键是否相等的谓词,默认为 `std::equal_to<Key>`。 - **Alloc**:内存分配器,通常使用默认值[^1][^2]。 #### 3. **基本操作** 以下是常见操作示例(使用 C++11 及以上版本): ```cpp #include <iostream> #include <unordered_set> #include <string> int main() { // 声明一个存储字符串的 unordered_set std::unordered_set<std::string> fruits; // 插入元素 fruits.insert("apple"); fruits.insert("banana"); fruits.insert("orange"); fruits.insert("apple"); // 重复元素,插入失败 // 查找元素 auto search = fruits.find("banana"); if (search != fruits.end()) { std::cout << "Found: " << *search << std::endl; // 输出: Found: banana } // 删除元素 fruits.erase("orange"); // 遍历元素(无序) for (const auto& fruit : fruits) { std::cout << fruit << " "; // 输出可能为: apple banana(顺序不定) } // 检查大小 std::cout << "\nSize: " << fruits.size(); // 输出: Size: 2 return 0; } ``` #### 4. **自定义哈希函数** 如果键类型无默认哈希函数(如自定义类),需提供自定义哈希和比较谓词: ```cpp #include <functional> struct Person { std::string name; int age; bool operator==(const Person& other) const { // 必须定义相等比较 return name == other.name && age == other.age; } }; // 自定义哈希函数 struct PersonHash { size_t operator()(const Person& p) const { return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age); } }; int main() { std::unordered_set<Person, PersonHash> people; people.insert({"Alice", 30}); // 注意:自定义哈希需确保减少冲突[^3]。 } ``` #### 5. **与 std::set 的区别** - **顺序性**:`std::set` 有序(基于红黑树),`std::unordered_set` 无序(基于哈希表)。 - **性能**:查找操作在 `std::set` 中为 $O(\log n)$,在 `std::unordered_set` 中平均为 $O(1)$。 - **内存开销**:`std::unordered_set` 可能因哈希表负载因子而占用更多内存。 - **适用场景**:`std::unordered_set` 适合高频查找,`std::set` 适合需要有序遍历的场景[^1]。 #### 6. **注意事项** - **哈希冲突**:高冲突会导致性能下降,可通过调整负载因子(`max_load_factor()`)优化。 - **键类型要求**:键必须支持哈希和相等比较,否则需自定义[^3]。 - **迭代器稳定性**:插入或删除元素可能使迭代器失效。 #### 7. **适用场景** - 高频查找操作(如缓存系统)。 - 需要快速去重的数据集。 - 不关心元素顺序的集合管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值