C++语法(21)---- 模拟map和set

news2024/11/13 15:11:45

 (1条消息) C++语法(20)---- 模拟红黑树_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130296772?spm=1001.2014.3001.5501

目录

1.stl中的设计思想

2.模拟set和map

1.set和map的类

2.BRTree的仿函数实现

3.红黑树的迭代器

4.set和map迭代器

5.红黑树的insert重新实现

6.map的[]实现

7.set的insert

3.实现代码


1.stl中的设计思想

1.map和set的实现是基于红黑树

2.set中的插入的元素是key,基于查找的元素也是key  --- RBTree<K,K>

3.map中的插入的元素是pair<K,V>,基于查找的元素也是key  --- RBTree<K,pair<K,V>>

2.模拟set和map

1.set和map的类

set

class Set
{
public:
private:
	BRTree<K, K> _t;
};

map

class Map
{
public:
private:
	BRTree<K, pair<const K, V>> _t;
};

此时两个类的初始构建就有了,但是有一个需要注意的点是,BRTree不知道怎么比较传入的节点,那就不能称之为排序,所以我们需要一个仿函数,接收比较的数据,通过这个数据可以用于比较数据大小。

为什么不用pair自带的比较?因为pair的比较是先比较first的值再比较second的值。但是我们想要其比较的只是Key,所以不符合。

2.BRTree的仿函数实现

template<class K, class T,class KeyOfT>
class BRTree
{
	Node* _root = nullptr;
};

这个仿函数是返回set或者map比较的元素是什么。

对于set,传入的就是key

template<class K>
class Set
{
public:
	struct SetKeyOfT
	{
		const K& operator()(const K& k)
		{
			return k;
		}
private:
	BRTree<K, K, SetKeyOfT> _t;
};

对于map,传入的就是pair的first

template<class K, class V>
class Map
{
public:
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
private:
	BRTree<K, pair<const K, V>, MapKeyOfT> _t;
};

对于红黑树

用传下来的仿函数,insert调用仿函数传出的节点进行判断数据大小就完成了

3.红黑树的迭代器

其中重要的实现是++和--操作

++操作:如果右边存在,我们要找右树的最小节点(最左节点);如果右边不存在,此时不能返回它的父亲节点,因为访问到右树意味着父亲节点已经被访问了,我们要找到是孩子是父亲的左边的那个祖先

--操作:与++操作很相似,如果左边存在,我们要找左树的最大节点(最右节点);如果左边不存在,此时不能返回它的父亲节点,因为访问到左树意味着父亲节点已经被访问了,我们要找到是孩子是父亲的右边的那个祖先

template <class T, class Ref, class Ptr>
struct __BRTreeIterator
{
	typedef BRTreeNode <T> Node;
	Node* _node;
	typedef __BRTreeIterator<T, Ref, Ptr> Self;

	/*-------------------------------------------------------------------------------------------------------*/
	typedef __BRTreeIterator<T, T&, T*> iterator;
	/*-------------------------------------------------------------------------------------------------------*/

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

	/*-------------------------------------------------------------------------------------------------------*/
	//普通迭代器时,是拷贝构造
	//const迭代器,指出迭代器构造const迭代器
	__BRTreeIterator(const iterator& s)
		:_node(s._node)
	{}
	/*-------------------------------------------------------------------------------------------------------*/

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

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

