C++——map和set的使用以及map系列

news2024/11/14 15:12:59

目录

map和set的使用

1. 序列式容器和关联式容器

2. set系列的使⽤

2.1 set和multiset参考⽂档

2.2 set类的介绍

2.3 set的构造和迭代器

2.4 set的增删查

set的增删查关注以下⼏个接⼝即可:

2.6 find和erase使⽤样例:

lower_bound(); upper_bound();

2.7 multiset和set的差异(可重复set(multiset))

那么到这里来一个set使用的例题

下一题,环形链表也超简单。

3. map系列的使⽤

3.1 map和multimap参考⽂档

3.2 map类的介绍

3.3 pair类型介绍

3.4 map的构造

3.5 map的增删查

3.6 map的数据修改

3.7 构造遍历及增删查使⽤样例

3.8 map的迭代器和[]功能样例:

利用operator[] 来统计水果出现的次数

3.9 multimap和map的差异

最后,来看一个运用列题:

总结:第一次做的时候总想着自己创建一个头节点,然后把整个链表跟它一样先把所有next串起来再进行查找,可是看完题解后,别人全用hash存起来后在进行连接是真的方便。


map和set的使用

1. 序列式容器和关联式容器

前⾯我们已经接触过STL中的部分容器如:string、vector、list、deque、list等,这 些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间⼀般没有紧密的关联关系,⽐如交换⼀下,他依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。
关联式容器也是⽤来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是⾮线性结构,两个位置有紧密的关联关系,交换⼀下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。
本章节讲解的map和set底层是红⿊树,红⿊树是⼀颗平衡⼆叉搜索树。set是key搜索场景的结构,map是key/value搜索场景的结构。

2. set系列的使⽤

2.1 set和multiset参考⽂档

https://legacy.cplusplus.com/reference/set/

2.2 set类的介绍

set的声明如下,T就是set底层关键字的类型
set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模版参数
•  set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参数。
⼀般情况下,我们都不需要传后两个模版参数
set底层是⽤红⿊树实现,增删查效率是O ( logN ) ,迭代器遍历是⾛的搜索树的中序,所以是有序的。
前⾯部分我们已经学习了vector / list等容器的使⽤,STL容器接⼝设计,⾼度相似,所以这⾥我们就不再⼀个接⼝⼀个接⼝的介绍,⽽是直接带着⼤家看⽂档,挑⽐较重要的接⼝进⾏介绍。
template < class T, // set::key_type/value_type
           class Compare = less<T>, // set::key_compare/value_compare
           class Alloc = allocator<T>, // set::allocator_type
         > class set;

2.3 set的构造和迭代器

set的构造我们关注以下⼏个接⼝即可。
set的⽀持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是⼆叉搜索树迭代器遍历⾛的中序;⽀持迭代器就意味着⽀持范围for,set的iterator和const_iterator都 不⽀持迭代器修改数据 修改关键字数据,破坏了底层搜索树的结构
// empty (1) ⽆参默认构造
explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());

// range (2) 迭代器区间构造
template <class InputIterator>
set (InputIterator first, InputIterator last,
     const key_compare& comp = key_compare(),
     const allocator_type& = allocator_type());

// copy (3) 拷⻉构造
set (const set& x);

// initializer list (5) initializer 列表构造
set (initializer_list<value_type> il,
     const key_compare& comp = key_compare(),
     const allocator_type& alloc = allocator_type());

// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type

// 正向迭代器
iterator begin();
iterator end();

// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

2.4 set的增删查

set的增删查关注以下⼏个接⼝即可:

// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);

int main()
{
	set<int> s;
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(4);
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	return 0;
}

// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
int main()
{
	set<int> s;
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(3);
	s.insert(4);
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	return 0;
}

// 查找val,返回val所在的迭代器,没有找到返回end()
iterator find (const value_type& val);
auto it = s.find(3);
cout << *it << endl;

