STL C++之无序容器哈希表unordered_set/unordered_multiset/unordered_map/unordered_multimap

模板类unordered_set

template < class Key,                        // unordered_set::key_type/value_type
           class Hash = hash<Key>,           // unordered_set::hasher
           class Pred = equal_to<Key>,       // unordered_set::key_equal
           class Alloc = allocator<Key>      // unordered_set::allocator_type
           > class unordered_set;

无序集是以无特定顺序存储唯一元素的容器,并且允许根据它们的值快速检索单个元素。

unordered_set 中,元素的值同时也是它的键,唯一地标识它。 键是不可变的,因此,unordered_set 中的元素不能在容器中修改一次,但是可以插入和删除它们。

在内部,unordered_set 中的元素不是按任何特定顺序排序的,而是根据它们的哈希值组织成桶,以允许直接通过它们的值快速访问各个元素(平均时间复杂度恒定)。

unordered_set 容器比 set 容器更快地通过它们的键访问单个元素,尽管它们通常对于通过其元素的子集进行范围迭代的效率较低。

容器中的迭代器至少是前向迭代器。

创建C++ unordered_set容器

构造函数如下

empty (1)	explicit unordered_set ( size_type n = /* see below */,
                         const hasher& hf = hasher(),
                         const key_equal& eql = key_equal(),
                         const allocator_type& alloc = allocator_type() );
			explicit unordered_set ( const allocator_type& alloc );
range (2)	template <class InputIterator>
         	unordered_set ( InputIterator first, InputIterator last,
                         size_type n = /* see below */,
                         const hasher& hf = hasher(),
                         const key_equal& eql = key_equal(),
                         const allocator_type& alloc = allocator_type() );
copy (3)	unordered_set ( const unordered_set& ust );
			unordered_set ( const unordered_set& ust, const allocator_type& alloc );
move (4)	unordered_set ( unordered_set&& ust );
			unordered_set ( unordered_set&& ust, const allocator_type& alloc );
initializer list (5)	
			unordered_set ( initializer_list<value_type> il,
                size_type n = /* see below */,
                const hasher& hf = hasher(),
                const key_equal& eql = key_equal(),
                const allocator_type& alloc = allocator_type() );
构造版本含义
empty container constructor构造一个空的 unordered_set 对象,不包含任何元素且大小为零
range constructor构造一个 unordered_set 对象,其中包含 [first,last) 范围内每个元素的副本
copy constructor (and copying with allocator)该对象被初始化为具有与 ust unordered_set 对象相同的内容和属性
move constructor (and moving with allocator)该对象获取右值 ust 的内容
initializer list用列表的内容初始化容器

示例

// constructing unordered_sets
#include <iostream>
#include <string>
#include <unordered_set>

template<class T>
T cmerge(T a, T b) { T t(a); t.insert(b.begin(), b.end()); return t; }

int main()
{
	std::unordered_set<std::string> first;                                // empty
	std::unordered_set<std::string> second({ "red","green","blue" });     // init list
	std::unordered_set<std::string> third({ "orange","pink","yellow" });  // init list
	std::unordered_set<std::string> fourth(second);                       // copy
	std::unordered_set<std::string> fifth(cmerge(third, fourth));         // move
	std::unordered_set<std::string> sixth(fifth.begin(), fifth.end());    // range

	std::cout << "sixth contains:";
	for (const std::string& x : sixth) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

输出

sixth contains: orange pink blue yellow green red

插入元素

(1)	pair<iterator,bool> insert ( const value_type& val );
(2)	pair<iterator,bool> insert ( value_type&& val );
(3)	iterator insert ( const_iterator hint, const value_type& val );
(4)	iterator insert ( const_iterator hint, value_type&& val );
(5)	template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );
(6)	void insert ( initializer_list<value_type> il );

unordered_set 中插入新元素。

仅当每个元素不等于容器中已有的任何其他元素时才插入每个元素(unordered_set 中的元素具有唯一值)

// unordered_set::insert
#include <iostream>
#include <string>
#include <array>
#include <unordered_set>

