【C++】封装哈希表 unordered_map和unordered_set容器

news2025/1/11 23:55:36

目录​​​​​​​

一、unordered系列关联式容器

1、unordered_map

 2、unordered_map的接口

 3、unordered_set

二、哈希表的改造

三、哈希表的迭代器

1、const 迭代器

 2、 operator++

 3、begin()/end()

​ 4、实现map[]运算符重载

 四、封装 unordered_map 和 unordered_set 的接口

1、unordered_map

2、unordered_set

3、封装map和 set 迭代器

五、解决 key 不能修改的问题

1、set

​ 2、map

六、完整代码


一、unordered系列关联式容器

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到log_2 N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个 unordered 系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,本文中只对unordered_mapunordered_set进行介绍,unordered_multimap和unordered_multiset学生可查看文档介绍。

1、unordered_map

1. unordered_map是存储键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。 2. 在unordered_map中,键值通常用于唯一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。

3. 在内部,unordered_map没有对按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map 将相同哈希值的键值对放在相同的桶中。

4. unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。

5. unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问 value。 6. 它的迭代器至少是前向迭代器【unordered_map官方文档】

 2、unordered_map的接口

①unordered_map的构造

函数声明功能介绍

unordered_map

构造不同格式的unordered_map对象

②unordered_map的容量

函数声明功能介绍
bool empty() const检测unordered_map是否为空
size_t size() const获取unordered_map的有效元素个数

③unordered_map的迭代器

函数声明功能介绍
begin返回unordered_map第一个元素的迭代器
end返回unordered_map最后一个元素下一个位置的迭代器
cbegin返回unordered_map第一个元素的const迭代器
cend返回unordered_map最后一个元素下一个位置的const迭代器

④unordered_map的元素访问

函数声明功能介绍
operator[]返回与key对应的value,没有一个默认值

 注意:该函数中实际调用哈希桶的插入操作,用参数key与V()构造一个默认值往底层哈希桶中插入,如果key不在哈希桶中,插入成功,返回V(),插入失败,说明key已经在哈希桶中, 将key对应的value返回。

⑤unordered_map的查询

函数声明功能介绍
iterator find(const K&key)返回key在哈希桶中的位置
size_t count(K& key)返回哈希桶中关键码为key的键值对的个数

 注意:unordered_map中key是不能重复的,因此count函数的返回值最大为1

⑥unordered_map的修改操作

函数声明功能介绍
insert向容器中插入键值对
erase删除容器中的键值对
void clear()清空容器中有效元素个数
void swap()交换两个容器中的元素

⑦unordered_map的桶操作

函数声明功能介绍
size_t bucket_count()const返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const返回n号桶中有效元素的总个数
size_t bucket(const K& key)返回元素key所在的桶号

 3、unordered_set

自行查阅文档【unordered_set在线文档说明】

二、哈希表的改造

我们要对哈希表进行改造,因为unordered_map和unordered_set底层用的都是哈希表,虽然不是同一个哈希表,但是是同一个模板实例化出来的哈希表。我们要让哈希表可以适配存储不同的数据类型,因为unordered_set里面存的是K类型,而unordered_map里面存的是pair<K,V>类型。

所以我们可以用来表示。当传的模板参数是K类型,哈希表就实例化存的就是K类型,当传的模板参数是pair<K,V>类型,哈希表实例化存的就是pair<K,V>类型,所以我们可以通过传不同的模板参数来决定哈希表里存的是什么数据类型。

🔴【问题】
这里不管是插入还是查找还是删除第一步都是需要将数据的哈希地址找到,而哈希地址是利用除留余数法计算得到的,是利用key值进行取模的,但这里一旦适配泛型后,我们就不知道具体的类型是K类型还是pair<K,V>类型,如果是K类型那么就可以直接取模,如果是pair<K,V>类型,那是不可以直接取模的,需要将里面的key值取出来。
🟢【解答】
我们可以利用一个仿函数,这个仿函数的功能是可以将数据里的Key类型数据取出来。那么我们可以给哈希表增加一个模板参数,给仿函数用。一旦遇到要计算哈希地址或者比较的操作时,我们就可以将数据里的K值取出来进行计算比较。
仿函数实现的原理:当T类型是K类型数据时,直接返回K值即可,当T类型是pair<K,V>数据时,返回里面的first数据即可(就是K值)。

三、哈希表的迭代器

哈希表的迭代器是一个自定义类型,因为原生的结点指针不能满足我们想要的操作,所以我们就直接将结点的指针封装起来,然后自定义一个类型,对这个结点指针增加一些操作来完成我们想要的效果。

