C++二叉树进阶

news2024/11/19 18:36:16

本期内容我们讲解二叉树的进阶知识,没有看过之前内容的小伙伴建议先看往期内容

二叉树-----补充_KLZUQ的博客-CSDN博客

目录

二叉搜索树 

代码实现

基础框架

Insert

Find

Erase

析构函数

拷贝构造

赋值

二叉搜索树的应用

全部代码


二叉搜索树 

二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树 :
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

 由于结构特性,二叉搜索树很适合查找数据,那它最多查找多少次呢?高度次吗?

其实不是的,二叉搜索树是可能会有右边这种情况的,时间复杂度是O(N)

 这其实是不好的,后面我们会讲AVL树和红黑树,他们的时间复杂度是O(longN)

话不多说,下面我们来实现二叉搜索树

代码实现

基础框架

template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;
	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		,_key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	BSTree()
		:_root(nullptr)
	{}
	bool Insert(const K& key)
	{

	}
private:
	Node* _root;
};

我们先写出基本框架,接着我们来实现插入

Insert

假设我们有这样一棵树,我们要插入11,13和16,该怎么办呢?我们将11和root节点对比,11比8大,应该在右边,继续对比11和10,比10大,在右边,11比14小,在左边,比13小,在左边

就成了这个样子,再看13,我们对比后发现树里面已经有13了,所以插入失败,最后看16,16比14大,链接在14的右边即可

bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else 
			{
				return false;
			}
		}
		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else 
		{
			parent->_left = cur;
		}
		return true;
	}

我们先用循环来实现

下面为了测试,我们写一个中序遍历

void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

我们写成子函数是因为我们在调用时还得传root,比如封装写成子函数,这样我们就不用传了,下面进行测试

 没有问题,中序遍历出来是有序的

bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
private:
	bool _InsertR(Node*& root, const K& key)
	{
		if (root == nullptr)//因为引用,所以下面可以直接new
		{
			root = new Node(key);
			return true;
		}
		if (root->_key < key)
		{
			return _InsertR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _InsertR(root->_left, key);
		}
		else
		{
			return false;
		}
	}

我们再看递归版本的,递归版本里参数加了引用,这个引用可以让我们在插入节点时不用找该节点的父亲节点,可谓是神之一手 

Find

bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

find也非常简单

bool FindR(const K& key)
	{
		return _FindR(_root, key);
	}
private:
	bool _FindR(Node* root,const K& key)//递归版本
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return true;
		}
	}

还有递归版本的,我们再把子函数写成私有的 

Erase

删除是一个重点,我们有这样一棵树,假设我们要删除7,14和3

7是叶子节点,删除后再把6的右置为空即可,14也还好说,它只有一个孩子,我们可以让10的右节点指向13,最麻烦的是3,我们该如何删除3呢?这里就要用到替换法,用3的左子树的最大节点或者右子树的最小节点来替换,一棵树的最大节点是这棵树的最右边的节点,所以左子树的最大节点很好找,最小的节点就是最左边的,所以右子树的最小节点也很好找

bool Erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到了
			{
				//左为空
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_right;
						}
						else
						{
							parent->_left = cur->_right;
						}
					}		
				}//右为空
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else 
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_left;
						}
						else
						{
							parent->_left = cur->_left;
						}
					}			
				}//左右都不为空
				else
				{
					//找替代节点
					Node* parent = cur;//这里不能给空,防止leftMax是根节点的左子树的第一个节点
					Node* leftMax = cur->_left;
					while (leftMax->_right)
					{
						parent = leftMax;
						leftMax = leftMax->_left;
					}
					swap(cur->_key, leftMax->_key);
					if (parent->_left == leftMax)
					{
						parent->_left = leftMax->_left;
					}
					else
					{
						parent->_right = leftMax->_left;
					}
					cur = leftMax;
				}
				delete cur;
				return true;
			}
		}
		return false;
	}

删除要考虑的情况非常多,大家一定要画图才能理解

 大家用这几张图画一画,结合代码,里面的坑是非常多的 