int main()
{
	std::unordered_set<std::string> myset = { "yellow","green","blue" };
	std::array<std::string, 2> myarray = { "black","white" };
	std::string mystring = "red";

	myset.insert(mystring);                          // copy insertion
	myset.insert(mystring + "dish");                 // move insertion
	myset.insert(myarray.begin(), myarray.end());    // range insertion
	myset.insert({ "purple","orange" });             // initializer list insertion

	std::cout << "myset contains:";
	for (const std::string& x : myset) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

输出

myset contains: yellow black red green blue reddish white purple orange

删除元素

by position (1)	iterator erase ( const_iterator position );
by key (2)	size_type erase ( const key_type& k );
range (3)	iterator erase ( const_iterator first, const_iterator last );
// unordered_set::erase
#include <iostream>
#include <string>
#include <unordered_set>

int main()
{
	std::unordered_set<std::string> myset =
	{ "USA","Canada","France","UK","Japan","Germany","Italy" };

	myset.erase(myset.begin());                    // erasing by iterator
	myset.erase("France");                         // erasing by key
	myset.erase(myset.find("Japan"), myset.end()); // erasing by range

	std::cout << "myset contains:";
	for (const std::string& x : myset) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

可能输出

myset contains:

查找元素

      iterator find ( const key_type& k );
const_iterator find ( const key_type& k ) const;

在容器中搜索以 k 为值的元素,如果找到则返回一个迭代器,否则返回一个迭代器到 unordered_set::end(容器末尾的元素)

// unordered_set::find
#include <iostream>
#include <string>
#include <unordered_set>

int main()
{
    std::unordered_set<std::string> myset = { "red","green","blue" };

    std::string input;
    std::cout << "color? ";
    getline(std::cin, input);

    std::unordered_set<std::string>::const_iterator got = myset.find(input);

    if (got == myset.end())
        std::cout << "not found in myset";
    else
        std::cout << *got << " is in myset";

    std::cout << std::endl;

    return 0;
}

输出

color? red
red is in myset

模板类unordered_multiset

std::unordered_multiset

template < class Key,                         // unordered_multiset::key_type/value_type
           class Hash = hash<Key>,            // unordered_multiset::hasher
           class Pred = equal_to<Key>,        // unordered_multiset::key_equal
           class Alloc = allocator<Key>       // unordered_multiset::allocator_type
           > class unordered_multiset;

无序多重集是不按特定顺序存储元素的容器,允许根据值快速检索单个元素,很像 unordered_set 容器,但允许不同元素具有等效值。

unordered_multiset 中,元素的值同时也是它的键,用于标识它。键是不可变的,因此,unordered_multiset 中的元素不能在容器中修改一次 - 但是可以插入和删除它们。

在内部,unordered_multiset 中的元素没有任何特定的排序,而是根据它们的哈希值组织到桶中,以允许直接通过它们的值快速访问单个元素(平均具有恒定的平均时间复杂度)。

具有等效值的元素被组合在同一个桶中,并且迭代器(参见 equal_range)可以遍历所有元素。

容器中的迭代器至少是前向迭代器。

请注意,此容器未在其自己的标头中定义,而是与 unordered_set 共享标头 <unordered_set>

创建C++ unordered_multiset容器

empty (1)	explicit unordered_multiset ( size_type n = /* see below */,
                              const hasher& hf = hasher(),
                              const key_equal& eql = key_equal(),
                              const allocator_type& alloc = allocator_type() );
			explicit unordered_multiset ( const allocator_type& alloc );
range (2)	template <class InputIterator>
         	unordered_multiset ( InputIterator first, InputIterator last,
                              size_type n = /* see below */,
                              const hasher& hf = hasher(),
                              const key_equal& eql = key_equal(),
                              const allocator_type& alloc = allocator_type() );
copy (3)	unordered_multiset ( const unordered_multiset& ums );
			unordered_multiset ( const unordered_multiset& ums, const allocator_type& alloc );
move (4)	unordered_multiset ( unordered_multiset&& ums );
			unordered_multiset ( unordered_multiset&& ums, const allocator_type& alloc );
initializer list (5)	
unordered_multiset ( initializer_list<value_type> il,
                     size_type n = /* see below */,
                     const hasher& hf = hasher(),
                     const key_equal& eql = key_equal(),
                     const allocator_type& alloc = allocator_type() );
// constructing unordered_multisets
#include <iostream>
#include <string>
#include <unordered_set>

template<class T>
T cmerge(T a, T b) { T t(a); t.insert(b.begin(), b.end()); return t; }

int main()
{
	std::unordered_multiset<std::string> first;									// empty
	std::unordered_multiset<std::string> second({ "red","green","blue" });		// init list
	std::unordered_multiset<std::string> third({ "red","yellow","blue" });		// init list
	std::unordered_multiset<std::string> fourth(second);						// copy
	std::unordered_multiset<std::string> fifth(cmerge(third, fourth));			// move
	std::unordered_multiset<std::string> sixth(fifth.begin(), fifth.end());		// range

	std::cout << "sixth contains:";
	for (const std::string& x : sixth) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

输出

sixth contains: red red green yellow blue blue

插入元素

(1)	iterator insert ( const value_type& val );
(2)	iterator insert ( value_type&& val );
(3)	iterator insert ( const_iterator hint, const value_type& val );
(4)	iterator insert ( const_iterator hint, value_type&& val );
(5)	template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );
(6)	void insert ( initializer_list<value_type> il );
// unordered_multiset::insert
#include <iostream>
#include <string>
#include <array>
#include <unordered_set>

int main()
{
	std::unordered_multiset<std::string> myums = { "red","green","blue" };
	std::array<std::string, 2> myarray = { "red","yellow" };
	std::string mystring = "red";

	myums.insert(mystring);                        // copy insertion
	myums.insert(mystring + "dish");                 // move insertion
	myums.insert(myarray.begin(), myarray.end());  // range insertion
	myums.insert({ "purple","orange" });           // initializer list insertion

	std::cout << "myums contains:";
	for (const std::string& x : myums) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

输出

myums contains: green red red red blue reddish yellow purple orange

删除元素

by position (1)	iterator erase ( const_iterator position );
by key (2)		size_type erase ( const key_type& k );
range (3)		iterator erase ( const_iterator first, const_iterator last );

unordered_multiset 容器中删除值为 k 的元素或元素范围 ([first,last))

// unordered_multiset::erase
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
  std::unordered_multiset<std::string> myums =
  {"fish","duck","cow","cow","pig","hen","sheep"};

  myums.erase ( myums.begin() );                    // erasing by iterator
  myums.erase ( "sheep" );                          // erasing by key
  myums.erase ( myums.find("fish"), myums.end() );  // erasing by range

  std::cout << "myums contains:";
  for ( const std::string& x: myums ) std::cout << " " << x;
  std::cout << std::endl;

  return 0;
}

可能输出

myums contains: hen cow cow duck pig

查找元素

      iterator find ( const key_type& k );
const_iterator find ( const key_type& k ) const;

在容器中搜索以 k 为值的元素,如果找到则返回一个迭代器,否则返回一个迭代器到 unordered_set::end(容器末尾的元素)。

// unordered_multiset::find
#include <iostream>
#include <string>
#include <unordered_set>

int main()
{
    std::unordered_multiset<std::string> myums =
    { "cow","cow","pig","sheep","pig" };

    std::unordered_multiset<std::string>::iterator it = myums.find("pig");

    if (it != myums.end())
        std::cout << *it << " found" << std::endl;

    return 0;
}

输出

pig found

模板类unordered_map

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;

无序映射是关联容器,它存储由键值和映射值组合形成的元素,并允许基于键快速检索各个元素。

unordered_map 中,键值一般用于唯一标识元素,而映射的值是与该键关联的内容的对象。键和映射值的类型可能不同。

在内部,unordered_map 中的元素并没有根据它们的键值或映射值按任何特定顺序排序,而是根据它们的哈希值组织到存储桶中,以允许通过它们的键值直接快速访问单个元素(使用常量平均时间复杂度)。

unordered_map 容器比 map 容器更快地通过其键访问单个元素,尽管它们通常对于通过其元素的子集进行范围迭代的效率较低。

无序映射实现了直接访问运算符 (operator[]),它允许使用其键值作为参数直接访问映射值。

容器中的迭代器至少是前向迭代器。

创建C++ unordered_map容器

构造函数如下

empty (1)	explicit unordered_map ( size_type n = /* see below */,
			                         const hasher& hf = hasher(),
			                         const key_equal& eql = key_equal(),
			                         const allocator_type& alloc = allocator_type() );
			explicit unordered_map ( const allocator_type& alloc );
range (2)	template <class InputIterator>
  			unordered_map ( InputIterator first, InputIterator last,
							size_type n = /* see below */,
							const hasher& hf = hasher(),
							const key_equal& eql = key_equal(),
		                  	const allocator_type& alloc = allocator_type() );
copy (3)	unordered_map ( const unordered_map& ump );
			unordered_map ( const unordered_map& ump, const allocator_type& alloc );
move (4)	unordered_map ( unordered_map&& ump );
			unordered_map ( unordered_map&& ump, const allocator_type& alloc );
initializer list (5)	
			unordered_map ( initializer_list<value_type> il,
				            size_type n = /* see below */,
				            const hasher& hf = hasher(),
				            const key_equal& eql = key_equal(),
				            const allocator_type& alloc = allocator_type() );
构造版本含义
empty container constructor构造一个空的 unordered_map 对象,不包含任何元素且大小为零
range constructor构造一个 unordered_map 对象,其中包含 [first,last) 范围内每个元素的副本
copy constructor (and copying with allocator)该对象被初始化为具有与 unordered_map 对象相同的内容和属性
move constructor (and moving with allocator)该对象获取右值 ump 的内容
initializer list用列表的内容初始化容器
// constructing unordered_maps
#include <iostream>
#include <string>
#include <unordered_map>

typedef std::unordered_map<std::string, std::string> stringmap;

stringmap merge(stringmap a, stringmap b) {
	stringmap temp(a); 
	temp.insert(b.begin(), b.end()); 
	return temp;
}

int main()
{
	stringmap first;												// empty
	stringmap second({ {"apple","red"},{"lemon","yellow"} });       // init list
	stringmap third({ {"orange","orange"},{"strawberry","red"} });  // init list
	stringmap fourth(second);										// copy
	stringmap fifth(merge(third, fourth));							// move
	stringmap sixth(fifth.begin(), fifth.end());					// range

	std::cout << "sixth contains:";
	for (auto& x : sixth) std::cout << " " << x.first << ":" << x.second;
	std::cout << std::endl;

	return 0;
}

输出

sixth contains: orange:orange strawberry:red apple:red lemon:yellow

插入元素

(1)	pair<iterator,bool> insert ( const value_type& val );
(2)	template <class P>
    pair<iterator,bool> insert ( P&& val );
(3)	iterator insert ( const_iterator hint, const value_type& val );
(4)	template <class P>
    iterator insert ( const_iterator hint, P&& val );
(5)	template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );
(6)	void insert ( initializer_list<value_type> il );

unordered_map 中插入新元素。

只有当每个元素的键不等于容器中已经存在的任何其他元素的键时,才会插入每个元素(unordered_map 中的键是唯一的)

// unordered_map::insert
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, double>
        myrecipe,
        mypantry = { {"milk",2.0},{"flour",1.5} };

    std::pair<std::string, double> myshopping("baking powder", 0.3);

    myrecipe.insert(myshopping);                                       // copy insertion
    myrecipe.insert(std::make_pair<std::string, double>("eggs", 6.0)); // move insertion
    myrecipe.insert(mypantry.begin(), mypantry.end());                 // range insertion
    myrecipe.insert({ {"sugar",0.8},{"salt",0.1} });                   // initializer list insertion

    std::cout << "myrecipe contains:" << std::endl;
    for (auto& x : myrecipe)
        std::cout << x.first << ": " << x.second << std::endl;

    std::cout << std::endl;
    return 0;
}