首先哈希表的迭代器里肯定要封装一个结点的指针,++后找到下一个结点,在一个哈希桶里,++就代表着找链表下面的结点即可,那么如果该结点就是链表最后一个结点呢?怎么找到下一个不为空的桶呢?又或者你是如何找到第一个不为空的桶的呢?
所以这里我们还需要一个指向哈希表的指针,这样我们才可以找到桶与桶之间的关系,而结点指针是用来找结点与结点之间的关系的。

哈希表的迭代器里封装两个对象,一个是结点指针,一个哈希表指针。

1、const 迭代器

与链表的const迭代器实现原理一样,我们通过三个模板参数(template <class T,class Ref,class Ptr>)来控制函数的返回值,从而控制返回的是普通类型的迭代器还是const类型的迭代器。这里也就是泛型适配,适配多种类型 。 

Ref 控制解引用函数的返回值,当Ref为T&时,返回的就是普通迭代器,当Ref 为 const T&时,返回的就是 const 迭代器。
Ptr 控制的 -> 重载函数的返回值,当 Ptr 为T时,返回的就是普通迭代器,当 Ptr 为 const T时,返回的就是const迭代器。

 2、 operator++

实现原理:

①假设当前结点在一个没有走完的桶里,那么直接找下一个结点即可
②如果这个结点是桶里最后一个节点,即桶走完了,那么我们就要找下一个不为空的桶
③如何找到下一个不为空的桶呢?首先将当前结点的哈希地址计算出来,然后将哈希地址++,再利用一个循环,查找后面的桶是否为空,如果不为空,那么这个桶就是最终结果,如果为空就再找后面的桶。

 🔴【问题1】

这里存在一个相互依赖关系问题,因为在哈希表里我们使用了迭代器,在迭代器里我们又使用了哈希表。

🟢【解答1】

我们需要在这里使用前置声明告诉编译器,我们是有哈希表,只不过哈希表在后面。这样就不会报错啦。

 🔴【问题2】

在迭代器的++里我们在计算当前结点的哈希地址时,取模时,利用哈希指针找到了哈希表里的vector<Node*> _tables 的元素,并访问了它的函数,这里我们在外面调用哈希表的私有成员,这样是不可行

🟢【解答2】让迭代器成为哈希表的友元类,这样在迭代器里就可以使用哈希表的私有成员了。

 3、begin()/end()

begin()就是找哈希表里第一个不为空的桶。
end()就是找最后一个不为空的桶的下一个位置,也就是空

🔴 【问题】编译会提示没有构造函数可以接收,或者构造函数重载不明确。这是为什么呢?

🟢【解答】

问题在于const修饰begin()和const修饰end()。因为const修饰了this指针,导致指向哈希表的指针变成const类型了,而迭代器的构造里,是用普通迭代器构造的。所以当this指针传过去构造时,const是不能传给普通类型的,权限放大了。所以这里我们只需要重载一个参数类型是const类型的哈希表指针即可。

 4、实现map[]运算符重载

map的[ ]运算符重载,底层实现本质是调用了 insert 函数。然后通过insert函数返回的pair<iterator,bool>类型数据来找到Value值。

所以在实现[ ]运算符重载时,我们需要对哈希表里的 insert 进行改造,因为原来的 insert 的返回值是布尔值,我们需要pair类型返回值。

哈希表的insert改造后,那么 set 和 map 里的insert都需要修改,因为底层用的就是调用用哈希表的insert。

 🔴【问题】

我们之前让普通迭代变成const迭代器,而这里的pair<iterator,bool>中的iterator其实本质上是const_iterator。
是pair<const_itearto,bool>类型的。而哈希表里的insert返回的是普通迭代器,也就是pair<iterator,bool>类型的。这是两个不同的类型,无法直接将pair<iterator,bool>类型转换成pair<const_itearto,bool>类型的。所以会报错。

🟢【解答】

我们需要的是让普通迭代器能够拷贝给const迭代器。所以我们需要自己增加拷贝函数。库里的设计很妙,库里重新定义了一个 iterator,作为拷贝对象,而这个iterator固定了就是普通的迭代器,不会随着调用对象而改变类型。所以当普通迭代器调用时,就会将普通iterator拷贝给它。当const迭代器调用时,就会将普通迭代器iterator拷贝给它。所以我们需要对哈希表的迭代器添加拷贝构造。用普通迭代器iteartor作为拷贝对象。

 四、封装 unordered_map 和 unordered_set 的接口

1、unordered_map

unordered_map中存储的是pair的键值对,K为key的类型,V为value的类型,HashFunc哈希函数类型 。unordered_map在实现时,只需将hash_bucket中的接口重新封装即可。