// 查找val,返回Val的个数
size_type count (const value_type& val) const;
因为set具有去重的能力,那么count就可以用来判断是否存在这个val
	cout << s.count(3) << endl;

// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);

这里注意不能再while里面删除后再之间访问,会造成迭代器失效,要么就删除一个,然后再将这个迭代器赋值给it 

// 删除val,val不存在返回0,存在返回1
size_type erase (const value_type& val);

s.erase(3);
auto it = s.begin();
while (it != s.end())
{
	cout << *it << " ";
	it++;
}

// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);

左闭右开区间,那么就删除[2,5)之间的值,但不会删除5 

auto first = s.find(2);
auto end = s.find(5);
s.erase(first, end);
auto it = s.begin();
while (it != s.end())
{
	cout << *it << " ";
	it++;
}

// 返回⼤于等于val位置的迭代器
iterator lower_bound (const value_type& val) const;

// 返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;

//10 20 30 40 50 60 

//>=25 那么就是从30开始
auto first = s.lower_bound(25); 

//>55的迭代器,那么就是60
auto end = s.upper_bound(55);
s.erase(first, end);
auto it = s.begin();
while (it != s.end())
{
	cout << *it << " ";
	it++;
}
//10 20 60

2.5. insert和迭代器遍历使⽤样例:
#include<iostream>
#include<set>
using namespace std;
int main()
{
	// 去重+升序排序
	set<int> s;

	// 去重+降序排序(给⼀个⼤于的仿函数)
	//set<int, greater<int>> s;
	s.insert(5);
	s.insert(2);
	s.insert(7);
	s.insert(5);
	//set<int>::iterator it = s.begin();
	auto it = s.begin();
	while (it != s.end())
	{

		// error C3892: “it”: 不能给常量赋值
		// *it = 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 插⼊⼀段initializer_list列表值,已经存在的值插⼊失败
	s.insert({ 2,8,3,9 });
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	set<string> strset = { "sort", "insert", "add" };

	// 遍历string⽐较ascll码⼤⼩顺序遍历的
	for (auto& e : strset)
	{
		cout << e << " ";
	}
	cout << endl;
}

2.6 find和erase使⽤样例:

#include<iostream>
#include<set>
using namespace std;
int main()
{
	set<int> s = { 4,2,7,2,8,5,9 };
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	// 删除最⼩值
	s.erase(s.begin());
	for (auto e : s)
	{
		cout << e << " ";
	}
		cout << endl;

	// 直接删除x
	int x;
	cin >> x;
	int num = s.erase(x);

	if (num == 0)
	{
		cout << x << "不存在!" << endl;
	}

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	// 直接查找在利⽤迭代器删除x
	cin >> x;
	auto pos = s.find(x);

	if (pos != s.end())
	{
		s.erase(pos);
	}
	else
	{
		cout << x << "不存在!" << endl;
	}

	for (auto e : s)
	{
		cout << e << " ";
	}

	cout << endl;
	// 算法库的查找 O(N)
	auto pos1 = find(s.begin(), s.end(), x);

	// set⾃⾝实现的查找 O(logN)
	auto pos2 = s.find(x);

	// 利⽤count间接实现快速查找
	cin >> x;
	if (s.count(x))
	{
		cout << x << "在!" << endl;
	}
	else
	{
	cout << x << "不存在!" << endl;
	}
	return 0;
}

lower_bound(); upper_bound();

#include<iostream>
#include<set>
using namespace std;
int main()
{
	std::set<int> myset;
	for (int i = 1; i < 10; i++)
		myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
	for (auto e : myset)
	{
		cout << e << " ";
	}
	cout << endl;

	// 实现查找到的[itlow,itup)包含[30, 60]区间
	// 返回 >= 30
	auto itlow = myset.lower_bound(30);
	// 返回 > 60
	auto itup = myset.upper_bound(60);

	// 删除这段区间的值
	myset.erase(itlow, itup);
	for (auto e : myset)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

2.7 multiset和set的差异(可重复set(multiset))

multiset和set的使⽤基本完全类似,主要区别点在于multiset⽀持值冗余,那么
insert/find/count/erase都围绕着⽀持值冗余有所差异,具体参看下⾯的样例代码理解。
#include<iostream>
#include<set>
using namespace std;
int main()
{
	// 相⽐set不同的是,multiset是排序,但是不去重
	multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 相⽐set不同的是,x可能会存在多个,find查找中序的第⼀个
	int x;
	cin >> x;
	auto pos = s.find(x);
	while (pos != s.end() && *pos == x)
	{
		cout << *pos << " ";
		++pos;
	}
	cout << endl;

	// 相⽐set不同的是,count会返回x的实际个数
	cout << s.count(x) << endl;

	// 相⽐set不同的是,erase给值时会删除所有的x
	s.erase(x);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

那么到这里来一个set使用的例题

怎么样这题是不是超简单~
只需要进行去重,然后看一个数组的元素是否再另一个数组里出现。那么就要用set去重很方便。
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s;
        for(auto e : nums1)
        {
            s.insert(e);
        }
        set<int> st;
        for(auto e : nums2)
        {
            st.insert(e);
        }

        vector<int> v;
        
        //只是在遍历s中的每一个元素 与st中的每一个元素进行比较
        for(auto e:s)
        {
            if(st.find(e)!=st.end())
            {
                v.push_back(e);
            }
        }
        return v;
    }
};

下一题,环形链表也超简单。

我记得只学C语言的时候,那个时候用快慢双指针来找规律,用快指针去追慢指针,那么这个时候就会发现是否存在环,但是这种办法太慢,要思考很仔细,如果你用set来进行判断,每次都添加节点,如果有一次下一个节点在里面存在就说明有环,否则就没有,就变得非常简单。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_map<ListNode*,int> hash;
        ListNode* root=head;
        while(root)
        {
            hash[root]++;
            if(hash[root]==2) return root;
            root=root->next;
        }
        return root;
    }
};