输出

myrecipe contains:
baking powder: 0.3
flour: 1.5
eggs: 6
milk: 2
sugar: 0.8
salt: 0.1

删除元素

by position (1)	iterator erase ( const_iterator position );
by key (2)	size_type erase ( const key_type& k );
range (3)	iterator erase ( const_iterator first, const_iterator last );
// unordered_map::erase
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, std::string> mymap;

    // populating container:
    mymap["U.S."] = "Washington";
    mymap["U.K."] = "London";
    mymap["France"] = "Paris";
    mymap["Russia"] = "Moscow";
    mymap["China"] = "Beijing";
    mymap["Germany"] = "Berlin";
    mymap["Japan"] = "Tokyo";

    // erase examples:
    mymap.erase(mymap.begin());      // erasing by iterator
    mymap.erase("France");             // erasing by key
    mymap.erase(mymap.find("China"), mymap.end()); // erasing by range

    // show content:
    for (auto& x : mymap)
        std::cout << x.first << ": " << x.second << std::endl;

    return 0;
}

可能输出

U.S.: Washington
Russia: Moscow

查找元素

      iterator find ( const key_type& k );
const_iterator find ( const key_type& k ) const;

在容器中搜索以 k 为键的元素,如果找到则返回一个迭代器,否则返回一个迭代器到 unordered_map::end(容器末尾的元素)

