C++语法知识点合集:10.stack和queue


一、stack的介绍和使用

1.stack的介绍

C++ 中的 stack 是一种数据结构,位于标准模板库(STL)中。它遵循后进先出(LIFO, Last In First Out)的原则,也就是说,最后插入的元素最先被移除。
主要特点:

  • LIFO 结构:最后插入的元素最先被取出。
  • 限制访问方式:只能访问栈顶元素,无法直接访问其他元素。

2.stack的使用

  1. stack() 构造栈
#include <iostream>
#include <stack>  // 引入stack容器适配器
#include <vector> // 引入vector容器
#include <deque>  // 引入deque容器
// deque 动态数组的块链表。其设计结合了数组和链表的优点,以便在两端都能高效地进行插入和删除操作,同时仍保持良好的随机访问性能。
int main()
{
    std::deque<int> mydeque(3, 100);   // 用3个元素100初始化一个deque
    std::vector<int> myvector(2, 200); // 用2个元素200初始化一个vector

    std::stack<int> first;           // 创建一个空的stack,底层容器默认为deque
    std::stack<int> second(mydeque); // 用mydeque初始化一个stack,底层容器为deque

    std::stack<int, std::vector<int>> third;            // 创建一个空的stack,指定底层容器为vector
    std::stack<int, std::vector<int>> fourth(myvector); // 用myvector初始化一个stack,底层容器为vector

    std::cout << "size of first: " << first.size() << '\n';   // 输出first栈的大小
    std::cout << "size of second: " << second.size() << '\n'; // 输出second栈的大小
    std::cout << "size of third: " << third.size() << '\n';   // 输出third栈的大小
    std::cout << "size of fourth: " << fourth.size() << '\n'; // 输出fourth栈的大小

    return 0;
}

  1. empty() 检测stack是否为空
#include <iostream>
#include <stack>

int main()
{
    std::stack<int> mystack;
    int sum(0);

    for (int i = 1; i <= 10; i++)
        mystack.push(i); // 使用循环将1到10的整数压入mystack中

    while (!mystack.empty()) // 当栈不为空时,继续循环
    {
        sum += mystack.top(); // 将栈顶元素加到sum中
        mystack.pop();        // 移除栈顶元素
    }

    std::cout << "total: " << sum << '\n';

    return 0;
}
  1. size() 返回stack中元素的个数
#include <iostream>
#include <stack>

int main()
{
    std::stack<int> myints;                            // 创建一个空的stack对象myints,存储int类型的元素
    std::cout << "0. size: " << myints.size() << '\n'; // 输出当前栈的大小,最初为0,因为栈是空的

    for (int i = 0; i < 5; i++)
        myints.push(i);                                // 使用循环将0到4的整数压入栈中,总共压入5个元素
    std::cout << "1. size: " << myints.size() << '\n'; // 输出栈的大小,此时栈中有5个元素

    myints.pop();                                      // 弹出栈顶的元素,此时栈顶元素是4
    std::cout << "2. size: " << myints.size() << '\n'; // 输出栈的大小,现在栈中剩下4个元素

    return 0;
}
  1. top() 返回栈顶元素的引用
#include <iostream>
#include <stack>

int main()
{
    std::stack<int> mystack; // 创建一个空的stack对象mystack,存储int类型的元素

    mystack.push(10);
    mystack.push(20);

    mystack.top() -= 5; // 访问栈顶元素,将栈顶元素减去5,原来的栈顶元素20变为15

    std::cout << "mystack.top() is now " << mystack.top() << '\n'; // 输出当前栈顶元素的值,现在是15

    return 0;
}
  1. push() 将元素val压入stack中 pop() 将stack中尾部的元素弹出
#include <iostream>
#include <stack>