下面我们测试一下

没有问题,大家测试时再把整棵树删空测试一下

bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else
		{
			Node* del = root;
			if (root->_left == nullptr)//左为空
			{
				root = root->_right;//因为引用所以可以直接改
			}
			else if (root->_right == nullptr)//右为空
			{
				root = root->_left;
			}
			else //左右都不为空
			{
				Node* leftMax = root->_left;
				while (leftMax->_right)
				{
					leftMax = leftMax->_right;
				}
				swap(root->_key, leftMax->_key);
				return _EraseR(root->_left, key);
			}
			delete del;
			return true;
		}
	}

我们再看递归版本,这里同样使用了引用,可以方便很多,大家画图对照一下就可以理解

下面我们再完成一下析构函数

析构函数

~BSTree()
	{
		Destroy(_root);
	}
void Destroy(Node*& root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
		root = nullptr;
	}

也是同样使用引用,为了方便置空

拷贝构造

BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}
Node* Copy(Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node* copyroot = new Node(root->_key);
		copyroot->_left = Copy(root->_left);
		copyroot->_right = Copy(root->_right);
		return copyroot;
	}

拷贝构造这里是不能调用insert的,因为任何顺序的插入都不能保证和原来树的一样,大家可以试一试,而我们这样写拷贝构造就可以解决问题

赋值

BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}

赋值的话我们直接用现代写法即可

二叉搜索树的应用

1. K 模型: K 模型即只有 key 作为关键码,结构中只需要存储 Key 即可,关键码即为需要搜索到 的值
比如: 给一个单词 word ,判断该单词是否拼写正确 ,具体方式如下:
以词库中所有单词集合中的每个单词作为 key ,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV 模型:每一个关键码 key ,都有与之对应的值 Value ,即 <Key, Value> 的键值对 。该种方式在现实生活中非常常见:
比如 英汉词典就是英文与中文的对应关系 ,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文 <word, chinese> 就构成一种键值对;
再比如 统计单词次数 ,统计成功后,给定单词就可快速找到其出现的次数, 单词与其出
现次数就是 <word, count> 就构成一种键值对

 下面我们修改我们上面的程序,把它变成KV的

template<class K, class V >
	struct BSTreeNode
	{
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _value;
		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{} 
	};

	template<class K,class V>
	class BSTree
	{
		typedef BSTreeNode<K,V> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}

		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool InsertR(const K& key, const V& value)
		{
			return _InsertR(_root, key,value);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)//左为空
				{
					root = root->_right;//因为引用所以可以直接改
				}
				else if (root->_right == nullptr)//右为空
				{
					root = root->_left;
				}
				else //左右都不为空
				{
					Node* leftMax = root->_left;
					while (leftMax->_right)
					{
						leftMax = leftMax->_right;
					}
					swap(root->_key, leftMax->_key);
					return _EraseR(root->_left, key);
				}
				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key,const V& value)
		{
			if (root == nullptr)//因为引用,所以下面可以直接new
			{
				root = new Node(key,value);
				return true;
			}
			if (root->_key < key)
			{
				return _InsertR(root->_right, key,value);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key,value);
			}
			else
			{
				return false;
			}
		}
		Node* _FindR(Node* root, const K& key)//递归版本
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return root;
			}
		}
		void _InOrder(Node* root)
		{
			if (root == NULL)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << ":"<<root->_value<<endl;
			_InOrder(root->_right);
		}
	private:
		Node* _root;
	};

这里我删除了一些,下面我们来进行测试

这里我们模拟一个字典 

还有统计水果出现的次数,没有出现的水果我们存进去,出现的让value++

全部代码