3. map系列的使⽤

3.1 map和multimap参考⽂档

https://legacy.cplusplus.com/reference/map/

3.2 map类的介绍

map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数,map底层存储数据的内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是⽤红⿊树实现,增删查改效率是 O ( logN ) ,迭代器遍历是⾛的中序,所以是按key有序顺序遍历的。
template < class Key, // map::key_type
	       class T, // map::mapped_type
		   class Compare = less<Key>, // map::key_compare
		   class Alloc = allocator<pair<const Key, T> > //map::allocator_type
	     > class map;

3.3 pair类型介绍

map底层的红⿊树节点中的数据,使⽤pair<Key, T>存储键值对数据。
typedef pair<const Key, T> value_type;

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() : first(T1()), second(T2())
	{}
	pair(const T1& a, const T2& b) : first(a), second(b)
	{}
	template<class U, class V>
	pair(const pair<U, V>& pr) : first(pr.first), second(pr.second)
	{}
};

template <class T1, class T2>
inline pair<T1, T2> make_pair(T1 x, T2 y)
{
	return (pair<T1, T2>(x, y));
}

3.4 map的构造

map的构造我们关注以下⼏个接⼝即可。
map的⽀持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序;⽀持迭代器就意味着⽀持范围for,map⽀持修改value数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
// empty (1) ⽆参默认构造
explicit map(const key_compare& comp = key_compare(),
	const allocator_type& alloc = allocator_type());

// range (2) 迭代器区间构造
template <class InputIterator>
map(InputIterator first, InputIterator last,
	const key_compare& comp = key_compare(),
	const allocator_type & = allocator_type());

// copy (3) 拷⻉构造
map(const map& x);

// initializer list (5) initializer 列表构造
map(initializer_list<value_type> il,
	const key_compare& comp = key_compare(),
	const allocator_type& alloc = allocator_type());