// unordered_map::find
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, double> mymap = {
       {"mom",5.4},
       {"dad",6.1},
       {"bro",5.9} };

    std::string input;
    std::cout << "who? ";
    getline(std::cin, input);

    std::unordered_map<std::string, double>::const_iterator got = mymap.find(input);

    if (got == mymap.end())
        std::cout << "not found";
    else
        std::cout << got->first << " is " << got->second;

    std::cout << std::endl;

    return 0;
}

输出

who? mom
mom is 5.4

模板类unordered_multimap

template < class Key,                                    // unordered_multimap::key_type
           class T,                                      // unordered_multimap::mapped_type
           class Hash = hash<Key>,                       // unordered_multimap::hasher
           class Pred = equal_to<Key>,                   // unordered_multimap::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_multimap::allocator_type
           > class unordered_multimap;

无序多映射是存储由键值和映射值组合形成的元素的关联容器,很像 unordered_map 容器,但允许不同的元素具有等效的键。

unordered_multimap 中,键值一般用于唯一标识元素,而映射的值是与该键关联的内容的对象。 键和映射值的类型可能不同。

在内部,unordered_multimap 中的元素并没有根据它们的键或映射值按任何特定顺序排序,而是根据它们的哈希值组织成桶,以允许直接通过它们的键值快速访问单个元素(使用常量 平均时间复杂度)。