namespace key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;
		BSTreeNode(const K& key)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}
		BSTree(const BSTree<K>& t)
		{
			_root = Copy(t._root);
		}
		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
		~BSTree()
		{
			Destroy(_root);
		}
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}

		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else//找到了
				{
					//左为空
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
					}//右为空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_right == cur)
							{
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}
					}//左右都不为空
					else
					{
						//找替代节点
						Node* parent = cur;//这里不能给空,防止leftMax是根节点的左子树的第一个节点
						Node* leftMax = cur->_left;
						while (leftMax->_right)
						{
							parent = leftMax;
							leftMax = leftMax->_left;
						}
						swap(cur->_key, leftMax->_key);
						if (parent->_left == leftMax)
						{
							parent->_left = leftMax->_left;
						}
						else
						{
							parent->_right = leftMax->_left;
						}
						cur = leftMax;
					}
					delete cur;
					return true;
				}
			}
			return false;
		}

		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* copyroot = new Node(root->_key);
			copyroot->_left = Copy(root->_left);
			copyroot->_right = Copy(root->_right);
			return copyroot;
		}
		void Destroy(Node*& root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)//左为空
				{
					root = root->_right;//因为引用所以可以直接改
				}
				else if (root->_right == nullptr)//右为空
				{
					root = root->_left;
				}
				else //左右都不为空
				{
					Node* leftMax = root->_left;
					while (leftMax->_right)
					{
						leftMax = leftMax->_right;
					}
					swap(root->_key, leftMax->_key);
					return _EraseR(root->_left, key);
				}
				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)//因为引用,所以下面可以直接new
			{
				root = new Node(key);
				return true;
			}
			if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}
		bool _FindR(Node* root, const K& key)//递归版本
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}
		void _InOrder(Node* root)
		{
			if (root == NULL)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
	private:
		Node* _root;
	};

	void Test()
	{
		int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
		BSTree<int> t;
		for (auto e : a)
		{
			t.InsertR(e);
		}
		t.InOrder();
		t.EraseR(4);
		t.InOrder();

		t.EraseR(6);
		t.InOrder();

		t.EraseR(7);
		t.InOrder();

		t.EraseR(3);
		t.InOrder();
		for (auto e : a)
		{
			t.Erase(e);
		}
		t.InOrder();
	}
	void Test2()
	{
		int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
		BSTree<int> t;
		for (auto e : a)
		{
			t.Insert(e);
		}
		t.InOrder();
		BSTree<int> t1(t);
		t1.InOrder();
	}
}

namespace key_value
{
	template<class K, class V >
	struct BSTreeNode
	{
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _value;
		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{} 
	};

	template<class K,class V>
	class BSTree
	{
		typedef BSTreeNode<K,V> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}

		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool InsertR(const K& key, const V& value)
		{
			return _InsertR(_root, key,value);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)//左为空
				{
					root = root->_right;//因为引用所以可以直接改
				}
				else if (root->_right == nullptr)//右为空
				{
					root = root->_left;
				}
				else //左右都不为空
				{
					Node* leftMax = root->_left;
					while (leftMax->_right)
					{
						leftMax = leftMax->_right;
					}
					swap(root->_key, leftMax->_key);
					return _EraseR(root->_left, key);
				}
				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key,const V& value)
		{
			if (root == nullptr)//因为引用,所以下面可以直接new
			{
				root = new Node(key,value);
				return true;
			}
			if (root->_key < key)
			{
				return _InsertR(root->_right, key,value);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key,value);
			}
			else
			{
				return false;
			}
		}
		Node* _FindR(Node* root, const K& key)//递归版本
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return root;
			}
		}
		void _InOrder(Node* root)
		{
			if (root == NULL)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << ":"<<root->_value<<endl;
			_InOrder(root->_right);
		}
	private:
		Node* _root;
	};
	void test()
	{
		BSTree<string, string> dict;
		dict.InsertR("insert", "插入");
		dict.InsertR("right", "右边");
		dict.InsertR("sort", "排序");
		dict.InsertR("left", "左边");
		dict.InsertR("date", "日期");
		string str;
		while (cin >> str)
		{
			BSTreeNode<string, string>* ret = dict.FindR(str);
			if (ret)
			{
				cout << ret->_value << endl;
			}
			else
			{
				cout << "无此单词" << endl;
			}
		}
	}
	void test2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
		BSTree<string, int> countTree;
		for (auto& str : arr)
		{
			auto ret = countTree.FindR(str);
			if (ret == nullptr)
			{
				countTree.InsertR(str, 1);
			}
			else
			{
				ret->_value++;
			}
		}
		countTree.InOrder();
	}
}

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

