C++-封装unordered

news2024/11/19 15:12:47

本期我们来封装实现unordered系列,需要前置知识,没有看过哈希的建议先看看哈希,而且哈希的代码都在这里面,一会要用到

C++-哈希Hash-CSDN博客

目录

代码实现

迭代器

const迭代器

全部代码


代码实现

 首先我们要把V改为T,因为我们不知道他是k结构还是kv结构

//unordered_set.h
namespace bai {
	template<class K>
	class unordered_set
	{
	private:
		hash_bucket::HashTable<K, K> _ht;
	};
}
namespace bai {
	template<class K,class V>
	class unordered_map
	{
	private:
		hash_bucket::HashTable<K,pair<K,V>> _ht;
	};
}

然后我们把框架搭好

我们对照着看一下,如果是set,这里就是key,data就是key,如果是map,这里就是pair,data就是pair

接下来还要修改insert,find等等,这里就因为不知道是pair还是k的原因,所以我们需要再次来写KeyOfT

        struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

写完后别忘记加上 

bool Insert(const T& data)
		{
			KeyOfT kot;
			if (Find(kot(data)))
			{
				return false;
			}
			HashFunc hf;
			//负载因子到1时扩容
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);
				//遍历旧表,把节点迁下来挂到新表
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						//头插到新表
						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}
				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return true;
		}

		Node* Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						//头删
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				cur = cur->_next;
			}
		}

接着我们要修改insert,find和erase的代码,下面我们来看看修改了哪里,我们按顺序看

大概就是这些地方,具体的还要看上面的代码

我们简单测试一下,没有问题,下面我们来封装迭代器

迭代器

要实现迭代器,我们要想想,当前桶走完了,如何到下一个桶呢?也就是2号位的桶完了后如何到6号位

template<class T>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		Node* _node;

		HTiterator(Node* node)
			:_node(node)
		{}

		operator++()
		{
			if (_node->_next)
			{
				_node = _node->next;
			}
			else
			{

			}
		}
	};

我们先把框架写好,我们来看operator++,如果当前桶没有走完,就让他到下一个位置即可

如果我们知道当前位置是几号桶可以解决吗?也不行,因为我们还需要哈希表的对象,大家想一想,他的底层是一个指针数组,每一个指针又指向一个单链表,我们现在是需要在指针数组里移动

    template<class K, class T, class KeyOfT, class HashFunc>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		typedef HTiterator<K, T, KeyOfT, HashFunc> Self;

		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;
		HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Self& operator++()
		{
			if (_node->_next)
			{
				//当前桶还没完
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				//从下一个位置开始查找下一个不为空的桶
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}
				_node = nullptr;
			}
			return *this;
		}
	};

所以我们需要把整个哈希表传过来,传一个pht的指针,而且这里和之前一样,不一定是可以直接取模,需要再套一层hashfunc,此时我们就可以计算出当前是第几个桶

接着我们开始找下一个不为空的桶,我们先对hashi++,从下一个位置开始寻找,如果当前桶不为空,我们就break,否则++hashi,循环结束后是有两种情况的,一种是可以找到,我们要让it指向下一个桶,另一种是我们已经找完了整个表,已经走到结尾了,我们让nullptr充当end的位置

另外这里可能会有人有疑问,为什么这里没有加typename? 只有不能区分静态变量和类型时才需要加

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

		T* operator->()
		{
			return &_node->_data;
		}        
        bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
        bool operator==(const Self& s)
		{
			return _node == s._node;
		}

我们顺手再把这几个也写一下

        typedef HTiterator<K, T, KeyOfT, HashFunc> iterator;
		iterator begin()
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return iterator(cur,this);
				}
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

接着我们在HashTable里加上begin和end,我们构造迭代器时第一个传cur,找不到传nullptr,第二个传this即可,this就是哈希表的指针

下面我们再套一层,在unordered里封装一层

注意,这里就需要加typename了,我们之前封装map和set时也是一样的

下面我们要开始解决各种问题了 

这里是一个相互依赖的问题

首先我们在哈希表里调用迭代器 ,迭代器在前,没有问题

但是我们在迭代器里又用了哈希表,这里谁在前谁在后都不行,不过还好这里用的是哈希表的指针,如果是对象就不好解决了

这里我们就要用到前置声明了,告诉编译器哈希表我们是有定义的,只不过可能在后面,先不要着急

 

此时问题就解决了 