// 迭代器是⼀个双向迭代器
iterator->a bidirectional iterator to const value_type

// 正向迭代器
iterator begin();
iterator end();

// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

3.5 map的增删查

map的增删查关注以下⼏个接⼝即可:
map增接⼝,插⼊的pair键值对数据,跟set所有不同,但是查和删的接⼝只⽤关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value, 同时通过迭代还可以修改value
Member types
key_type->The first template parameter(Key)
mapped_type->The second template parameter(T)
value_type->pair<const key_type, mapped_type>

// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
pair<iterator, bool> insert(const value_type& val);

// 列表插⼊,已经在容器中存在的值不会插⼊
void insert(initializer_list<value_type> il);

// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert(InputIterator first, InputIterator last);

// 查找k,返回k所在的迭代器,没有找到返回end()
iterator find(const key_type& k);

// 查找k,返回k的个数
size_type count(const key_type& k) const;

// 删除⼀个迭代器位置的值
iterator erase(const_iterator position);

// 删除k,k存在返回0,存在返回1
size_type erase(const key_type& k);

// 删除⼀段迭代器区间的值
iterator erase(const_iterator first, const_iterator last);

// 返回⼤于等k位置的迭代器
iterator lower_bound(const key_type& k);

// 返回⼤于k位置的迭代器
const_iterator lower_bound(const key_type& k) const;

3.6 map的数据修改

前⾯我提到map⽀持修改mapped_type 数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map还有⼀个⾮常重要的修改接⼝operator[],但是operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接⼝需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T类型,typedef为mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的T映射值叫做value

operator[] 很重要,在各类算法题里都常常用到。

Member types
key_type->The first template parameter(Key)
mapped_type->The second template parameter(T)
value_type->pair<const key_type, mapped_type>
// 查找k,返回k所在的迭代器,没有找到返回end(),如果找到了通过iterator可以修改key对应的
mapped_type值
iterator find(const key_type& k);
// ⽂档中对insert返回值的说明
// The single element versions (1) return a pair, with its member pair::first
//set to an iterator pointing to either the newly inserted element or to the
//element with an equivalent key in the map.The pair::second element in the pair
//is set to true if a new element was inserted or false if an equivalent key
//already existed.
// insert插⼊⼀个pair<key, T>对象
// 1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象
//first是key所在结点的迭代器,second是false
// 2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象
//first是新插⼊key所在结点的迭代器,second是true
// 也就是说⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭
//代器
// 那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现operator[]
// 需要注意的是这⾥有两个pair,不要混淆了,⼀个是map底层红⿊树节点中存的pair<key, T>,另
//⼀个是insert返回值pair<iterator, bool>
pair<iterator, bool> insert(const value_type & val);
mapped_type& operator[] (const key_type& k);
// operator的内部实现
mapped_type& operator[] (const key_type& k)
{
	// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
//	mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊ + 修改功能
		// 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
		//迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找 + 修改的功能
		pair<iterator, bool> ret = insert({ k, mapped_type() });
	iterator it = ret.first;
	return it->second;
}
// 1 、如果 k 不在 map 中, insert 会插⼊ k mapped_type 默认值,同时 [] 返回结点中存储
mapped_type 值的引⽤,那么我们可以通过引⽤修改返映射值。所以 [] 具备了插⼊ + 修改功能
// 2 、如果 k map 中, insert 会插⼊失败,但是 insert 返回 pair 对象的 first 是指向 key 结点的
迭代器,返回值同时 [] 返回结点中存储 mapped_type 值的引⽤,所以 [] 具备了查找 + 修改的功能

3.7 构造遍历及增删查使⽤样例