如有错误,还请指正

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

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

相关文章

echarts实现图表标签(label)可拖拽,以及保存拖拽后的位置

需求背景&#xff1a; 当echarts图表中像素点非常多&#xff0c;或者有像素点重合的时候&#xff0c;标签就会被覆盖或者重叠。为了解决这个问题&#xff0c;让用户体验更加友好&#xff0c;于是就实现了对label进行拖拽。用户可以把label拖拽到任何他想要的位置&#xff0c;并…

从0开始做yolov5模型剪枝

文章目录 从0开始做yolov5模型剪枝 ****1 前言2 GitHub取源码3 原理3.1 原理3.2 network slimming过程 4 具体实施步骤4.1 安装虚拟环境4.2 配置参数4.2.1 数据集参数4.2.2 模型结构参数4.2.3 train.py中的参数 4.3 正常训练4.3.1 准备4.3.2 训练及问题解决 4.4 稀疏化训练4.4.…

kali 2023.3新增工具

在终端模拟器中运行 sudo apt update && sudo apt full-upgrade 命令来更新其安装 Kali Linux 2023.3 发布中包含了九个新工具&#xff0c;分别是&#xff1a; Calico&#xff1a;云原生网络和网络安全。 cri-tools&#xff1a;用于Kubelet容器运行时接口的命令行界面…

【无标题】AI在开发工具中的应用:自动代码生成

AI辅助开发工具的概述 随着人工智能&#xff08;AI&#xff09;技术的发展&#xff0c;越来越多的开发工具开始利用AI来辅助程序员的工作。其中一项重要的应用是自动代码生成。AI辅助开发工具能够通过学习大量的代码库和规范&#xff0c;分析程序员的需求&#xff0c;并自动生成…

论文阅读_模型结构_LoRA

name_en: LoRA: Low-Rank Adaptation of Large Language Models name_ch: LORA&#xff1a;大语言模型的低阶自适应 paper_addr: http://arxiv.org/abs/2106.09685 date_read: 2023-08-17 date_publish: 2021-10-16 tags: [‘深度学习’,‘大模型’] author: Edward J. Hu cita…

leetcode76. 最小覆盖子串(滑动窗口-java)

滑动窗口 最小覆盖子串滑动窗口代码 上期经典 最小覆盖子串 难度 - 困难 原题链接 - 最小覆盖字串 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 “” 。 注意&#xff1a; 对于 t…

【TypeScript】never 类型

在 TypeScript 中&#xff0c;never 是一种特殊的类型&#xff0c;表示永远不会发生的值或类型。它通常用于表示一些绝对不可能出现的情况&#xff0c;例如永远不会返回的函数类型或在某些条件下绝对不会发生的值。 以下是一些关于 never 类型的情况&#xff1a; 函数返回值…

大红喜庆版UI猜灯谜小程序源码/猜字谜微信小程序源码

今天给大家带来一款UI比较喜庆的猜灯谜小程序&#xff0c;大家看演示图的时候当然也是可以看得到那界面是多么的喜庆&#xff0c;而且新的一年也很快就来了,所以种种的界面可能都比较往喜庆方面去变吧。 这款小程序搭建是免服务器和域名的&#xff0c;只需要使用微信开发者工具…

243:vue+Openlayers 更改鼠标滚轮缩放地图大小,每次缩放小一点

第243个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayers项目中设置鼠标滚轮缩放地图大小,每次滑动一格滚轮,设定的值非默认值1。具体的设置方法,参考源代码。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源…

Java快速入门体验

Java快速入门体验 一、环境信息1.1 硬件信息1.2 软件信息 二、Maven安装2.1 Maven介绍2.2 Maven安装包下载2.3 Maven安装2.4 Maven初始化 三、Java安装3.1 JDK下载3.2 JDK安装3.3 JDK初始化 四、开发环境搭建4.1 安装开发工具4.2 关联Maven环境4.2.1 新建JAVA项目4.2.2 Maven与…