int main()
{
    std::stack<int> mystack;

    for (int i = 0; i < 5; ++i) // 使用循环将0到4的整数压入栈中,总共压入5个元素
        mystack.push(i);

    std::cout << "Popping out elements...";
    while (!mystack.empty()) // 当栈不为空时,持续循环
    {
        std::cout << ' ' << mystack.top(); // 输出栈顶元素的值
        mystack.pop();                     // 弹出栈顶元素
    }
    std::cout << '\n'; // 输出换行符,结束输出

    return 0;
}

3.stack的模拟实现

从栈的接口中可以看出,栈实际是一种特殊的vector,因此使用vector完全可以模拟实现stack

#include <vector> // 引入标准库中的 vector 容器,用来实现栈的存储结构

namespace stimulate // 定义命名空间 stimulate,避免名称冲突
{
    // 定义模板类 stack,模板参数为 T,T 表示栈中存储的数据类型
    template <class T>
    class stack
    {
    public:
        stack() {} // 默认构造函数,什么也不做,初始化一个空栈

        // push 函数,接收一个常量引用的元素 x,并将其压入栈顶
        void push(const T &x)
        {
            _c.push_back(x); // 使用 vector 的 push_back 方法将元素添加到容器末尾
        }

        // pop 函数,用于将栈顶元素弹出
        void pop()
        {
            _c.pop_back(); // 使用 vector 的 pop_back 方法移除最后一个元素(即栈顶元素)
        }

        // top 函数,返回栈顶元素的引用
        T &top()
        {
            return _c.back(); // 使用 vector 的 back 方法返回最后一个元素的引用(即栈顶元素)
        }

        // const 版本的 top 函数,返回栈顶元素的常量引用,用于不可修改栈的情况
        const T &top() const
        {
            return _c.back(); // 使用 vector 的 back 方法返回最后一个元素的常量引用
        }

        // size 函数,返回栈的大小(元素个数)
        size_t size() const
        {
            return _c.size(); // 使用 vector 的 size 方法返回元素的个数
        }

        // empty 函数,判断栈是否为空,返回一个布尔值
        bool empty() const
        {
            return _c.empty(); // 使用 vector 的 empty 方法判断容器是否为空
        }

    private:
        // 私有成员变量 _c,用来存储栈中的元素,底层实现使用 std::vector
        std::vector<T> _c;
    };
}

二、queue的介绍和使用

1.queue的介绍

队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元
素,另一端提取元素。
在这里插入图片描述

2.queue的使用

  1. queue() 构造空的队列
// constructing queues
#include <iostream>       // std::cout
#include <deque>          // std::deque
#include <list>           // std::list
#include <queue>          // std::queue

int main ()
{
  std::deque<int> mydeck (3,100);        // deque with 3 elements
  std::list<int> mylist (2,200);         // list with 2 elements

  std::queue<int> first;                 // empty queue
  std::queue<int> second (mydeck);       // queue initialized to copy of deque

  std::queue<int,std::list<int> > third; // empty queue with list as underlying container
  std::queue<int,std::list<int> > fourth (mylist);

  std::cout << "size of first: " << first.size() << '\n';
  std::cout << "size of second: " << second.size() << '\n';
  std::cout << "size of third: " << third.size() << '\n';
  std::cout << "size of fourth: " << fourth.size() << '\n';

  return 0;
}
  1. empty() 检测队列是否为空,是返回true,否则返回false
// queue::empty
#include <iostream>       // std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myqueue;
  int sum (0);

  for (int i=1;i<=10;i++) myqueue.push(i);

  while (!myqueue.empty())
  {
     sum += myqueue.front();
     myqueue.pop();
  }

  std::cout << "total: " << sum << '\n';

  return 0;
}
  1. size() 返回队列中有效元素的个数
// queue::size
#include <iostream>       // std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myints;
  std::cout << "0. size: " << myints.size() << '\n';

  for (int i=0; i<5; i++) myints.push(i);
  std::cout << "1. size: " << myints.size() << '\n';

  myints.pop();
  std::cout << "2. size: " << myints.size() << '\n';

  return 0;
}
  1. front() 返回队头元素的引用