#include<iostream>
#include<map>
using namespace std;
int main()
{
	// initializer_list构造及迭代遍历
	map<string, string> dict = { {"left", "左边"}, {"right", "右边"},
	{"insert", "插⼊"},{ "string", "字符串" } };
	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();
	while (it != dict.end())
	{
		//cout << (*it).first <<":"<<(*it).second << endl;
		// map的迭代基本都使⽤operator->,这⾥省略了⼀个->
		// 第⼀个->是迭代器运算符重载,返回pair*,第⼆个箭头是结构指针解引⽤取pair数据
			//cout << it.operator->()->first << ":" << it.operator->()-> second << endl;
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

	// insert插⼊pair对象的4种⽅式,对⽐之下,最后⼀种最⽅便
	pair<string, string> kv1("first", "第⼀个");
	dict.insert(kv1);
	dict.insert(pair<string, string>("second", "第⼆个"));
	dict.insert(make_pair("sort", "排序"));
	dict.insert({ "auto", "⾃动的" });

	// "left"已经存在,插⼊失败
	dict.insert({ "left", "左边,剩余" });

	// 范围for遍历
	for (const auto& e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

	string str;
	while (cin >> str)
	{
		auto ret = dict.find(str);
		if (ret != dict.end())
		{
			cout << "->" << ret->second << endl;
		}
		else
		{
			cout << "⽆此单词,请重新输⼊" << endl;
		}
	}
	// erase等接⼝跟set完全类似,这⾥就不演⽰讲解了
	return 0;
}

3.8 map的迭代器和[]功能样例:

利用迭代器进行记录水果次数
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
	// 利⽤find和iterator修改功能,统计⽔果出现的次数
	string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠",
	"苹果", "⾹蕉", "苹果", "⾹蕉" };
	map<string, int> countMap;
	for (const auto& str : arr)
	{
		// 先查找⽔果在不在map中
		// 1、不在,说明⽔果第⼀次出现,则插⼊{⽔果, 1}
		// 2、在,则查找到的节点中⽔果对应的次数++
		auto ret = countMap.find(str);
		if (ret == countMap.end())
		{
			countMap.insert({ str, 1 });
		}
		else
		{
			ret->second++;
		}
	}
	for (const auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	return 0;
}

利用operator[] 来统计水果出现的次数

#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
	// 利⽤[]插⼊+修改功能,巧妙实现统计⽔果出现的次数
	string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠",
	"苹果", "⾹蕉", "苹果", "⾹蕉" };
	map<string, int> countMap;
	for (const auto& str : arr)
	{
		// []先查找⽔果在不在map中
		// 1、不在,说明⽔果第⼀次出现,则插⼊{⽔果, 0},同时返回次数的引⽤,++⼀下就变成1次了
			// 2、在,则返回⽔果对应的次数++
			countMap[str]++;
	}
	for (const auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	return 0;
}
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	// key不存在->插⼊ {"insert", string()}
	dict["insert"];
	// 插⼊+修改
	dict["left"] = "左边";
	// 修改
	dict["left"] = "左边、剩余";
	// key存在->查找
	cout << dict["left"] << endl;
	return 0;
}

3.9 multimap和map的差异

multimap和map的使⽤基本完全类似,主要区别点在于multimap⽀持关键值key冗余,insert/find/count/erase都围绕着⽀持关键值key冗余有所差异,这⾥跟set和multiset完全⼀样,⽐如find时,有多个key,返回中序第⼀个。其次就是multimap 不⽀持 [],因为⽀持key冗余,[]就只能⽀持插⼊了,不能⽀持修改。

最后,来看一个运用列题:

就是将整个链表的next 和 random全都丢入hash表里面,然后创建新的链表,每次连接链表的next 和 random全都再hash表里面进行查找即可,这一题是对哈希表最好的灵活运用用map或unordered_map 都行。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head==nullptr) return nullptr;

        unordered_map<Node*,Node*> hash;
        Node* cur=head;
        while(cur)
        {
            hash[cur]=new Node(cur->val);
            cur=cur->next;
        }

        cur=head;
        while(cur)
        {
            hash[cur]->next=hash[cur->next];
            hash[cur]->random=hash[cur->random];
            cur=cur->next;
        }

        return hash[head];
    }
};