具有等效键的元素被组合在同一个桶中,并且迭代器(参见 equal_range)可以遍历所有元素。

容器中的迭代器至少是前向迭代器。

创建C++ unordered_multimap容器

empty (1)	explicit unordered_multimap ( size_type n = /* see below */,
                              const hasher& hf = hasher(),
                              const key_equal& eql = key_equal(),
                              const allocator_type& alloc = allocator_type() );
			explicit unordered_multimap ( const allocator_type& alloc );
range (2)	template <class InputIterator>
         	unordered_multimap ( InputIterator first, InputIterator last,
                              size_type n = /* see below */,
                              const hasher& hf = hasher(),
                              const key_equal& eql = key_equal(),
                              const allocator_type& alloc = allocator_type() );
copy (3)	unordered_multimap ( const unordered_multimap& umm );
			unordered_multimap ( const unordered_multimap& umm, const allocator_type& alloc );
move (4)	unordered_multimap ( unordered_multimap&& umm );
			unordered_multimap ( unordered_multimap&& umm, const allocator_type& alloc );
initializer list (5)	
unordered_multimap ( initializer_list<value_type> il, size_type n = /* see below */,
                     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                     const allocator_type& alloc = allocator_type() );
// constructing unordered_multimaps
#include <iostream>
#include <string>
#include <unordered_map>