我们调用一下迭代器,再次编译,出现了这样的错误,我们仔细看可以发现,这里是私有的问题

这里在类外边是不能访问私有的 ,所以这里可以写一个gettable,或者使用友元

是迭代器要访问哈希表,所以迭代器是哈希表的友元,类模板的友元需要带上模板参数 

此时我们的迭代器就可以跑起来了 

接下来我们需要解决可以修改的问题,也就是要实现const迭代器了

const迭代器

和之前一样,要实现const迭代器我们需要传T*和T&

于是我们修改迭代器的模板参数和self 

operator*和箭头的返回参数也要修改

友元声明也需要修改 

        const_iterator begin() const
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return const_iterator(cur, this);
				}
			}
			return const_iterator(nullptr, this);
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

然后要在HashTable里加上begin和end

接着我们对外层的set封装,无论是iterator还是const_iterator,它们其实都是const_iterator,而且只提供了const版本的begin和end,和库里面是一样的,这里的begin和end的返回值无论是const_iterator还是iterator都是可以的,因为普通对象是可以调用const成员函数的

此时我们的第一个问题就解决了

接着我们编译,发现这里编译不通过,不仅仅是这里,上面也是编译不通过的,这里报错是无构造函数可以接受源类型

我们来看,这里的const修饰的谁?

修饰的是*this

所以这里this的类型应该是这样的

我们画出来就是这样的,这里权限放大,是传不过去的 ,我们之前写的const迭代器没有遇到这样的问题,因为以前的const迭代器是不需要传哈希表的,而只需要传一个节点的指针过去即可

我们来看这种方法,我们重载了一个构造函数,并且直接把_pht改为了const

 对于原来的构造函数,pht是const变为非const,是权限的缩小

那此时我们不要第一个构造,只要第二个可以吗?

也是可以的,普通迭代器传过来只有const,权限缩小

我们来看库里面是怎么解决的,首先是普通的迭代器 

这里有一个

 库里面写了两个迭代器,用两个迭代器类来实现

这里还都写成const了,相当于单独写了一个类来解决

我们的方法和库里面的方法,这两种方法都可以解决

map的话和以前一样,改为const K

 修改这些地方

 这样就解决了

下面我们来修改返回值

先修改一下find的返回值

然后是insert的 

然后我们把map和set封装的insert也改一下,然后就是set这里的问题了

这里看起来是这样的

但是我们看这里

 但是它们是不同区域的代码

 set不允许修改key,所以选择用const迭代器替代普通迭代器

所以这里的两个pair是不一样的,pair是一个类模板,实例化为两个不一样的类,是不一样的类型

也就是说上面的pair是这样的,所以会报错

map为什么没报错?

 因为map的iterator就是iterator

注意,set这里的问题和权限缩小之类无关,只有指针和引用才有权限缩小的概念,这里是类模板实例化不同的模板参数,导致它们是不同的类型

我们之前在封装map和set就讲过了,这是库里面list的实现办法,不太清楚的各位可以去我往期的map和set实现里再看一看,我把链接放在下面

map和set模拟实现-CSDN博客

下面这个圈主的函数,如果是普通迭代器,这里就是拷贝构造,是const迭代器时,是构造函数,是支持普通迭代器转换成const迭代器的构造,这是一个有双重意义的函数

我们来用这个举例子,我们想用aa1构造aa3

我们要加这样一个构造,但是这里报错了一个私有

原因是上面这里是A<T,Ref>

这里是A<T,T&>

当这里Ref也是T&时是同一个类,当Ref是const时就不是同一个类了

我们看为什么库里面不报错呢?

因为库里面是struct

我们去掉private就好了,这些都是细节,大家需要注意

现在回到我们的哈希表里

我们也加上这样一个构造

接着我们再次编译,发现了几个错误

首先是我们把这里改为it(这里之前是写错了)

然后是find返回时不能返回nullptr,这里返回end(),接着编译就可以通过了

我们上次在这里写的和这里是不一样的

这里我们是先接收pair,再用pair去调用它的构造

这样写是没问题的,这里相当于把两个参数提取了出来,单独调用pair构造,这里的ret.first是一个普通迭代器,在pair的初始化列表里,初始化列表对自定义类型会调用构造

而这里屏蔽的(我们之前写的),是编译器的不严格检查,如果检查的严格一点,是不通过的

首先这里调用pair的构造

我们上面的a<int>和a<const int>都不是同一个类型,这里也是一样的