2、unordered_set

底层封装一个哈希表,哈希表里存的是K类型。

3、封装map和 set 迭代器

只有哈希表里的迭代器完成了,才可以封装map和set里的迭代器。
封装set的迭代器,本质就是调用哈希表的迭代器接口。

1.不过要注意的是,在重命名红黑树里的迭代器时,需要在类名前面加上typename,如果不加上typename是不行的,因为这时类模板还没有实例化出对象出来,就算实例化了,也有部分类型没有实例,因为编译器也不知道这个是内置类型还是静态变量,加上typename是告诉编译器这个是类型,这个类型在类模板里定义,等类模板实例化后再找。
2.定义好普通迭代和const迭代器后,就可以实现begin()和end()了。

五、解决 key 不能修改的问题

1、set

🔴【存在问题】

迭代器的解引用是可以修改的,一旦修改可能不是二叉树:

set 的 key 值、使用迭代器修改:

🟢【解决方法】
set 里存储的值就只有Key值,索性我们直接让这个存储的数据无法被修改,只能访问读取,无法修改。所以我们让普通迭代器变成const迭代器即可。所以在set里,我们将普通迭代器和const迭代器都设为const迭代器。
 但此时会遇到一个问题,begin() 和 end() 的返回类型是const 迭代器,但是_t.begin()是普通迭代器,这样就会类型不匹配。因此我们需要定义一个构造函数将普通迭代器和const迭代器相转换。

 2、map

🟢map的解决原理
在存储的时候就让K值无法修改。
因为我们知道map里存储的数据是pair<K,V>类型,我们不能想set那个让普通迭代器变成const迭代器,因为map要求Value的值还是可以修改的,所以不让pair<K,V>类型无法修改,而是单纯的让里面的K值无法修改,也就是在里面用const修饰K,那么这样K值就不能被修改,V值可以被修改。
pair是可以修改的,但是里面的K是无法被修改的!

六、完整代码

HashTable.h

#pragma once
#include<vector>

template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

//HashFunc<string>
template<>
struct HashFunc<string>
{
	size_t operator()(const string& key)
	{
		// BKDR
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}

		//cout << key << ":" << hash << endl;
		return hash;
	}
};


namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		HashNode<T>* _next;
		T _data;

		HashNode(const T& data)
			:_data(data)
			, _next(nullptr)
		{}
	};

	// 前置声明
	//这里存在问题,迭代器里要用哈希,哈希里面要有迭代器,相互依赖关系
    //我们这里用前置声明告诉编译器,我们要是有哈希表,这个表是存在的,在后面
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
		//底层封装着一个结点指针
		Node* _node;
		//底层还封装着一个哈希表指针
	    //这里可以加const因为我们不是根据pht来找到哈希表来修改哈希表里的内容
		const HashTable<K, T, KeyOfT, Hash>* _pht;

		size_t _hashi;

		__HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}

		__HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}

		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶还有节点,走到下一个节点
				_node = _node->_next;
			}
			else
			{
				// 当前桶已经走完了,找下一个桶开始
				//KeyOfT kot;
				//Hash hf;
				//size_t hashi = hf(kot(_node->_data)) % _pht._tables.size();
				++_hashi;
				while (_hashi < _pht->_tables.size())
				{
					if (_pht->_tables[_hashi])
					{
						_node = _pht->_tables[_hashi];
						break;
					}

					++_hashi;
				}

				if (_hashi == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
	};

	// unordered_set -> Hashtable<K, K>
	// unordered_map -> Hashtable<K, pair<K, V>>
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct __HTIterator;

	public:
		//适配普通迭代器
		typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		//适配const迭代器
		typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this, i);
				}
			}

			return end();
		}

		iterator end()
		{
			return iterator(nullptr, this, -1);
		}

		const_iterator begin() const
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return const_iterator(_tables[i], this, i);
				}
			}

			return end();
		}

		// this-> const HashTable<K, T, KeyOfT, Hash>*
		const_iterator end() const
		{
			return const_iterator(nullptr, this, -1);
		}

		HashTable()
		{
			_tables.resize(10);
		}

		~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}

		pair<iterator, bool> Insert(const T& data)
		{
			Hash hf;
			KeyOfT kot;

			iterator it = Find(kot(data));
			if (it != end())
				return make_pair(it, false);

			// 负载因子最大到1
			if (_n == _tables.size())
			{
				vector<Node*> newTables;
				newTables.resize(_tables.size() * 2, nullptr);
				// 遍历旧表
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						// 挪动到映射的新表
						size_t hashi = hf(kot(cur->_data)) % newTables.size();
						cur->_next = newTables[i];
						newTables[hashi] = cur;

						cur = next;
					}

					_tables[i] = nullptr;
				}

				_tables.swap(newTables);
			}

			size_t hashi = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);

			// 头插
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;

			return make_pair(iterator(newnode, this, hashi), true);
		}

		iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this, hashi);
				}

				cur = cur->_next;
			}

			return end();
		}

		bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;

					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _tables;
		size_t _n = 0;
	};
}

 unoederedMap.h