5G网关如何提升智慧乡村农业生产效率

得益于我国持续推进5G建设&#xff0c;截至今年5月&#xff0c;我国5G基站总数已达284.4万个&#xff0c;覆盖全国所有地级市、县城城区和9成以上的乡镇镇区&#xff0c;实现“镇镇通5G”&#xff0c;全面覆盖了从城市到农村的延伸。 依托5G网络的技术优势&#xff0c;智慧乡村…

有没有好用的微信管理软件?解决企业营销管理痛点

企业营销管理痛点&#xff1a; 1、如何提高员工跟进客户的能力和效率&#xff1f; 2、怎么杜绝飞单私单工作怠慢等问题&#xff1f; 3、微信好友太多无法实现精准营销&#xff1f; 4、如何第一时间知道员工的违规行为&#xff1f; 多微信聚合聊天 多个微信号聚合在一个界面…

Linux 打开U盘硬盘等报错 file type exfat not configured in kernel

目录 原因&#xff1a; 查看系统文件系统和当前系统版本 回归正题&#xff0c;如何解决报错 在centons 7中打开U盘&#xff0c;报错file type exfat not configured in kernel。 原因&#xff1a; 这是因为Linux采用的文件系统和我U盘的文件系统不一致引起。如下图&#xf…

在线ppt转pdf如何转换?用这一个方法就够了

在线PPT转PDF是一种将PPT文件转换为PDF格式的便捷且常用的工具。随着科技的发展&#xff0c;PPT已经成为了商务、教育等领域中最常用的演示工具之一。PDF格式具有较好的稳定性和兼容性。PPT文件可能因为不同的操作系统、软件版本或字体缺失等原因而导致显示不一致或乱码等问题&…

【数据结构练习】单链表OJ题(二)

目录 一、相交链表二、环形链表1三、环形链表2四、链表分割五、复制带随机指针的链表 一、相交链表 题目&#xff1a; 示例&#xff1a; 注意&#xff1a;不能根据节点的值来比较是否相交&#xff0c;而是根据节点在内存中是否指向相同的位置。 例如以上图&#xff1a; 链表…

ATFX汇市:美元指数疯狂上涨,英镑单日贬值近1%

环球汇市行情摘要—— 昨日&#xff0c;美元指数上涨0.55%&#xff0c;收盘在103.95点&#xff0c; 欧元贬值0.50%&#xff0c;收盘价1.0810点&#xff1b; 日元贬值0.69%&#xff0c;收盘价145.84点&#xff1b; 英镑贬值0.98%&#xff0c;收盘价1.2600点&#xff1b; 瑞…

火山引擎边缘云,助你沉浸式回忆童年

发现了吗&#xff1f;在抖音、西瓜视频上能观看4K修复的经典港片了&#xff01;得益于抖音、中国电影资料馆、火山引擎共同发起的“经典香港电影修复计划”&#xff0c;我们童年时期看过的《大话西游之大圣娶亲》《武状元苏乞儿》等22部港片以更清晰、流畅、颜色饱满的状态回归…

基于Java+SpringBoot+vue前后端分离可盈保险合同管理系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

【数据库】使用ShardingSphere+Mybatis-Plus实现读写分离

书接上回&#xff1a;数据库调优方案中数据库主从复制&#xff0c;如何实现读写分离 ShardingSphere 实现读写分离的方式是通过配置数据源的方式&#xff0c;使得应用程序可以在执行读操作和写操作时分别访问不同的数据库实例。这样可以将读取操作分发到多个从库&#xff08;从…

十二、pikachu之URL重定向

文章目录 1、URL重定向概述2、实战3、URL跳转的几种方式:3.1 META标签内跳转3.2 javascript跳转3.3 header头跳转 1、URL重定向概述 不安全的url跳转问题可能发生在一切执行了url地址跳转的地方。如果后端采用了前端传进来的&#xff08;可能是用户传参&#xff0c;或者之前预埋…