// queue::front
#include <iostream>       // std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myqueue;

  myqueue.push(77);
  myqueue.push(16);

  myqueue.front() -= myqueue.back();    // 77-16=61

  std::cout << "myqueue.front() is now " << myqueue.front() << '\n';

  return 0;
}
  1. back() 返回队尾元素的引用
// queue::back
#include <iostream>       // std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myqueue;

  myqueue.push(12);
  myqueue.push(75);   // this is now the back

  myqueue.back() -= myqueue.front();

  std::cout << "myqueue.back() is now " << myqueue.back() << '\n';

  return 0;
}
  1. push() 在队尾将元素val入队列
// queue::push/pop
#include <iostream>       // std::cin, std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myqueue;
  int myint;

  std::cout << "Please enter some integers (enter 0 to end):\n";

  do {
    std::cin >> myint;
    myqueue.push (myint);
  } while (myint);

  std::cout << "myqueue contains: ";
  while (!myqueue.empty())
  {
    std::cout << ' ' << myqueue.front();
    myqueue.pop();
  }
  std::cout << '\n';

  return 0;
}
  1. pop() 将队头元素出队列
// queue::empty
#include <iostream>       // std::cout
#include <queue>          // std::queue

int main ()
{
  std::queue<int> myqueue;
  int sum (0);

  for (int i=1;i<=10;i++) myqueue.push(i);

  while (!myqueue.empty())
  {
     sum += myqueue.front();
     myqueue.pop();
  }

  std::cout << "total: " << sum << '\n';

  return 0;
}

3.queue的模拟实现

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,故可以借助list来模拟实现queue

#include <list>
namespace simulate
{
    template <class T>
    class queue
    {
    public:
        queue() {}
        void push(const T &x)
        {
            _c.push_back(x);
        }
        void pop()
        {
            _c.pop_front();
        }
        T &back()
        {
            return _c.back();
        }
        const T &back() const
        {
            return _c.back();
        }
        T &front()
        {
            return _c.front();
        }
        const T &front() const
        {
            return _c.front();
        }
        size_t size() const
        {
            return _c.size();
        }
        bool empty() const
        {
            return _c.empty();
        }

    private:
        std::list<T> _c;
    };
}

三、priority_queue的介绍和使用

1.priority_queue的介绍

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
std::priority_queue<int> pq;
pq.push(10);
pq.push(20);
pq.push(5);
std::cout << pq.top();  // 输出 20,最大值在队列顶部

std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
min_pq.push(10);
min_pq.push(20);
min_pq.push(5);
std::cout << min_pq.top();  // 输出 5,最小值在队列顶部

应用场景:

  • 任务调度:在操作系统中,任务可以根据优先级来调度,优先级队列可以根据任务的重要性排序。
  • Dijkstra算法:用于最短路径算法中,优先级队列可以帮助快速找到当前最短路径的节点。
  • 事件模拟:在模拟系统中,事件可以按照时间顺序(或优先级)处理。

2.priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。

  1. priority_queue()/priority_queue(first,last) 构造一个空的优先级队列
#include <iostream>
#include <queue> // 引入优先队列(priority_queue)库
#include <vector>
#include <functional> // 引入函数对象(greater)库

// 定义比较类,用于自定义优先队列的排序方式
class mycomparison
{
    bool reverse; // 是否反向排序的标志
public:
    // 构造函数,初始化reverse标志,默认false为升序
    mycomparison(const bool &revparam = false)
    {
        reverse = revparam;
    }
    // 重载操作符(),用于比较两个整数
    bool operator()(const int &lhs, const int &rhs) const
    {
        if (reverse)
            return (lhs > rhs); // 如果reverse为true,进行降序排序
        else
            return (lhs < rhs); // 否则进行升序排序
    }
};