#define  _CRT_SECURE_NO_WARNINGS

#pragma once
#include"HashTable.h"

namespace zhou
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		/*typedef typename hash_bucket<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket<K, pair< K, V>, MapKeyOfT>::const_iterator const_iterator;*/

		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;


		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		const V& operator[](const K& key) const
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
	};
}

unorderedSet.h

#define  _CRT_SECURE_NO_WARNINGS

#pragma once
#include"HashTable.h"

namespace zhou
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator const_iterator;

		/*iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}*/

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

		pair<const_iterator, bool> insert(const K& key)
		{
			auto ret = _ht.Insert(key);
			return pair<const_iterator, bool>(const_iterator(ret.first._node, ret.first._pht, ret.first._hashi), ret.second);
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht;
	};

	//void test_set()
	//{
	
	//	unordered_set<int> us;
	//	us.insert(5);
	//	us.insert(15);
	//	us.insert(52);
	//	us.insert(3);

	//	unordered_set<int>::iterator it = us.begin();
	//	while (it != us.end())
	//	{
	//		//*it += 5;
	//		cout << *it << " ";
	//		++it;
	//	}
	//	cout << endl;

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

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

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

相关文章

ThinkPHP Lang多语言本地文件包含漏洞(QVD-2022-46174)漏洞复现

1 漏洞描述 ThinkPHP是一个在中国使用较多的PHP框架。在其6.0.13版本及以前&#xff0c;存在一处本地文件包含漏洞。当ThinkPHP开启了多语言功能时&#xff0c;攻击者可以通过lang参数和目录穿越实现文件包含&#xff0c;当存在其他扩展模块如 pear 扩展时&#xff0c;攻击者可…

【城市】2023香港身份与生活定居相关政策(IANG,优才/高才/专才,受养人/单程证)

【城市】2023香港身份与生活定居相关政策&#xff08;IANG&#xff0c;优才/高才/专才&#xff0c;受养人/单程证&#xff09; 文章目录 一、如何获得香港身份1、7年计划2、旅游签 二、港澳相关的证件类别1、HK证件2、CN证件 三、香港生活对比内地 本文仅代表2023年查阅相关资料…

「C/C++ 01」计算结构体/类的大小和内存对齐

目录 一、计算结构体的大小 二、计算类的大小 三、内存对齐 一、计算结构体的大小 计算结构体的大小要遵循内存对齐规则&#xff1a;即从第二个成员变量开始&#xff0c;起始位置要计算&#xff0c;在自己的大小和默认对齐数(VS编译器中默认对齐数为8)中选择较小的那个&#x…

LVGL自定义滑动