所以我们把iterator取出来

 然后直接调用它的构造,就会走它的初始化列表,初始化列表对于自定义类型就会调用它的构造

虽然这里两种方法都可以编译通过,但是我们还是建议使用方法2,不然换一个编译器可能就不通过了 

此时我们的问题就解决了

我们上面那么多事情,都是因为它 

insert要返回pair,就引发了普通迭代器转换const迭代器的问题,为什么insert要返回pair呢?

因为我们要实现operator[ ] ,这里都是相关联的

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

为什么我们这里接收时可以用iterator?

因为map的iterator就是iterator,和set不一样

此时我们就可以使用[ ] 了,和库里面一样

全部代码

#include"Hash.h"
//unordered_set.h
namespace bai {
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		const_iterator begin() const
		{
			return _ht.begin();
		}
		const_iterator end() const
		{
			return _ht.end();
		}
		pair<const_iterator, bool> insert(const K& key)
		{
			//return _ht.Insert(key);
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
			return pair<const_iterator, bool>(ret.first, ret.second);
		}
		
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}
#include"Hash.h"
//unordered_map.h
namespace bai {
	template<class K,class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::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<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;
		}
	private:
		hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;
	};
}
#pragma once
#include<vector>
#include<iostream>
using namespace std;
//Hash.h
template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& str)
	{
		//BKDR
		size_t hash = 0;
		for (auto ch : str)
		{
			hash *= 131;
			hash += ch;
		}
		return hash;
	}
};

namespace open_address {
	enum STATE //状态
	{
		EXIST,
		EMPTY,
		DELETE
	};

	template<class K, class V>
	struct HashData
	{
		pair<K, V> _kv;
		STATE _state = EMPTY;
	};

	//struct stringHashFunc
	//{
	//	size_t operator()(const string& str)
	//	{
	//		return str[0];
	//	}
	//};

	template<class K, class V, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
	public:
		HashTable()
		{
			_table.resize(10);
		}
		bool Insert(const pair<K, V>& kv)
		{
			if(Find(kv.first))
			{
				return false;
			}
			//扩容
			//if ((double)_n / _table.size() >= 0.7)
			if (_n * 10 / _table.size() >= 7)
			{
				size_t newSize = _table.size() * 2;
				//重新映射
				HashTable<K, V, HashFunc> newHT;
				newHT._table.resize(newSize);
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_table[i]._state == EXIST)
					{
						newHT.Insert(_table[i]._kv);
					}
				}
				_table.swap(newHT._table);
			}
			//线性探测
			HashFunc hf;
			size_t hashi = hf(kv.first) % _table.size();
			while (_table[hashi]._state == EXIST)
			{
				++hashi;
				hashi %= _table.size();//防止越界,这样可以回到数组开头
			}
			_table[hashi]._kv = kv;
			_table[hashi]._state = EXIST;
			++_n;
			return true;
		}
		HashData<const K, V>* Find(const K& key)
		{
			//线性探测
			HashFunc hf;
			size_t hashi = hf(key) % _table.size();
			while (_table[hashi]._state != EMPTY)
			{
				if (_table[hashi]._state == EXIST
					&& _table[hashi]._kv.first == key)
				{
					return (HashData<const K, V>*) & _table[hashi];
				}
				++hashi;
				hashi %= _table.size();
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			HashData<const K, V>* ret = Find(key);
			if (ret)
			{
				ret->_state = DELETE;
				--_n;
				return true;
			}
			return false;
		}
	private:
		vector<HashData<K, V>> _table;
		size_t _n; //存储有效数据的个数
	};
}

namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

	//前置声明
	template<class K, class T,class KeyOfT, class HashFunc>
	class HashTable;

	template<class K, class T, class Ptr, class Ref,class KeyOfT, class HashFunc>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		typedef HTiterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef HTiterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;

		Node* _node;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;

		/*HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}*/

		HTiterator(Node* node,const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		//普通迭代器时是拷贝构造
		//cosnt迭代器时是构造
		HTiterator(const Iterator& it)
			:_node(it._node)
			, _pht(it._pht)
		{}

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

		Ptr operator->()
		{
			return &_node->_data;
		}
		Self& operator++()
		{
			if (_node->_next)
			{
				//当前桶还没完
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				//从下一个位置开始查找下一个不为空的桶
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}
				_node = nullptr;
			}
			return *this;
		}

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

	//set -> hash_bucket::HashTable<K, K> _ht;
	//map -> hash_bucket::HashTable<K, pair<K,V>> _ht;
	template<class K,class T,class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		//友元声明
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc >
		friend struct HTiterator;
	public:
		typedef HTiterator<K, T,T*,T&, KeyOfT, HashFunc> iterator;
		typedef	HTiterator<K, T,const T*,const T&, KeyOfT, HashFunc> const_iterator;
		
		iterator begin()
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return iterator(cur,this);
				}
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

		const_iterator begin() const
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return const_iterator(cur, this);
				}
			}
			return const_iterator(nullptr, this);
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

		HashTable()
		{
			_table.resize(10,nullptr);
		}

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

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,false);
			}
			HashFunc hf;
			//负载因子到1时扩容
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);
				//遍历旧表,把节点迁下来挂到新表
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						//头插到新表
						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}
				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return make_pair(iterator(newnode,this),true);
		}

		iterator Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				cur = cur->_next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						//头删
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			--_n;
			return false;
		}

		void Print()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				printf("[%d]->", i);
				Node* cur = _table[i];
				while (cur)
				{
					cout << cur->_kv.first << ":" <<cur->_kv.second <<"->";
					cur = cur->_next;
				}
				printf("NULL\n");
			}
			cout << endl;
		}
	private:
		vector<Node*> _table; //指针数组
		size_t _n = 0; //有效数据
	};
}

以上即为本期全部内容,希望大家可以有所收获

如有错误,还请指正

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

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

相关文章

【回顾一下Docker的基本用法】

文章目录 回顾一下Docker的基本用法1.初识Docker1.1.什么是Docker1.1.1.应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结 1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.小结 1.4.…

计算摄像技术02 - 颜色空间

一些计算摄像技术知识内容的整理&#xff1a;颜色视觉与感知特性、颜色空间和基于彩色滤镜阵列的彩色感知。 文章目录 一、颜色视觉与感知特性 &#xff08;1&#xff09;色调 &#xff08;2&#xff09;饱和度 &#xff08;3&#xff09;明度 二、颜色空间 &#xff08;1&…

2023版 STM32实战7 通用同步/异步收发器(串口)F103/F407

串口简介和习惯 -1-通用同步异步收发器 (USART) 能够灵活地与外部设备进行全双工数据交换&#xff0c;满足外部设备对工业标准 NRZ 异步串行数据格式的要求。 -2-硬件流控制一般是关闭的 -3-波特率指单位时间传输bit个数 -4-数据位一般是8位 -5-一般无校验位 编写代码思路 -1-参…

位图/布隆过滤器

一、位图 1.1位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&#xff0c;数据无重复的场景。通常是用来判断某个数据存不存在的。 1.2位图的实现 template<size_t N>class bitset{public:bitset(){//需要N个比特位&#xff0c;…

SpringBoot客户端实现分片、断点上传

客户端的分片上传和断点续传是指将一个文件拆分为多个分片&#xff0c;在网络不稳定或上传中断后&#xff0c;可以从中断处继续上传而不需要重新上传整个文件。 当需要上传大型文件时&#xff0c;客户端的分片上传和断点续传是一种常用的技术。它可以提高上传效率并减少网络中断…

【计算机网络面试题(62道)】

文章目录 计算机网络面试题&#xff08;62道&#xff09;基础1.**说下计算机网络体系结构2.说一下每一层对应的网络协议有哪些&#xff1f;3.那么数据在各层之间是怎么传输的呢&#xff1f; 网络综合4.**从浏览器地址栏输入 url 到显示主页的过程&#xff1f;5.说说 DNS 的解析…

Vscode配置C#编程环境(win10)

目录 1、安装好Vscode 2、下载安装.NetCore SDK 3、配置C#环境 3.1 打开Vscode并下载扩展 3.2 Vscode中打开文件夹并配置环境 3.3 调试运行 1、安装好Vscode 2、下载安装.NetCore SDK 官网如下&#xff0c;下载完成后双击打开一路走到底就行.NetCore SDK官网 软件显示安…

利用freesurfer6进行海马分割的环境配置和步骤,以及获取海马体积

利用freesurfer6进行海马分割的环境配置和步骤 Matlab Runtime 安装1. 运行recon-all&#xff1a;2. 利用 recon-all -s subj -hippocampal-subfields-T1 进行海马分割3. 结束后需要在/$SUBJECTS_DIR/subject/的文件夹/mri路径下输入下面的代码查看分割情况4. 在文件SUBJECTS_D…