int main()
{
    int myints[] = {10, 60, 50, 20}; // 初始化一个整数数组

    std::priority_queue<int> first;                      // 创建一个默认的空优先队列,使用最大堆
    std::priority_queue<int> second(myints, myints + 4); // 使用数组中的元素创建优先队列,默认最大堆
    std::priority_queue<int, std::vector<int>, std::greater<int>>
        third(myints, myints + 4); // 使用std::greater进行最小堆排序的优先队列

    // 使用自定义比较类mycomparison:
    typedef std::priority_queue<int, std::vector<int>, mycomparison> mypq_type; // 定义自定义类型的优先队列

    mypq_type fourth;                    // 默认使用less-than比较的优先队列(最大堆)
    mypq_type fifth(mycomparison(true)); // 使用greater-than比较的优先队列(最小堆)

    return 0;
}

  1. empty( ) 检测优先级队列是否为空,是返回true,否则返回false
#include <iostream>
#include <queue> // 引入优先队列(priority_queue)库

int main()
{
    std::priority_queue<int> mypq; // 创建一个空的优先队列,存储int类型元素
    int sum(0);                    // 初始化sum变量,计算总和

    for (int i = 1; i <= 10; i++)
        mypq.push(i);

    // 当优先队列不为空时,执行循环
    while (!mypq.empty())
    {
        sum += mypq.top(); // 获取优先队列顶部元素(最大值)并加到sum中
        mypq.pop();        // 移除优先队列顶部的元素
    }

    std::cout << "total: " << sum << '\n'; // 输出元素总和

    return 0; // 返回0,表示程序成功结束
}

  1. top( ) 返回优先级队列中最大(最小元素),即堆顶元素
#include <iostream>
#include <queue>
int main()
{
    std::priority_queue<int> mypq; // 创建一个空的优先队列,存储int类型元素

    mypq.push(10);
    mypq.push(20);
    mypq.push(15);

    // 输出当前优先队列的顶部元素(最大值)
    std::cout << "mypq.top() is now " << mypq.top() << '\n';

    return 0;
}

  1. push(x) 在优先级队列中插入元素x
    pop() 删除优先级队列中最大(最小)元素,即堆顶元素
#include <iostream>
#include <queue>

int main()
{
    std::priority_queue<int> mypq;

    mypq.push(30);
    mypq.push(100);
    mypq.push(25);
    mypq.push(40);

    std::cout << "Popping out elements...";
    // 当优先队列不为空时,依次弹出顶部元素
    while (!mypq.empty())
    {
        std::cout << ' ' << mypq.top(); // 输出当前优先队列的顶部元素(最大值)
        mypq.pop();                     // 移除当前优先队列的顶部元素
    }
    std::cout << '\n';

    return 0;
}

  1. 默认情况下,priority_queue是大堆
#include <iostream>
#include <vector>
#include <queue>
#include <functional> // 引入greater算法头文件,用于小堆
using namespace std;
void TestPriorityQueue()
{
    // 默认情况下,创建的是大顶堆(最大堆),其底层按照小于号比较
    vector<int> v{3, 2, 7, 6, 0, 4, 1, 9, 8, 5}; // 初始化向量v,包含10个整数元素
    priority_queue<int> q1;                      // 创建一个默认的大顶堆优先队列,类型为int
    for (auto &e : v)
        q1.push(e);
    cout << q1.top() << endl; // 输出优先队列q1的顶部元素,即最大值

    // 如果要创建小顶堆(最小堆),将第三个模板参数换成greater比较方式
    priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
    cout << q2.top() << endl;
}

  1. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。
#include <iostream>
#include <vector>
#include <queue>
#include <functional>
using namespace std;
class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day)
    {
    }

    // 重载小于运算符,用于比较两个日期
    bool operator<(const Date &d) const
    {
        // 比较年份,如果年相同则比较月份,如果月相同则比较日期
        return (_year < d._year) ||
               (_year == d._year && _month < d._month) ||
               (_year == d._year && _month == d._month && _day < d._day);
    }

    // 重载大于运算符,用于比较两个日期
    bool operator>(const Date &d) const
    {
        // 比较年份,如果年相同则比较月份,如果月相同则比较日期
        return (_year > d._year) ||
               (_year == d._year && _month > d._month) ||
               (_year == d._year && _month == d._month && _day > d._day);
    }

    // 重载输出运算符,以"年-月-日"的格式输出日期
    friend ostream &operator<<(ostream &_cout, const Date &d)
    {
        _cout << d._year << "-" << d._month << "-" << d._day;
        return _cout;
    }

