二叉堆的实现和改进

堆的优点

  1. 队列

    普通队列:先进先出、后进后出。

    特殊队列(栈):先进后出、后进先出。

    优先队列:出队顺序和入队顺序无关,与优先级有关。

  2. 优先队列的实现

    普通数组: 入队时间O(1) 出队时间O(n)

    有序数组: 入队时间O(n) 出队时间O(1)

    ​ 堆 :出队时间O(lg n) 出队时间O(lg n)

    证明:
    	2^((n+1)/2) > n -> (n+1)/2 > log n -> n+1 > 2log n
    	故 2*log n < n+1
    

实现二叉堆

  1. 二叉堆的结构:

    二叉堆为一颗完全二叉树,每个节点最多有两个子节点,且不能只有右子节点而不能有左子节点。

  2. 构造二叉堆:

    可以用数组来实现二叉堆。

    数组从1开始时,节点n的左子节点为2*n、右子节点为2*n+1。

    数组从0开始时,节点n的左子节点为2*(n+1)-1、右子节点为2*(n+1)。

  3. shiftUp函数

    该函数用于向堆中插入新元素

    函数思路:插入元素时,先检验堆中是否有空间,有空间时将元素插入到队列末尾。此时相当于元素在堆的底部,然后和父节点比较,如果不符合堆的定义(大根堆:父节点大于左右子节点;小根堆:父节点小于左右子节点)就交换新节点和其父节点,交换后再次进行比较,直到满足定义。

   template<typename Item>
   void MaxHeap<Item>::shiftUp(int k)
   {
   	for (int i = k; (i > 1) && (data[i] > data[i / 2]); i = i / 2)
   		swap(data[i], data[i / 2]);
   
    //减少了调用swap函数的次数提高了时间效率
   	//Item item = data[k];
   	//while (k > 1 && item > data[k / 2])
   	//{
   	//	data[k] = data[k / 2];
   	//	k = k / 2;
   	//}
   	//data[k] = item;
   }
   
   template<typename Item>
   void MaxHeap<Item>::insert(Item item)
   {
   	assert(count + 1 <= capacity);
   
   	data[++count] = item;
   
   	shiftUp(count);
   }
   
  1. shiftDown函数

    该函数用于从堆中取出根(最大或最小)节点

    函数思路:取出元素时,先判断堆是否为空。不为空时将根节点取出,把末尾节点的值放置到根节点上,再比较是否大于其左右子节点。如果小于左右子节点,则将其与较大的(或较小的)那个节点进行替换,替换后再次进行比较,直到满足定义(大根堆:大于其左右子节点;小根堆:小于其左右子节点)。

    template<typename Item>
    inline void MaxHeap<Item>::shiftDown(int k)
    {
    	while (2 * k > count)
    	{
    		int j = 0;
    		if (data[2 * i] > data[2 * i + 1])
    			j = 2 * i;
    		else
    			j = 2 * i + 1;
    
    		if (data[j] < data[k])
    			break;
    
    		swap(data[j], data[k]);
    		k = j;
    	}
        
    }
    
    template<typename Item>
    inline Item MaxHeap<Item>::extractMax()
    {
    	assert(count > 0);
    
    	Item item = data[1];
    	swap(data[1], data[count--]);
    	shiftDown(int k);
    
    	return item;
    }
    
    
  2. heapPrint函数

    将堆按层次输出

    函数思路:定义两个变量,一个变量用于存储输出的元素个数、一个用于实现2n,当余下的不足2n时,直接输出。

    template<typename Item>
    inline void MaxHeap<Item>::heapPrint()
    {
    	int i = 0, k = 1;
        //每行按 2^n 输出
    	for (i; i < count&&(count-i)>k; i += (k/2))
    	{
    		for (int j = 1; j <= k; j++)
    			cout << data[i + j] << '-' << i+j << ' ';
    		cout << endl;
    		k *= 2;
    	}
        
        //不足 2^n ,直接输出
    	for (i ; i < count; i++)
    		cout << data[i] << '-' << i << ' ';
    }
    
    
  3. 基于堆的排序
    有两种方法使用堆结构进行排序:

    • 定义一个堆,然后将数组依次输入数组中。

      template<typename Item>
      MaxHeap(Item *arr,int n)
      {
          for(int i=0;i<n;i++)
              insert(arr[i]);
      }
      //时间复杂度:O(n logn);空间复杂度:O(n)
      
    • 从数组 count/2 处开始,依次对前 count/2 个元素调用shiftDown。

      template<typename Item>
      inline MaxHeap<Item>::MaxHeap(Item * arr, int n)
      {
      	data = new Item[n + 1];
      	capacity = n;
      	count = n;
      	memcpy(data, arr, n);//#<string>
      
      	for (int i = n / 2; i > 0; i--)
      		shiftDown(i);
      }
      //时间复杂度:O(n);空间复杂度:O(n)
      
  4. 基础二叉堆(排序)的实现

    #pragma once
    
    #include<iostream>//cout
    #include<algorithm>
    #include<assert.h>//assert
    #include<string>//memcpy
    
    using namespace std;
    
    template<typename Item>
    class MaxHeap
    {
    private:
    	Item *data;//数组堆
    	int capacity;//数组容量
    	int count = 0;//堆内存储元素的个数
    	void shiftUp(int k);//向用户隐藏实现
    	void shiftDown(int k);//向用户隐藏实现
    	
    public:
    	MaxHeap(int capacity);//构建一颗容量为capacity的空二叉树
    	MaxHeap(Item *arr, int n);//将一个长度为n的数组转换成二叉树
    	~MaxHeap();
    	int getHeapSize();//堆中存储的个数
    	bool isEmpty();//堆是否为空
    	void insert(Item item);//向堆中添加元素
    	void heapPrint();//输出堆中的元素
    	Item extractMax();//取出堆中的最大元素
    	void clearHeap();//清空堆
    };
    
    template<typename Item>
    void MaxHeap<Item>::shiftUp(int k)
    {
    	Item item = data[k];
    	while (k > 1 && item > data[k / 2])//此处为item与data[k/2]比较
    	{
    		data[k] = data[k / 2];
    		k = k / 2;
    	}
    	data[k] = item;
    }
    
    template<typename Item>
    inline void MaxHeap<Item>::shiftDown(int k)
    {
    	while (2 * k > count)
    	{
    		int j = 0;
    		if (data[2 * k] > data[2 * k + 1] && 2 * k + 1 < count)
    			j = 2 * k;
    		else
    			j = 2 * k + 1;
    
    		if (data[j] < data[k])
    			break;
    
    		swap(data[j], data[k]);
    		k = j;
    	}
    }
    
    template<typename Item>
    inline MaxHeap<Item>::MaxHeap(int capacity)
    {
    	data = new Item[capacity + 1];
    	count = 0;
    	this->capacity = capacity;
    }
    
    template<typename Item>
    inline MaxHeap<Item>::MaxHeap(Item * arr, int n)
    {
    	data = new Item[n + 1];
    	capacity = n;
    	count = n;
    	memcpy(data, arr, n);
    
    	for (int i = n / 2; i > 0; i--)
    		shiftDown(i);
    }
    
    template<typename Item>
    inline MaxHeap<Item>::~MaxHeap()
    {
    	delete[] data;
    }
    
    template<typename Item>
    inline int MaxHeap<Item>::getHeapSize()
    {
    	return count;
    }
    
    template<typename Item>
    inline bool MaxHeap<Item>::isEmpty()
    {
    	return count == 0;
    }
    
    /*Fn		Insert
    @brief		将索引和数据项存入二叉堆
    @param		Item-item
    @return		void
    */
    template<typename Item>
    inline void MaxHeap<Item>::insert(Item item)
    {
    	assert(count + 1 <= capacity);
    
    	++count;
    	data[count] = item;
    	//cout << "insert data:" << data[count] << endl;
    	//cout << "count:" << count << endl;
    	shiftUp(count);
    }
    
    /*Fn		heapPrint
    @brief		将数据按层次输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline void MaxHeap<Item>::heapPrint()
    {
    	cout << "HeapPrint" << endl;
    
    	int i = 0, k = 1;
    	for (i; i < count&&(count-i)>k; i += (k/2))
    	{
    		for (int j = 1; j <= k; j++)
    			cout << data[i + j] << '-' << i+j << ' ';
    		cout << endl;
    		k *= 2;
    	}
    	for (i++ ; i <= count; i++)
    		cout << data[i] << '-' << i << ' ';
    	cout << endl;
    }
    
    /*Fn		extractMax()
    @brief		将堆顶的元素输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline Item MaxHeap<Item>::extractMax()
    {
    	assert(count > 0);
    
    	Item item = data[1];
    	swap(data[1], data[count--]);
    	shiftDown(1);
    
    	return item;
    }
    
    template<typename Item>
    inline void MaxHeap<Item>::clearHeap()
    {
    	count = 0;
    }
    

二叉堆的优化

  1. 原地堆排序

    template<typename Item>
    void __shiftDown(Item arr[], int n, int k)
    {
    //※※※※※※※
    
    	while (2 * k + 1 < n)
    	{
    		int j = 2 * k + 1;
    		if (arr[j + 1] > arr[j] && j + 1 < n)
    			j += 1;
    
    		if (arr[k] >= arr[j])
    			break;
    
    		swap(arr[k], arr[j]);
    		k = j;
    	}
    }
    
    template<typename Item>
    void heapSort(Item arr[], int n)
    {
        //先对数组进行堆排序
    	//从 (最后一个元素的索引值-1)/2 开始
    	//最后一个元素的索引 = n-1
    	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
    		__shiftDown(arr, n, i);//对arr数组第i个位值的元素进行shiftDown操作,数组arr共有n个元素
    
        //每次都将前i个中的最大值(第一个元素)和到第n-i个元素交换(每次都将余下元素中的最大值移到末尾处。
    	for (int i = n - 1; i > 0; i--)
    	{
    		swap(arr[0], arr[i]);
    		__shiftDown(arr, i, 0);
    	}
     
    }
    
    
  2. 索引堆

    对每一项数据都增加了索引,可以快速的根据数据的编号找到对应的值,也可以修改特定项的值。

    #pragma once
    
    #include<iostream>//cout
    #include<algorithm>
    #include<assert.h>//assert
    #include<string>//memcpy
    
    using namespace std;
    
    template<typename Item>
    class IndexMaxHeap
    {
    private:
    	Item *data;//数组堆
    	int *indexes;//索引列表
    	int capacity;//数组容量
    	int count = 0;//堆内存储元素的个数
    	void shiftUp(int k);//向用户隐藏实现
    	void shiftDown(int k);//向用户隐藏实现
    
    public:
    	IndexMaxHeap(int capacity);//构建一颗容量为capacity的空二叉树
    	IndexMaxHeap(Item *arr, int n);//将一个长度为n的数组转换成二叉树
    	~IndexMaxHeap();
    	int getHeapSize();//堆中存储的个数
    	bool isEmpty();//堆是否为空
    	void insert(int index, Item item);//向堆中添加元素
    	void heapPrint();//按堆输出堆中的元素
    	void linePrint();//按行输出堆中的元素
    	Item extractMax();//取出堆中的最大元素
    	int extractMaxIndex();//取出最大元素的索引
    	Item getItem(int index);//根据索引取出相应的元素
    	void change(int index, Item item);//将index项元素的值改为item
    };
    
    template<typename Item>
    void IndexMaxHeap<Item>::shiftUp(int k)
    {
    	int index = indexes[k];
    	while (k > 1 && data[index] > data[indexes[k / 2]])
    	{
    		indexes[k] = indexes[k / 2];
    		k = k / 2;
    	}
    	indexes[k] = index;
    
    }
    
    template<typename Item>
    inline void IndexMaxHeap<Item>::shiftDown(int k)
    {
    	while (2 * k < count)
    	{
    		int j = 0;
    		if (2 * k + 1 < count && data[indexes[2 * k]] > data[indexes[2 * k + 1]])
    			j = 2 * k;
    		else
    			j = 2 * k + 1;
    
    		if (data[indexes[j]] < data[indexes[k]])
    			break;
    
    		swap(indexes[j], indexes[k]);
    		k = j;
    	}
    }
    
    template<typename Item>
    inline IndexMaxHeap<Item>::IndexMaxHeap(int capacity)
    {
    	data = new Item[capacity + 1];
    	indexes = new int[capacity + 1];
    	count = 0;
    	this->capacity = capacity;
    }
    
    template<typename Item>
    inline IndexMaxHeap<Item>::IndexMaxHeap(Item * arr, int n)
    {
    	data = new Item[n + 1];
    	indexes = new int[capacity + 1];	
    	capacity = n;
    	count = n;
    	memcpy(data, arr, n);
    
    	for (int i = n / 2; i > 0; i--)
    		shiftDown(i);
    }
    
    template<typename Item>
    inline IndexMaxHeap<Item>::~IndexMaxHeap()
    {
    	delete[] data;
    }
    
    template<typename Item>
    inline int IndexMaxHeap<Item>::getHeapSize()
    {
    	return count;
    }
    
    template<typename Item>
    inline bool IndexMaxHeap<Item>::isEmpty()
    {
    	return count == 0;
    }
    
    /*Fn		insert
    @brief		将索引和数据项存入二叉堆
    @param		int-index Item-item	int型的索引以及定义类型的数据
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeap<Item>::insert(int index, Item item)
    {
    	//对用户而言,索引从0开始
    	assert(count + 1 <= capacity);
    	assert(index + 1 >= 1);
    	assert(index + 1 <= capacity);
    
    	data[++index] = item;//data数组存储item,data数组的下标是index
    	indexes[++count] = index;//indexes数组存储index,indexes数组下标使用count+1
    	
    	shiftUp(count);
    }
    
    /*Fn		heapPrint
    @brief		将数据按层次输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeap<Item>::heapPrint()
    {
    	cout << "HeapPrint" << endl;
    
    	int i = 0, k = 1;
    	for (i; i < count && (count - i)>k; i += (k / 2))
    	{
    		for (int j = 1; j <= k; j++)
    			cout << indexes[i + j] << '-' << data[indexes[i + j]] << '-' << i + j << ' ';
    		cout << endl;
    		k *= 2;
    	}
    	for (++i; i <= count; i++) 
    		cout << indexes[i] << '-' << data[indexes[i]] << '-' << i << ' ';//TARGET
    
    	cout << endl;
    }
    
    /*Fn		linePrint
    @brief		将堆中的元素按行输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeap<Item>::linePrint()
    {
    	cout << "LinePrint:" << endl;
    
    	cout << "data[]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << data[i] << ' ';
    	cout << endl;
    
    	cout << "indexes[]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << indexes[i] << ' ';
    	cout << endl;
    
    	cout << "data[indexes[]]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << data[indexes[i]] << ' ';
    	cout << endl;
    }
    
    /*Fn		extractMax()
    @brief		将堆顶的元素输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline Item IndexMaxHeap<Item>::extractMax()
    {
    	assert(count > 0);
    
    	Item item = data[indexes[1]];
    	swap(indexes[1], indexes[count--]);
    	shiftDown(1);
    
    	return item;
    }
    
    /*Fn		extractMaxIndex
    @brief		取出堆顶元素的序号
    @param		void
    @return		int
    */
    template<typename Item>
    inline int IndexMaxHeap<Item>::extractMaxIndex()
    {
    	assert(count > 0);
    	int ret = indexes[1] - 1;
    
    	swap(indexes[1], indexes[count--]);
    
    	shiftDown(1);
    
    	return indexes[1];
    }
    
    /*Fn		getItem
    @brief		根据索引得到值
    @param		int-index 
    @return		Item-item 
    */
    template<typename Item>
    inline Item IndexMaxHeap<Item>::getItem(int index)
    {
    	return data[index + 1];
    }
    
    /*Fn		change
    @brief		改变对应索引值元素的值
    @param		int-index Item-item
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeap<Item>::change(int index, Item newItem)
    {
    	index += 1;
    	data[index] = newItem;
    
    	//找到indexes[i]=index,i是data[index]在堆中的位值
    	//然后shiftUp(i)、shiftDown(i);
    	for (int i = 1; i <= count; i++)
    	{
    		if (index == indexes[i])
    		{
    			shiftDown(i);
    			shiftUp(i);
    			return;
    		}
    	}
    
    }
    
    
  3. 索引堆plus

    增加反向查找数组reverse[],使得通过索引更改数值操作的时间复杂度由O(n)降到了O(log n)。
    indexes[i]=j — reverse[j]=i
    indexes[reverse[i]]=i — reverse[indexes[i]]=i。

    #pragma once
    
    #include<iostream>//cout
    #include<algorithm>
    #include<assert.h>//assert
    #include<string>//memcpy
    
    using namespace std;
    
    template<typename Item>
    class IndexMaxHeapPLUS
    {
    private:
    	Item *data;//数组堆
    	int *indexes;//索引列表
    	int *reverse;//反向索引数组
    	int capacity;//数组容量
    	int count = 0;//堆内存储元素的个数
    	void shiftUp(int k);//向用户隐藏实现
    	void shiftDown(int k);//向用户隐藏实现
    	bool contain(int index);//判断索引是否存在
    
    public:
    	IndexMaxHeapPLUS(int capacity);//构建一颗容量为capacity的空二叉树
    	IndexMaxHeapPLUS(Item *arr, int n);//将一个长度为n的数组转换成二叉树
    	~IndexMaxHeapPLUS();
    	int getHeapSize();//堆中存储的个数
    	bool isEmpty();//堆是否为空
    	void insert(int index, Item item);//向堆中添加元素
    	void heapPrint();//按堆输出堆中的元素
    	void linePrint();//按行输出堆中的元素
    	Item extractMax();//取出堆中的最大元素
    	int extractMaxIndex();//取出最大元素的索引
    	Item getItem(int index);//根据索引取出相应的元素
    	void change(int index, Item item);//将index项元素的值改为item
    };
    
    template<typename Item>
    void IndexMaxHeapPLUS<Item>::shiftUp(int k)
    {
    
    	while (k > 1 && data[indexes[k]] > data[indexes[k / 2]])
    	{
    		swap(indexes[k], indexes[k / 2]);
    		reverse[indexes[k]] = k;
    		reverse[indexes[k / 2]] = k / 2;
    		k = k / 2;
    	}
    	
    }
    
    template<typename Item>
    inline void IndexMaxHeapPLUS<Item>::shiftDown(int k)
    {
    
    	while (2 * k <= count)
    	{
    		int j = 2*k;
    		if (j + 1 < count&&data[indexes[j]] < data[indexes[j + 1]])
    			j += 1;
    
    
    		if (data[indexes[j]] < data[indexes[k]])
    			break;
    
    		swap(indexes[j], indexes[k]);
    		reverse[indexes[j]] = j;
    		reverse[indexes[k]] = k;
    		k = j;
    	}
    }
    
    template<typename Item>
    inline IndexMaxHeapPLUS<Item>::IndexMaxHeapPLUS(int capacity)
    {
    	data = new Item[capacity + 1];
    	indexes = new int[capacity + 1];
    	reverse = new int[capacity + 1];
    	memset(reverse, capacity + 1, 0);//赋初值
    	count = 0;
    	this->capacity = capacity;
    }
    
    template<typename Item>
    inline IndexMaxHeapPLUS<Item>::IndexMaxHeapPLUS(Item * arr, int n)
    {
    	data = new Item[n + 1];
    	indexes = new int[capacity + 1];
    	capacity = n;
    	count = n;
    	memcpy(data, arr, n);
    
    	for (int i = n / 2; i > 0; i--)
    		shiftDown(i);
    }
    
    template<typename Item>
    inline IndexMaxHeapPLUS<Item>::~IndexMaxHeapPLUS()
    {
    	delete[] data;
    }
    
    template<typename Item>
    inline int IndexMaxHeapPLUS<Item>::getHeapSize()
    {
    	return count;
    }
    
    template<typename Item>
    inline bool IndexMaxHeapPLUS<Item>::isEmpty()
    {
    	return count == 0;
    }
    
    /*Fn		insert
    @brief		将索引和数据项存入二叉堆
    @param		int-index Item-item	int型的索引以及定义类型的数据
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeapPLUS<Item>::insert(int index, Item item)
    {
    	//对用户而言,索引从0开始
    	assert(count + 1 <= capacity);
    	assert(index + 1 >= 1);
    	assert(index + 1 <= capacity);
    
    	data[++index] = item;
    	indexes[++count] = index;
    	reverse[index] = count;
    
    
    	shiftUp(count);
    }
    
    /*Fn		heapPrint
    @brief		将数据按层次输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeapPLUS<Item>::heapPrint()
    {
    	cout << "HeapPrint:" << endl;
    	int i = 0, k = 1;
    	for (i; i < count && (count - i)>k; i += (k / 2))
    	{
    		for (int j = 1; j <= k; j++)
    			cout << data[indexes[i + j]] << '-' << i + j << ' ';
    		cout << endl;
    		k *= 2;
    	}
    	for (++i; i <= count; i++)
    		cout << data[indexes[i]] << '-' << i << ' ';
    	cout << endl;
    }
    
    template<typename Item>
    inline void IndexMaxHeapPLUS<Item>::linePrint()
    {
    	cout << "LinePrint:" << endl;
    
    	cout << "data[]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << data[i] << ' ';
    	cout << endl;
    
    	cout << "indexes[]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << indexes[i] << ' ';
    	cout << endl;
    
    	cout << "reverse[]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << reverse[i] << ' ';
    	cout << endl;
    
    	cout << "data[indexes[]]:" << endl;
    	for (int i = 1; i <= count; i++)
    		cout << data[indexes[i]] << ' ';
    	cout << endl;
    }
    
    /*Fn		extractMax()
    @brief		将堆顶的元素输出
    @param		void
    @return		void
    */
    template<typename Item>
    inline Item IndexMaxHeapPLUS<Item>::extractMax()
    {
    	assert(count > 0);
    
    	Item item = data[indexes[1]];
    	swap(indexes[1], indexes[count]);
    	reverse[indexes[1]] = 1;
    	reverse[indexes[count--]] = 0;
    	shiftDown(1);
    
    	return item;
    }
    
    /*Fn		extractMaxIndex
    @brief		取出堆顶元素的序号
    @param		void
    @return		int
    */
    template<typename Item>
    inline int IndexMaxHeapPLUS<Item>::extractMaxIndex()
    {
    	assert(count > 0);
    	int ret = indexes[1] - 1;
    
    	swap(indexes[1], indexes[count]);
    	reverse[indexes[1]] = 1;
    	reverse[indexes[count--]] = 0;
    	shiftDown(1);
    
    	return indexes[1];
    }
    
    /*Fn		contian
    @brief		判断是否有逆向数组
    @param		int-index
    @return		bool
    */
    template<typename Item>
    inline bool IndexMaxHeapPLUS<Item>::contain(int index)
    {
    	assert(index + 1 >= 1 && index + 1 <= capacity);
    return reverse[index + 1] != 0;
    }
    
    /*Fn		getItem
    @brief		根据索引得到值
    @param		int-index 
    @return		Item-item 
    */
    template<typename Item>
    inline Item IndexMaxHeapPLUS<Item>::getItem(int index)
    {
    	assert(contain(index));
    	return data[index + 1];
    }
    
    /*Fn		change
    @brief		改变对应索引值元素的值
    @param		int-index Item-item 
    @return		void
    */
    template<typename Item>
    inline void IndexMaxHeapPLUS<Item>::change(int index, Item newItem)
    {
    	assert(contain(index));
    
    	index += 1;
    	data[index] = newItem;
    
    
    	int j = reverse[index];
    	cout << j << endl;
    	shiftUp(j);
    	shiftDown(j);
    
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值