	Self operator++() 
	{
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}

			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self operator--()
	{
		if (_node->_left)
		{
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}

			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

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

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

4.set和map迭代器

set

注意:设计set时,它的迭代器不能修改里面的内容,因为搜索二叉树的结构可能会被破坏

因此set的普通迭代器和const迭代器都是红黑树的const迭代器构造的。

因此begin和end实现时,需要把this指针变为const型,不然传入会被权限放大

typedef typename BRTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename BRTree<K, K, SetKeyOfT>::const_iterator const_iterator;

iterator begin() const
{
	return _t.begin();
}

iterator end() const
{
	return _t.end();
}

map 

注意:设计map时,它的迭代器需要修改里面的内容,多少也不能破坏搜索二叉树的结构

所以我们传入的pair中key是const类型,这样就保证key不能被修改

那么迭代器就可以有普通和const型

typedef typename BRTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename BRTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

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

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

5.红黑树的insert重新实现

stl中的红黑树实现了[]来统计次数,[]调用的是insert,而insert函数的返回值是一个pair类型。pair存储两个东西,一个是迭代器,一个是bool类型的数。insert插入成功,返回插入的新迭代器位置,bool类型为true;insert插入失败返回的迭代器是找到对应数的迭代器,bool是false

	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = Black;
			return make_pair(iterator(_root), true);
		}
		KeyOfT kot;
		//父子节点确定插入的位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return make_pair(iterator(cur), false);
		}