private:
    int _year;
    int _month;
    int _day;
};

void TestPriorityQueue()
{
    // 创建一个大顶堆(最大堆),要求自定义类型Date重载"<"运算符
    priority_queue<Date> q1;
    q1.push(Date(2018, 10, 29));
    q1.push(Date(2018, 10, 28));
    q1.push(Date(2018, 10, 30));
    cout << q1.top() << endl; // 输出优先队列顶部的日期(最大值)

    // 创建一个小顶堆(最小堆),要求自定义类型Date重载">"运算符
    priority_queue<Date, vector<Date>, greater<Date>> q2;
    q2.push(Date(2018, 10, 29));
    q2.push(Date(2018, 10, 28));
    q2.push(Date(2018, 10, 30));
    cout << q2.top() << endl; // 输出优先队列顶部的日期(最小值)
}

3.priority_queue的模拟实现

#pragma once // 防止头文件被重复包含

#include <iostream>
using namespace std;

#include <vector>

namespace simulate // 自定义命名空间simulate
{
    // less 仿函数,用于小于号比较(用于最大堆)
    template <class T>
    struct less
    {
        bool operator()(const T &left, const T &right)
        {
            return left < right;
        }
    };

    // greater 仿函数,用于大于号比较(用于最小堆)
    template <class T>
    struct greater
    {
        bool operator()(const T &left, const T &right)
        {
            return left > right;
        }
    };

    // 优先级队列的模板类
    template <class T, class Container = std::vector<T>, class Compare = less<T>>
    class priority_queue
    {
    public:
        // 构造空的优先级队列
        priority_queue() : c() {}

        // 使用迭代器构造优先级队列
        template <class Iterator>
        priority_queue(Iterator first, Iterator last)
            : c(first, last)
        {
            // 将容器c中的元素调整成堆的结构
            int count = c.size();
            int root = ((count - 2) >> 1); // 找到最后一个非叶子节点
            for (; root >= 0; root--)
                AdjustDown(root); // 从最后一个非叶子节点开始向下调整
        }

        // 向优先队列中插入元素
        void push(const T &data)
        {
            c.push_back(data);      // 将新元素加入容器末尾
            AdjustUP(c.size() - 1); // 从最后一个元素开始向上调整堆
        }

        // 从优先队列中移除堆顶元素
        void pop()
        {
            if (empty()) // 如果队列为空则直接返回
                return;

            swap(c.front(), c.back()); // 将堆顶元素和最后一个元素交换
            c.pop_back();              // 移除最后一个元素(即原来的堆顶)
            AdjustDown(0);             // 从堆顶开始向下调整堆
        }

        // 返回优先队列的大小
        size_t size() const
        {
            return c.size();
        }

        // 检查优先队列是否为空
        bool empty() const
        {
            return c.empty();
        }

        // 返回堆顶元素,但不允许修改,因为修改可能会破坏堆的特性
        const T &top() const
        {
            return c.front(); // 返回容器的第一个元素(堆顶)
        }

    private:
        // 向上调整堆
        void AdjustUP(int child)
        {
            int parent = ((child - 1) >> 1); // 计算父节点索引
            while (child)                    // 当子节点不是根节点时
            {
                if (Compare()(c[parent], c[child])) // 比较父子节点,若不满足堆性质
                {
                    swap(c[child], c[parent]);   // 交换父子节点
                    child = parent;              // 更新子节点为父节点
                    parent = ((child - 1) >> 1); // 更新父节点
                }
                else
                {
                    return; // 满足堆性质,退出循环
                }
            }
        }