typedef std::unordered_multimap<std::string, std::string> stringmap;

stringmap merge(stringmap a, stringmap b) {
	stringmap temp(a); temp.insert(b.begin(), b.end()); return temp;
}

int main()
{
	stringmap first;												// empty
	stringmap second({ {"apple","red"},{"lemon","yellow"} });       // init list
	stringmap third({ {"apple","green"},{"strawberry","red"} });    // init list
	stringmap fourth(second);										// copy
	stringmap fifth(merge(third, fourth));							// move
	stringmap sixth(fifth.begin(), fifth.end());					// range

	std::cout << "sixth contains:";
	for (auto& x : sixth) std::cout << " " << x.first << ":" << x.second;
	std::cout << std::endl;

	return 0;
}

输出

sixth contains: apple:green apple:red strawberry:red lemon:yellow

插入元素

(1)	iterator insert ( const value_type& val );
(2)	template <class P>
    iterator insert ( P&& val );
(3)	iterator insert ( const_iterator hint, const value_type& val );
(4)	template <class P>
    iterator insert ( const_iterator hint, P&& val );
(5)	template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );
(6)	void insert ( initializer_list<value_type> il );
// unordered_multimap::insert
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_multimap<std::string, int>
        first,
        second = { {"AAPL",200},{"GOOG",100} };

    std::pair<std::string, int> mypair("MSFT", 500);

    first.insert(mypair);                            // copy insertion
    first.insert(std::make_pair<std::string, int>("GOOG", 50)); // move insertion
    first.insert(second.begin(), second.end());  // range insertion
    first.insert({ {"ORCL",100},{"GOOG",100} });    // initializer list insertion

    std::cout << "first contains:" << std::endl;
    for (auto& x : first)
        std::cout << x.first << ": " << x.second << std::endl;

    std::cout << std::endl;
    return 0;
}

输出

first contains:
ORCL: 100
AAPL: 200
MSFT: 500
GOOG: 50
GOOG: 100
GOOG: 10

删除元素

by position (1)	iterator erase ( const_iterator position );
by key (2)	size_type erase ( const key_type& k );
range (3)	iterator erase ( const_iterator first, const_iterator last );
// unordered_multimap::erase
#include <iostream>
#include <unordered_map>

int main()
{
    std::unordered_multimap<std::string, std::string> myumm = {
           {"strawberry","red"},
           {"banana","yellow"},
           {"orange","orange"},
           {"lemon","yellow"},
           {"apple","red"},
           {"apple","green"},
           {"pear","green"},
    };


    // erase examples:
    myumm.erase(myumm.begin());     // erasing by iterator
    myumm.erase("apple");             // erasing by key (erases 2 elements)
    myumm.erase(myumm.find("orange"), myumm.end()); // erasing by range

    // show content:
    for (auto& x : myumm)
        std::cout << x.first << ": " << x.second << std::endl;

    return 0;
}

可能结果

lemon: yellow
banana: yellow

查找元素

      iterator find ( const key_type& k );
const_iterator find ( const key_type& k ) const;
// unordered_multimap::find
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_multimap<std::string, std::string> mymap = {
       {"mom","church"},
       {"mom","college"},
       {"dad","office"},
       {"bro","school"} };

    std::cout << "one of the values for 'mom' is: ";
    std::cout << mymap.find("mom")->second;
    std::cout << std::endl;

    return 0;
}

输出

one of the values for 'mom' is: church

总结

容器底层实现是否有序数值是否可以重复能否更改数值查询增删
std::unordered_set哈希表O(1)O(1)
std::unordered_multiset哈希表O(1)O(1)
std::unordered_map哈希表key否key否O(1)O(1)
std::unordered_multimap哈希表key可key否O(1)O(1)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值