		//走到这cur就是要插入的位置
		//cur要连接parent,parent也要连接cur---判断靠kv的大小
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(cur->_data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == Red)
		{
			Node* grandparent = parent->_parent;
			//parent分在grandparent左右
			if (grandparent->_left == parent)
			{
				//关键是看uncle节点不存在/红色/黑色的情况
				Node* uncle = grandparent->_right;
				//1.uncle红
				//parent和uncle变黑,grandparent变红
				//grandparent变红需要往上判断
				if (uncle && uncle->_col == Red)
				{
					grandparent->_col = Red;
					parent->_col = uncle->_col = Black;

					cur = grandparent;
					parent = cur->_parent;
				}
				else  //uncle不存在/黑色
				{
					//2.cur也是parent的左边,uncle不存在/黑色
					//右旋grandparents,parent变黑,
					if (cur == parent->_left)
					{
						_RotateR(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					//3.cur是parent的右边,uncle不存在/黑色
					//左旋parent再右旋grandparents,cur变黑,grandparents变红
					else
					{
						_RotateL(parent);
						_RotateR(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}

					//抽象树的头被设置为黑色,对上面没有影响,所以不需要进行循环
					break;
				}

			}
			else
			{
				Node* uncle = grandparent->_left;

				//1.uncle红
				//parent和uncle变黑,grandparent变红
				//grandparent变红需要往上判断
				if (uncle && uncle->_col == Red)
				{
					grandparent->_col = Red;
					parent->_col = uncle->_col = Black;

					cur = grandparent;
					parent = cur->_parent;
				}
				else  //uncle不存在/黑色
				{
					//2.cur也是parent的右边,uncle不存在/黑色
					//左旋grandparents,parent变黑,
					if (cur == parent->_right)
					{
						_RotateL(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					//3.cur是parent的右边,uncle不存在/黑色
					//右旋parent再左旋grandparents,cur变黑,grandparents变红
					else
					{
						_RotateR(parent);
						_RotateL(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}

					//抽象树的头被设置为黑色,对上面没有影响,所以不需要进行循环
					break;
				}
			}
		}
		_root->_col = Black;
		return make_pair(iterator(newnode), true);
	}

6.map的[]实现

如果成功返回新迭代器位置

失败返回找到的迭代器位置

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

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

7.set的insert

set的普通迭代器是红黑树的const迭代器重命名的,但是insert调用的是红黑树的普通迭代器,如果直接insert红黑树的,普通迭代器和const迭代器类型是无法相互转换。所以我们需要调用红黑树原生普通迭代器。

不过,在stl中都实现了普通迭代器与const迭代器的转换

实现的思路就是:

1.在迭代器实现时重命名两个迭代器,一个是<T,R,P>,一个是<T,T*,T&>

2.在类中的普通迭代器,那么这两个都是一样的,是普通迭代器

3.在类中传入const的数据,那么左边是const迭代器,右边是普通迭代器

4.在迭代器的构造中,实现两个构造函数

pair<iterator, bool> Insert(const K& k)
{
	pair<typename BRTree<K, K, SetKeyOfT>::iterator, bool>ret = _t.Insert(k);
	//1.取红黑树里的普通迭代器,因为set的迭代器都是const变过来的
	//2.类模板中分不清是迭代器还是成员函数,所以需要变成typename
	return pair<iterator, bool>(ret.first, ret.second);
	//普通迭代器可以构造成const迭代器,所以需要调用红黑树的const迭代器构造
}

3.实现代码

RBTrss.hpp

#pragma once
#include<iostream>
#include<assert.h>
#include <stdlib.h>
#include<time.h>
using namespace std;

using namespace std;

enum Color
{
	Black,
	Red,
};

template <class T>
struct BRTreeNode
{
	T _data;
	BRTreeNode<T>* _left;
	BRTreeNode<T>* _right;
	BRTreeNode<T>* _parent;
	Color _col;

	BRTreeNode(const T& data)
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(Red)
	{}
};

template <class T, class Ref, class Ptr>
struct __BRTreeIterator
{
	typedef BRTreeNode <T> Node;
	Node* _node;
	typedef __BRTreeIterator<T, Ref, Ptr> Self;

	/*-------------------------------------------------------------------------------------------------------*/
	typedef __BRTreeIterator<T, T&, T*> iterator;
	/*-------------------------------------------------------------------------------------------------------*/

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

	/*-------------------------------------------------------------------------------------------------------*/
	//普通迭代器时,是拷贝构造
	//const迭代器,指出迭代器构造const迭代器
	__BRTreeIterator(const iterator& s)
		:_node(s._node)
	{}
	/*-------------------------------------------------------------------------------------------------------*/

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

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

	Self operator++() 
	{
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}

			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self operator--()
	{
		if (_node->_left)
		{
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}

			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

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

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

/*-------------------------------------------------------------------------------------------------------*/

template<class K, class T,class KeyOfT>
class BRTree
{
public:
	typedef BRTreeNode<T> Node;
	typedef __BRTreeIterator<T, T&, T*> iterator;
	typedef __BRTreeIterator<T,const T&,const T*> const_iterator;

	iterator begin() 
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator begin() const
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return const_iterator(left);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = Black;
			return make_pair(iterator(_root), true);
		}
		KeyOfT kot;
		//父子节点确定插入的位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return make_pair(iterator(cur), false);
		}

		//走到这cur就是要插入的位置
		//cur要连接parent,parent也要连接cur---判断靠kv的大小
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(cur->_data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == Red)
		{
			Node* grandparent = parent->_parent;
			//parent分在grandparent左右
			if (grandparent->_left == parent)
			{
				//关键是看uncle节点不存在/红色/黑色的情况
				Node* uncle = grandparent->_right;
				//1.uncle红
				//parent和uncle变黑,grandparent变红
				//grandparent变红需要往上判断
				if (uncle && uncle->_col == Red)
				{
					grandparent->_col = Red;
					parent->_col = uncle->_col = Black;

					cur = grandparent;
					parent = cur->_parent;
				}
				else  //uncle不存在/黑色
				{
					//2.cur也是parent的左边,uncle不存在/黑色
					//右旋grandparents,parent变黑,
					if (cur == parent->_left)
					{
						_RotateR(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					//3.cur是parent的右边,uncle不存在/黑色
					//左旋parent再右旋grandparents,cur变黑,grandparents变红
					else
					{
						_RotateL(parent);
						_RotateR(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}

					//抽象树的头被设置为黑色,对上面没有影响,所以不需要进行循环
					break;
				}

			}
			else
			{
				Node* uncle = grandparent->_left;

				//1.uncle红
				//parent和uncle变黑,grandparent变红
				//grandparent变红需要往上判断
				if (uncle && uncle->_col == Red)
				{
					grandparent->_col = Red;
					parent->_col = uncle->_col = Black;

					cur = grandparent;
					parent = cur->_parent;
				}
				else  //uncle不存在/黑色
				{
					//2.cur也是parent的右边,uncle不存在/黑色
					//左旋grandparents,parent变黑,
					if (cur == parent->_right)
					{
						_RotateL(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					//3.cur是parent的右边,uncle不存在/黑色
					//右旋parent再左旋grandparents,cur变黑,grandparents变红
					else
					{
						_RotateR(parent);
						_RotateL(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}

					//抽象树的头被设置为黑色,对上面没有影响,所以不需要进行循环
					break;
				}
			}
		}
		_root->_col = Black;
		return make_pair(iterator(newnode), true);
	}

	void Print()
	{
		_Print(_root);
		cout << endl;
	}

	bool Inspect()
	{
		return _Inspect(_root);
	}

private:
	bool _Find(Node* root, const T& data)
	{
		if (root == nullptr)
			return false;
		if (root->_data == data)
			return true;
		return _Find(root->_left) || _Find(root->_right);
	}

	bool check(Node* root, size_t& reference, size_t num)
	{
		if (root == nullptr)
		{
			if (num != reference)
			{
				cout << "路径长度有问题" << endl;
				return false;
			}

			return true;
		}


		if (root->_col == Red && root->_parent && root->_parent->_col == Red)
		{
			cout << "节点连续红色" << endl;
			return false;
		}

		if (root->_col == Black)
			num++;

		return check(root->_left, reference, num) && check(root->_right, reference, num);
	}

	bool _Inspect(Node* root)
	{
		//空树也是红黑树
		if (_root == nullptr)
			return true;

		//检测根节点是否为黑色
		if (_root->_col != Black)
		{
			cout << "根节点是红色的" << endl;
			return false;
		}

		size_t leftNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == Black)
				leftNum++;
			cur = cur->_left;
		}
		//检测所有路径黑色节点的数量是否一样
		//检测相邻节点是不是都是红色的
		return check(_root, leftNum, 0);
	}

	void _Print(Node*& cur)
	{
		if (cur == nullptr)
			return;
		_Print(cur->_left);
		cout << cur->_kv.first << " ";
		_Print(cur->_right);
	}

	void _RotateL(Node*& parent)
	{
		Node* pparent = parent->_parent;
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		if (pparent == nullptr)
		{
			_root = SubR;
			SubR->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = SubR;
			else
				pparent->_right = SubR;
			SubR->_parent = pparent;
		}
		parent->_parent = SubR;
		SubR->_left = parent;
		parent->_right = SubRL;
		if (SubRL != nullptr)
			SubRL->_parent = parent;
	}

	void _RotateR(Node*& parent)
	{
		Node* pparent = parent->_parent;
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		if (pparent == nullptr)
		{
			_root = SubL;
			SubL->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = SubL;
			else
				pparent->_right = SubL;
			SubL->_parent = pparent;
		}
		parent->_parent = SubL;
		SubL->_right = parent;
		parent->_left = SubLR;
		if (SubLR != nullptr)
			SubLR->_parent = parent;
	}

	Node* _root = nullptr;
};

map.cc

#pragma once
#include "BRTree.h"
#include <map>

namespace MY
{
	template<class K, class V>
	class Map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

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

		typedef typename BRTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename BRTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

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

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

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

	private:
		BRTree<K, pair<const K, V>, MapKeyOfT> _t;
	};

	void MapText()
	{
		Map<int, int> m;
		//int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		//int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		int arr[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
		for (int e : arr)
		{
			m.Insert(make_pair(e, e));
		}
		Map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << " ";
			++it;
		}
		cout << endl;
	}
}

set.cc

#pragma once
#include "BRTree.h"

namespace MY{
	template<class K>
	class Set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}

			bool Insert(const K& k)
			{
				return _t.Insert(k);
			}
		};
		typedef typename BRTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename BRTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		pair<iterator, bool> Insert(const K& k)
		{
			pair<typename BRTree<K, K, SetKeyOfT>::iterator, bool>ret = _t.Insert(k);
			//1.取红黑树里的普通迭代器,因为set的迭代器都是const变过来的
			//2.类模板中分不清是迭代器还是成员函数,所以需要变成typename
			return pair<iterator, bool>(ret.first, ret.second);
			//普通迭代器可以构造成const迭代器,所以需要调用红黑树的const迭代器构造
		}

		iterator begin() const
		{
			return _t.begin();
		}

		iterator end() const
		{
			return _t.end();
		}

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

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

	private:
		BRTree<K, K, SetKeyOfT> _t;
	
	};
	void SetText()
	{
		Set<int> s;
		//int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		//int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		int arr[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
		for (int e : arr)
		{
			s.Insert(e);
		}
		Set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
	}
}

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

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

相关文章

C语言力扣简单题-两数之和

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 两数之和 题目&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和…

从数据处理到人工智能(常用库的介绍)

Python库之数据分析 ​​​​​​​​​​​​ 可以这么理解pandas通过扩展了对一维数据和二维数据的一种表示&#xff0c;因而能够形成更高层对数据的操作&#xff0c;简化数据分析的运行 Python库之数据可视化 Matplotlib — Visualization with Python seaborn: statistic…

DNS基础:通过dig命令理解DNS域名解析中的A记录,AAAA记录,CNAME记录,MX记录,NS记录

参考博文&#xff1a;https://blog.csdn.net/zxl1990_ok/article/details/125432123 目录 参考资料DNS简介查询过程直接显示DNS寻址结果向特定DNS服务器寻址查询A记录查询AAAA记录CNAME记录MX记录NS记录PTR记录SOA记录查看DNS服务器的主从关系 参考资料 http://ruanyifeng.com…

C++ Primer阅读笔记--异常处理机制

目录 1--异常处理机制 2--throw表达式 3--try语句块 4--标准异常类 5--代码实例 1--异常处理机制 异常处理机制为程序中的异常检测和异常处理这两部分的协作提供支持&#xff1b;在 C 语言中&#xff0c;异常处理包括&#xff1a;throw表达式、try语句块和异常类&#xff…

线程池【Linux】

文章目录 1. 引入2. 应用3. 实现封装线程封装线程池线程函数生产消费逻辑互斥锁条件变量线程函数主线程测试1 4. 优化5. 日志日志的重要性实现日志级别提取参数stdarg.h 头文件日志文件 懒汉实现单例模式什么是懒汉模式什么是单例模式实现 1. 引入 线程池是一种池化技术&#…

Vue后台管理系统-前端登录设计

在做后台管理系统时&#xff0c;登录模块是必不可少的&#xff0c;访模块看似简单&#xff0c;在开发涉及到许多细节&#xff0c;一般来说主要有以下这些&#xff1a; 1. 登录输入的信息要进行正则校验&#xff1b; 2. 密码输入要可以查看明文&#xff1b; 3. 密码输入时要对…

JavaWeb——IP协议的相关特性

目录 一、IP协议 1、地址管理 &#xff08;1&#xff09;、动态分配IP地址 &#xff08;2&#xff09;、NAT机制 &#xff08;3&#xff09;、IPv6协议 2、路由选择 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、原理 二、路由器 三、IP地址的组成 1…

Postman登录注册指导

在做后端开发的小伙伴经常需要测试自己写的接口是否可以用 这时Postman就是一个很好的选择 如果您还没有下载 可以参考我的文章 API接口调试工具Postman下载安装步骤 安装好之后 我们就需要处理 它的一个注册和登录 我们点击右上角 这里就有一个创建一个账户 我们点击一下 …

CF - Playing in a Casino

题意&#xff1a;一个数的幸运值计算规则是用里面最大的数字-最小的数字所得值 给出一个范围&#xff0c;求这个范围里幸运值最大的是哪个数 解&#xff1a; 这道题相对来说就很简单了&#xff0c;注意数值限制范围在1道1e6&#xff0c;虽然这道题确实暴力就能做出来&#xf…

手把手教你用几行代码给winform多个控件(数量无上限)赋值

前言&#xff1a; 我们在开发winform程序的过程中&#xff0c;经常会遇到这样一个场景&#xff0c;我们设计的界面&#xff0c;比如主窗体有一百多个TextBox&#xff0c;然后初始化的时候要对这个一百多个TextBox的Text属性赋值&#xff0c;比如赋个1&#xff0c;如果是winfor…

CAD DLL 15 crack增加了对SLDASM、FSAT

CAD DLL 15 crack增加了对SLDASM、FSAT 改进的3D&#xff1a; 提高了打开三维文件的速度。 提高了SAT、STEP、SLDPRT、X_T、X_B、OBJ格式的阅读能力。 增加了对SLDASM、FSAT、SAB、SMT、IPT、IFC格式的支持。 增加了导出为SAT、SAB、STL、OBJ格式的功能。 改进了SAT、STE…

系统错误 无法启动此程序,因为计算机中丢失MSVCP140_1.dll。尝试重新安装该程序已解决此问题

Qt系列文章目录 文章目录 Qt系列文章目录前言一、解决方法 前言 我在windows10系统&#xff0c;使用Qt5.15.2 打包命令&#xff1a;windeployqt.exe ImageManageSys.exe &#xff0c;把ImageManageSys.exe 拷贝到windows7系统下&#xff0c;报错&#xff1a;ImageManageSys.ex…

红海云CEO孙伟解密智能化人力资源新范式

4月25日&#xff0c;由广州人力资源服务协会联合HRflag主办的“2023广州人力资源创新与科技展”在广州越秀国际会议中心举办&#xff0c;大会邀请红海云CEO孙伟出席并发表主题演讲&#xff0c;分享人力资源数字化的创新&#xff0c;实践以及思考。 红海云持续高增长的密码 在…

jsp内置对象

request 将要介绍request对象的作用范围及其常用的方法。用户每访问一个页面&#xff0c; 就会产生一个HTTP请求。这些请求中一般都包含了请求所需的参数值或者信息&#xff0c; 如果将request对象看作是客户请求的一个实例&#xff0c; 那么这个实例就包含了客户请求的所有数…

Nuxt3 布局layouts和NuxtLayout的使用

Nuxt3是基于Vue3的一个开发框架&#xff0c;基于服务器端渲染SSR&#xff0c;可以更加方便的用于Vue的SEO优化。 用Nuxt3 SSR模式开发出来的网站&#xff0c;渲染和运行速度非常快&#xff0c;性能也非常高&#xff0c;而且可SEO。 接下来我主要给大家讲解下Nuxt3的layouts布…

CF - Li Hua and Pattern

题意&#xff1a;给出了矩阵&#xff0c;里面每个位置分为蓝色或红色&#xff08;数据上用1和0体现了&#xff09;&#xff0c;给出了一个操作次数&#xff0c;每次可以改变一个坐标的颜色&#xff0c;问能否通过操作使得图像旋转180度后不变。 解&#xff1a;很容易想到&…

修改DaemonSet 的/args参数后多个pod重启的顺序

理论 当您修改了DaemonSet的/args参数时&#xff0c;DaemonSet控制器会自动触发Pod的滚动更新。滚动更新的过程是逐个将旧的Pod删除并创建新的Pod&#xff0c;以确保应用程序的高可用性和稳定性。 在进行滚动更新时&#xff0c;DaemonSet控制器会按照以下步骤逐个重启Pod&…

flask+apscheduler+企业微信消息机器人推送

简介&#xff1a;APScheduler是一个轻量级的Python库&#xff0c;用于在后台运行定时任务和延迟任务。它可以轻松地安排任务并支持多种类型的触发器&#xff0c;例如固定间隔、日期/时间表达式、CRON表达式等。APScheduler还提供了多个后台调度器实现&#xff0c;例如基于线程池…

51单片机(五)LCD1602调试工具

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

4月24日~4月26日学习总结

一&#xff0c;刷题目情况&#xff0c;已经完成了8道题目&#xff0c;对于其中一些题目做一下题解。 这个题目的意思是找到的两个位置l和r&#xff0c;为了做到这个数组的l到r的子数组经过排序后&#xff0c;会变成输入的另外一个数组&#xff0c;这个题目的思路就是首先找到在…