触摸和编码器都可以操作 typedef struct {lv_obj_t* obj;int16_t x;int16_t y;int16_t width;int16_t height; }pos_and_size_t;typedef struct {lv_obj_t* obj;lv_coord_t height;lv_coord_t width;lv_coord_t width_pad;lv_coord_t height_pad;lv_coord_t child_widget;lv_co…

监控操作台为生活提供安全保障

在科技日新月异的现代社会&#xff0c;监控操作台已成为我们生活中不能缺少的一部分。它犹如一座城市的守护神&#xff0c;默默无闻地守护着我们的安全&#xff0c;确保着每一刻的平安。今天&#xff0c;和北京嘉德立一同走进这个神秘的世界&#xff0c;揭开监控操作台的神秘面…

(十四)Servlet教程——Servlet中HttpSession的使用

除了使用Cookie&#xff0c;Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制&#xff0c;相应地也增加了服务器的存储压力。 1. 什么是Session Session是另外一种记录客户端状态的机制&#xff0c;不同的是Cookie保存在客户…

线程池嵌套导致的死锁问题

1、背景 有一个报告功能&#xff0c;报告需要生成1个word&#xff0c;6个excel附件&#xff0c;总共7个文件&#xff0c;需要记录报告生成进度&#xff0c;进度字段jd初始化是0&#xff0c;每个文件生成成功进度加1&#xff0c;生成失败就把生成状态置为失败。 更新进度语句&…

Vue入门到关门之Vue项目工程化

一、创建Vue项目 1、安装node环境 官网下载&#xff0c;无脑下一步&#xff0c;注意别放c盘就行 Node.js — Run JavaScript Everywhere (nodejs.org) 需要两个命令 npm---->pipnode—>python 装完检查一下&#xff0c;hello world检测&#xff0c;退出crtlc 2、搭建vu…

Linux:浏览器访问网站的基本流程(优先级从先到后)

浏览器访问网站的基本流程&#xff08;优先级从先到后&#xff09; 首先查找浏览器是否存在该网站的访问缓存 其次查找本机的域名解析服务器 windows&#xff1a;C:\Windows\System32\drivers\etc\hostsLinux&#xff1a;/etc/hosts 使用外部的域名解析服务器解析&#xff…

逆向第一步 去掉debugger(无任何门槛小白可学习)

准备工具 1.ReRes 地址&#xff1a;ReRes 用法&#xff1a; 用法 2.nodepad 地址&#xff1a;nodepad 注意下载后缀为.x64.exe版本的 我这里下的npp.8.6.5.Installer.x64.exe 3给nodepad装上JSTool插件 下载 可省略下叙详细步骤点此链接直接下载 JSToolNpp 然后到导…

Go语言基本语法(三)指针

什么是指针 在Go语言中&#xff0c;"指针是一种存储变量内存地址的数据类型"&#xff0c;意味着指针本身是一个特殊的变量&#xff0c;它的值不是数据本身&#xff0c;而是另一个变量在计算机内存中的位置&#xff08;地址&#xff09;。形象地说&#xff0c;就像存…

Avalonia .NET构建Linux桌面应用

目录 &#x1f47b;前言 &#x1f4bb;安装Avalonia &#x1f4e6;创建项目 &#x1f4da;在win下运行 ​&#x1f511;打包发布​编辑 &#x1f4fb;在linux下运行 环境WIN10 VS2022 debian &#x1f47b;前言 Avalonia 是一个用于创建跨平台用户界面 (UI) 的开源框架…

C++——STL容器——vector

vector是STL容器的一种&#xff0c;和我们在数据结构中所学的顺序表结构相似&#xff0c;其使用和属性可以仿照顺序表的形式。vector的本质是封装了一个动态大小的数组&#xff0c;支持动态管理容量、数据的顺序存储以及随机访问。 1.前言说明 vector作为容器&#xff0c;应该…

对6个默认成员函数的总结

前言&#xff1a;本篇文章是对六大默认成员函数的自我总结&#xff0c;不适合刚入门的新人学习。适合想进一步深入了解六大默认成员函数的人学习。 1.构造函数&#xff1a;给对象初始化的函数&#xff0c;相当于之前写的Init函数。 构造函数的特性&#xff1a; 对内置类型不…

深度解析:人工智能作画算法的原理与技术

引言 在数字艺术的探索中&#xff0c;人工智能&#xff08;AI&#xff09;作画算法以其独特的创造性和艺术性引起了广泛的兴趣。这些算法不仅仅是简单的图像处理工具&#xff0c;它们背后蕴藏着复杂的神经网络和深度学习模型。本文将深入探讨AI作画算法的原理与技术&#xff0…

day15 学一下Tailwindcss(java转ts全栈/3r教室)

目前距离全栈差得最多的是前端&#xff0c;而对于前端主要是CSS一直不熟悉&#xff0c;觉得很复杂写起来总是不上道&#xff0c;所以特别关注下Tailwindcss吧&#xff0c;其他前端框架可以先放放&#xff0c;多说无益直接用tailwindcss做个页面试试 看下文档&#xff1a;Tailwi…

【LeetCode刷题记录】104. 二叉树的最大深度

104 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff…

HarmonyOS开发案例:【排行榜页面】

介绍 本课程使用声明式语法和组件化基础知识&#xff0c;搭建一个可刷新的排行榜页面。在排行榜页面中&#xff0c;使用循环渲染控制语法来实现列表数据渲染&#xff0c;使用Builder创建排行列表布局内容&#xff0c;使用装饰器State、Prop、Link来管理组件状态。最后我们点击…

基于python+django网易新闻+评论的舆情热点分析平台

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

回溯Backtracking Algorithm

目录 1) 入门例子 2) 全排列-Leetcode 46 3) 全排列II-Leetcode 47 4) 组合-Leetcode 77 5) 组合总和-Leetcode 39 6) 组合总和 II-Leetcode 40 7) 组合总和 III-Leetcode 216 8) N 皇后 Leetcode 51 9) 解数独-Leetcode37 10) 黄金矿工-Leetcode1219 其它题目 1) 入…