        // 向下调整堆
        void AdjustDown(int parent)
        {
            size_t child = parent * 2 + 1; // 计算左子节点索引
            while (child < c.size())       // 当子节点存在时
            {
                // 如果右子节点存在且大于左子节点,选取较大的子节点
                if (child + 1 < c.size() && Compare()(c[child], c[child + 1]))
                    child += 1;

                // 比较父子节点,若父节点不满足堆性质
                if (Compare()(c[parent], c[child]))
                {
                    swap(c[child], c[parent]); // 交换父子节点
                    parent = child;            // 更新父节点为子节点
                    child = parent * 2 + 1;    // 更新子节点索引
                }
                else
                    return; // 满足堆性质,退出循环
            }
        }

    private:
        Container c; // 底层容器
    };
}

// 测试优先队列
void TestQueuePriority()
{
    // 创建最大堆(默认使用less比较器)
    simulate::priority_queue<int> q1;
    q1.push(5);
    q1.push(1);
    q1.push(4);
    q1.push(2);
    q1.push(3);
    q1.push(6);
    cout << q1.top() << endl; // 输出堆顶元素(最大值)

    q1.pop();                 // 移除堆顶元素
    q1.pop();                 // 再次移除堆顶元素
    cout << q1.top() << endl; // 输出新的堆顶元素

    // 创建最小堆(使用greater比较器)
    vector<int> v{5, 1, 4, 2, 3, 6};
    simulate::priority_queue<int, vector<int>, simulate::greater<int>> q2(v.begin(), v.end());
    cout << q2.top() << endl; // 输出堆顶元素(最小值)

    q2.pop();                 // 移除堆顶元素
    q2.pop();                 // 再次移除堆顶元素
    cout << q2.top() << endl; // 输出新的堆顶元素
}

四、容器适配器

1.概念

  • 容器适配器是一类特殊的容器,它们并不是从零开始实现数据存储和管理功能,而是基于已有的序列容器(如 vector、deque 等)构建的一层封装,用于提供简化或定制的接口。这种封装可以让我们使用容器更方便,且专注于特定的功能需求。
  • 容器适配器通过对已有的容器类型进行适配,提供了不同的接口和行为。主要有三种常见的容器适配器:
    stack(栈) queue(队列) priority_queue(优先级队列)

2.STL标准库中stack和queue的底层结构

stack 是一种后进先出(LIFO,Last In First Out)的数据结构,意味着最后插入的元素最先被访问或移除。stack 的底层实现可以使用以下序列容器之一:

  • deque(双端队列)
  • vector(动态数组)
  • list(双向链表)

默认的底层结构:deque stack 默认使用 deque 作为其底层容器。deque提供了快速的双端插入和删除操作,这使得它非常适合实现栈,因为栈的主要操作都发生在顶部(即容器的一端)。

queue 是一种先进先出(FIFO,First In First
Out)的数据结构,意味着最先插入的元素最先被访问或移除。queue 的底层实现可以使用以下序列容器之一:

  • deque(双端队列)
  • list(双向链表) 默认的底层结构:deque queue 默认使用 deque 作为其底层容器。和栈一样,deque 提供了双端插入和删除的能力,这非常适合队列的操作需求。在队列中,元素从一端插入,从另一端删除,这与 deque 的特点相匹配。

3.为什么选择deque作为stack和queue的底层默认容器

为什么 deque 适合作为 stack 的默认实现?

  • 快速的双端插入和删除:deque 支持在头部和尾部进行常数时间的插入和删除操作,这对于栈的 push 和 pop 操作非常重要。
  • 灵活性:相比 vector 只能在尾部操作,deque 提供了更灵活的接口,在两端都可以插入和删除元素。

为什么 deque 适合作为 queue 的默认实现?

  • 双端操作:deque 允许在两端进行快速的插入和删除操作,这与队列的特点(从尾部插入,从头部删除)非常吻合。
  • 灵活性:相比于 list,deque提供了类似数组的连续存储,可能在某些操作中更高效。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值