总结:第一次做的时候总想着自己创建一个头节点,然后把整个链表跟它一样先把所有next串起来再进行查找,可是看完题解后,别人全用hash存起来后在进行连接是真的方便。

总结一下吧~该节只是对set/map简单的结束和运用,对于这两个数据结构来刷算法是完全够用了,对我的作用很大,希望对你也是!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Css_动态渐变圆圈旋转效果

1、效果图 2、实现代码 <template><div class"box"><div class"line"></div><div class"lineNew"></div></div> </template><script lang"ts" setup></script><styl…

MySQL篇(存储引擎 - InnoDB存储引擎架构)(持续更新迭代)

目录 一、逻辑存储结构 1. 表空间 2. 段 3. 区 4. 页 5. 行 二、架构 1. 简介 2. 内存结构&#xff08;四部分&#xff09; Buffer Pool Change Buffer Adaptive Hash Index Log Buffer 3. 磁盘结构&#xff08;七部分&#xff09; System Tablespace File-Per-…

pdf文件怎么直接翻译?使用这些工具让翻译变得简单

在全球化日益加深的职场环境中&#xff0c;处理外语PDF文件成为了许多职场人士面临的共同挑战。 面对这些“加密”的信息宝库&#xff0c;如何高效、准确地将英文pdf翻译成对应语言&#xff0c;成为了提升工作效率的关键。 以下是几款在PDF翻译领域表现出色的软件&#xff0c…

化繁为简:中介者模式如何管理复杂对象交互

化繁为简&#xff1a;中介者模式如何管理复杂对象交互 中介者模式 是一种行为型设计模式&#xff0c;定义了一个中介者对象&#xff0c;来封装一组对象之间的交互。中介者模式通过将对象之间的交互行为从多个对象中抽离出来&#xff0c;集中封装在一个中介者对象中&#xff0c;…

智能除螨仪——NV040D-SOP8语音芯片方案引领除螨仪新时代

随着物联网技术的快速发展&#xff0c;除螨仪作为家庭清洁的重要工具&#xff0c;其智能化、人性化的设计成为提升市场竞争力的关键。置入语音芯片的除螨仪&#xff0c;通过开机提示、工作状态反馈、操作指引、故障提醒等内容。用户可以更加直观地了解除螨仪的工作状态&#xf…

开发谷歌插件之GA埋点

目录 一、背景 二、踩坑 三、谷歌插件开发的GA埋点的实现方式 一、背景 开发了一个谷歌插件&#xff0c;领导需要对用户的一些行为进行分析&#xff0c;于是让我在代码里面加上GA埋点。由于我们的PC端的项目一直都有进行GA埋点&#xff0c;当时就想着&#xff0c;这不就是把…

Spring Cloud Alibaba-(4)Sentinel【流控和降级】

Spring Cloud Alibaba-&#xff08;1&#xff09;搭建项目环境 Spring Cloud Alibaba-&#xff08;2&#xff09;Nacos【服务注册与发现、配置管理】 Spring Cloud Alibaba-&#xff08;3&#xff09;OpenFeign【服务调用】 Spring Cloud Alibaba-&#xff08;4&#xff09;Sen…

界面控件Telerik UI for WinForms 2024 Q3概览 - 支持合并单元格等

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件。所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 本文将介绍界面组件Telerik UI for WinForms在今年第一…

Java语言程序设计基础篇_编程练习题***18.32 (游戏:骑士的旅途)

目录 题目&#xff1a;***18.32 (游戏:骑士的旅途) 习题思路 代码示例 输出结果 题目&#xff1a;***18.32 (游戏:骑士的旅途) 骑士的旅途是一个古老的谜题&#xff0c;它的目的是使骑从棋盘上的任意一个正方 形开始移动&#xff0c;经过其他的每个正方形一次&#xff0c;如…