连续波雷达相关内容简介

连续波雷达(CW雷达)连续发射高频信号。回波信号被永久接收和处理。 一、未调制连续波雷达 未调制的连续波雷达除了振荡的相位之外没有其他时间参考。它只能用于测量小于波长的距离。以上的一切都极为含糊。因此,它只能用作多普勒雷达或控制已知距离的恒定性。 二、调频连…

VUE3照本宣科——eslint与prettier

VUE3照本宣科——eslint与prettier VUE3照本宣科系列导航 前言一、eslint1.配置文件2.配置规则3.忽略错误 二、prettier三、总结 VUE3照本宣科系列导航 1.VUE3照本宣科——认识VUE3 2.VUE3照本宣科——应用实例API与setup 3.VUE3照本宣科——响应式与生命周期钩子 4.VUE3照本宣…

关于Go语言的底层,Channel

1.Channel 介绍一下Channel&#xff08;有缓冲和无缓冲&#xff09; Go 语言中&#xff0c;不要通过共享内存来通信&#xff0c;而要通过通信来实现内存共享。Go 的CSP(Communicating Sequential Process)并发模型&#xff0c;中文可以叫做通信顺序进程&#xff0c;是通过 gor…

剑指offer——JZ54 二叉树搜索树的第k个节点 解题思路与具体代码【C++】

一、题目描述与要求 二叉搜索树的第k个节点_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定一棵结点数为n 二叉搜索树&#xff0c;请找出其中的第 k 小的TreeNode结点值。 1.返回第k小的节点值即可 2.不能查找的情况&#xff0c;如二叉树为空&#xff0c;则返回-1&#xf…

Sql server 使用DBCC Shrinkfile 收缩日志文件

磁盘空间有限&#xff0c;需要收缩日志文件释放空间。 数据库名称上右击属性->文件,逻辑名称日志文件默认名称为“_log”结尾。 alter database 数据库 set recovery simple dbcc shrinkfile(XXX_log,2,truncateonly) alter database 数据库 set recovery full

webp批量转换为png、jpg工具

webp批量转换为png、jpg工具 链接&#xff1a;https://pan.baidu.com/s/1NZArgbiF88_qBbAIiUR4qQ 提取码&#xff1a;2sqs –来自百度网盘超级会员V5的分享

IO流 之 转换流( InputStreamReader 字节输入转换流 和 OutputStreamWriter 字节输出转换流)

当文本文件和代码的编码不一致时&#xff0c;使用字符流会导致读取出来的文字乱码。如下&#xff1a; 读取文件的编码时GBK编码。 代码的编码时UTF-8的编码。 程序运行出来中文则是乱码。 这里就要使用到转换流了。 字节转换流 InputStreamReader 字节输入转换流 使用步骤…

一文搞懂时间序列ARIMA模型

文章目录 1 ARIMA的定义2 差分(differencing)2.1 Order&#xff1a;差分的阶数2.2 Lag&#xff1a;差分的滞后2.3 滞后运算/滞后算子/延迟算子2.4 关于差分的两个误解 3 ARIMA的平稳性4 ACF与PACF5 时序模型的选择与评估5.1 超参数p、q、d的确定5.2 时间序列的评估指标 1 ARIMA…

Linux自用笔记

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Linux相关 ✨特色专栏&#xff1a; My…

码支付添加银行转账功能,手动回调

在后台中通道列表先加上 路径&#xff1a;【后台】 - > 【通道管理】 - > 【新增】 总后台页面通道类型加上支付类型yh_pay和通道yhzz添加后存到数据库admin_chanel上 修改商户页面的【新增通道】页面 /view/index/channel/index.html 添加一个支付通道 <option…

【算法训练-数组 三】【数组矩阵】螺旋矩阵、旋转图像、搜索二维矩阵

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是螺旋矩阵&#xff0c;使用【二维数组】这个基本的数据结构来实现 螺旋矩阵【EASY】 二维数组的结构特性入手 题干 解题思路 根据题目示例 mat…

【DevOps】搭建你的第一个 Docker 应用栈

搭建你的第一个 Docker 应用栈 1.Docker 集群部署2.第一个 Hello World2.1 获取应用栈各节点所需镜像2.2 应用栈容器节点互联2.3 应用栈容器节点启动2.4 应用栈容器节点的配置2.4.1 Redis Master 主数据库容器节点的配置2.4.2 Redis Slave 从数据库容器节点的配置2.4.3 Redis 数…