R18 5G网络中 AI/ML技术特性及其在5GS和NG-RAN中的应用

随着5G技术的发展&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;在网络中的应用越来越广泛。本文将介绍R18 5G网络中AI/ML的新特性&#xff0c;包括在5G系统&#xff08;5GS&#xff09;中的应用、在新一代无线接入网&#xff08;NG-RAN…

AD中PCB元器件常用的对齐用法

1.shift 选中要对齐的元器件 2.按右键&#xff0c;选择对齐&#xff0c;或者按A&#xff0c;弹出对齐对菜单&#xff1b;&#xff08;切记不要选择多余的元器件或者线条&#xff0c;要不然也会根据它的位置来做对齐&#xff0c;按shift一个一个元器件选择&#xff09; 常用如下…

基于SpringBoot+Vue的在线学习平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

出现conda不是内部或外部命令,也不是可运行的程序或批处理文件。的解决办法

发现是我的环境变量不对&#xff0c;需要改成conda.exe所在的目录下 如果不知道自己conda.exe在哪的 可以下载个everything这个软件 找东西很快 找到后 点击环境变量-系统变量-Path-新建-&#xff08;你的conda.exe所在目录&#xff1a;绝对路径&#xff09; 完成上述操作…

【English】语法的整体结构

目录 &#x1f4cc;词类——"英语的本质是词与词之间的修饰关系" 名词( Noun ) 形容词( Adjective ) 动词( Verb ) 副词( Adverbs ) 修饰关系——"理解词与词之间的修饰关系与规则" &#x1f4cc;句子结构 句子的基本成分 五种基本句型和三种句子类型 关于…

ROC和AUC也不是评估机器学习性能的金标准

对于不平衡数据集&#xff0c;AUC值是分类器效果评估的常用标准。但如果在解释时不仔细&#xff0c;它也会有一些误导。以Davis and Goadrich (2006)中的模型为例。如图所示&#xff0c;左侧展示的是两个模型的ROC曲线&#xff0c;右侧展示的是precision-recall曲线 (PRC)。 Pr…

前端univer创建、编辑excel

前端univer创建、编辑excel 源码在线demo&#xff1a;https://codesandbox.io/p/sandbox/univer-q87kqg?file/src/Demo.jsx univer官网地址&#xff1a;https://univer.ai/zh-CN/guides/sheet/introduction 安装univer npm install univerjs/core univerjs/design univerjs…

大模型爬虫—ScrapeGraphAI

大模型爬虫—ScrapeGraphAI 一、介绍 ScrapeGraphAI是一个网络爬虫 Python 库,使用大型语言模型和直接图逻辑为网站和本地文档(XML,HTML,JSON 等)创建爬取管道。 只需告诉库您想提取哪些信息,它将为您完成! scrapegraphai有三种主要的爬取管道可用于从网站(或本地文…

dockerfile 添加arthas 监控插件。容器添加arthas监控

1. arthas官网&#xff1a; 简介 | arthas 2. arthas下载地址&#xff1a; Releases alibaba/arthas GitHub 3. 下载版本&#xff1a; 4. 下载压缩包后&#xff0c;解压缩&#xff0c;放入Dockerfile 同级目录 5. dockerfile 命令&#xff1a; RUN mkdir -p /opt/arthas…

HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践

鸿蒙HarmonyOS开发实战往期文章必看&#xff1a; HarmonyOS NEXT应用开发性能实践总结 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; 非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&am…

【RPA私教课:UIPath】RPA 赋能科技企业,登录时验证码自动截取

在某科技型企业里&#xff0c;专门设置了一个验证码接收系统。每当用户进行登录操作时&#xff0c;都必须从这个系统中抓取最新的登录验证码&#xff0c;以确保登录的安全性。 具体需求如下&#xff1a; 客户会预先在表格中妥善保存众多的账户和密码。当 RPA 机器人在业务系统…