【C++】——二叉搜索树

news2024/10/17 13:07:30

目录

一、前言

二、二叉搜索树

2.1概念

2.2二叉搜索树操作

2.2.1 二叉树的查找

2.2.2 二叉搜索树的插入 

2.2.3 二叉搜索树的删除

​编辑 2.3二叉搜索树的实现

2.3.1查找

2.3.2 插入

2.3.3 删除

 2.3.4 打印

2.3.5 拷贝构造和赋值重载

2.3.6 析构函数

2.4 二叉搜索树的应用

2.5二叉搜索树的性能分析 

 三、全部代码

四、结语


一、前言

🌇个人主页:_麦麦_ 

📚今日名言:滔滔不绝很容易,可我只想和你在一个慢下来 的世界里交谈。——两地书

二、二叉搜索树

2.1概念

        二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

●若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

●若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

●它的左右子树也分别为二叉搜索树

2.2二叉搜索树操作

        在认识到什么是一颗二叉搜索树后,那么我们能对这棵树树进行什么操作,接下来让我们来进行对这些操作的认识,为了方便操作的讲解,我们以下面这棵二叉搜索树为例进行操作的讲解。

2.2.1 二叉树的查找

        首先讲解的操作是对二叉树的查找。

        对于一颗二叉树,如果我们想要去找一个值有没有存储在这个树中,就需要用到查找这个操作。我们大致讲一下查找的思路,具体的模拟实现会在下面为大家进行一个呈现。由于二叉搜索树与一般的树不同,根的左节点的值小于根,根的有节点的值大于根,根据这一个特性,当我们进行数据查找的时候,先从根开始。如果我们要查找的数据比根大,我们就走到根的右节点,反之就走到根的左节点,知道找到我们所想要的数据为止,如果一直找到输的最后也就是访问到空结点都没有找到我们想要的数据,就说明这棵树中并没有我们想查找的数据。

        我们以查找数字7为例,根结点的8大于7,我们走到它的左节点3,发现左节点3小于7,紧接着来到3的右结点,6还是小于7,我们继续往右走,发现等于7,那么这个节点就是我们想要的节点。那么如果我们查找的数据比如说是20,还是从根节点开始比较,20大于8,我们来到右节点10,20大于10,继续往右节点走,20大于14,继续往右走,发现是个空节点,此时就说明该树中并没有存储我们想要的数据20。

注:对于二叉搜索树的查找最多查找树的高度次也就是O(n)

 

2.2.2 二叉搜索树的插入 

        接下来就是二叉树的插入操作,插入的具体过程大致分为两点:

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

2.2.3 二叉搜索树的删除

        首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情 况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

        看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程 如下:

●情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除        

●情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

●情况d:在它的右子树中寻找中序下的第一个结点(关键码最小)/左子树中寻找中序下的最后一个结点(关键码最大),用它的值填补到被删除节点 中,再来处理该结点的删除问题--替换法删除

 2.3二叉搜索树的实现

        在讲完二叉搜索树的基本操作后,下面我们来简单模拟实现一下二叉搜索树。

2.3.1查找

        首先我们先来实现二叉搜索树的查找。

        我们要利用搜索二叉树的特性,即父节点的值大于左子树的值,小于右子树的值,根据这一特性我们就能够将根节点的值与我们所要查找的值进行比较,大于所查找的值,就将其左子树的值拿来进行进一步比较,反之则是右子树……不断遍历此树,我们就能够找到我们所查找的值,如果一直找到空节点我们都没能找到,说明该树中并不存在我们想要的值。

        在知道思路后,我们可以以递归或非递归的方式来实现查找这一功能,具体代码如下:

        //查找-非递归
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)	  cur = cur->_left;
				else if (cur->_key < key) cur = cur->_right;
				else return true;
			}
			return false;
		}
		//查找-递归
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

        bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr) return false;
			if (root->_key < key)
			{
				_FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}

 

2.3.2 插入

        接下来我们来实现二叉搜索树的插入,对于插入来说,有两种方式可以实现:递归和非递归。

        非递归版的插入:当我们向往当前树插入一个新节点的时候,第一个要做的是操作是判断该树是否为空,为空则需要创建新结点并将该节点作为这棵树的根结点;不为空再进行后续的操作。第二,当我们判断出该树不为空后,第一步是要找出新结点应该要插入的位置,然后再将其与上一个结点进行链接即可。那么我们要如何找到新结点的位置呢,其实我们只要根据二叉搜索树的根节点大于左子树,小于右子树这一特点,从根节点开始,将其值与我们要插入的值进行比较,小于根结点就走到根节点的左子树,大于根结点就走到根节点的右子树,然后再重新与其进行比较,直到走到空为止,那么就是该节点要插入的位置,如果遇到相等的情况就直接退出。在找到插入的位置后我们该如何与父节点进行链接呢,我们知道一个结点只能往左右进行链接,是不能往上找到父节点的,因此我们还需要另外一个变量来存储父节点,当我们不断比较大小遍历树的时候,将父节点进行更新,这样当我们来到正确的位置的时候,就能将新结点与父节点进行链接。

        递归版的插入:在了解完非递归版的插入后,其实递归版与其思路大致相同,都是从根节点开始不断遍历树,并将其值与所插入的值进行比较来找到正确插入的位置并进行父节点的链接。与其不同的地方在于,递归版的插入可以不用创建一个变量来单独存储父节点,而是可以通过函数的参数来存储父节点,不过要注意的是要传递结点的引用才能够将其与新结点进行链接。

        具体代码如下:

        //插入-非递归
		bool insert(const K& key)
		{
			//空树
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			//树不为空
			else
			{
				//寻找父亲结点
				Node* parent = nullptr;
				Node* cur = _root;
				while (cur)
				{
					if (cur->_key > key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (cur->_key < key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else return false;
				}
				//插入新节点
				if (parent->_key > key) parent->_left = new Node(key);
				else parent->_right = new Node(key);
				return true;
			}
		}

        //插入-递归
		bool insertR(const K& key)
		{

			return	_insertR(_root, key);
		}

		bool _insertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (root->_key < key)
			{
				_insertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_insertR(root->_left, key);
			}
			else return false;
		}


2.3.3 删除

        在插入之后,我们来实现一下删除。

        对于二叉搜索树而言,删除是较为复杂的一个操作。当我们想删除某个值对于的节点是,第一步就是在该树中查找我们所需的值,如果没有直接返回即可。第二步当我们找到值所对应的节点的时,该节点可能会有四种情况:1、左右子树都为空 2、左子树为空  3、右子树为空 4、左右子树都不为空。下面我们对这四种情况进行删除的模拟实现。

        对于第一种左右子树都为空的情况,由于我们可以将其归为左子树为空或者右子树为空的情况,我们就可以不对其进行单独处理,归于第二种或者第三种情况都可以。

        对于左子树为空的情况,我们需要将其父节点与其右子树进行链接之后,再将该节点的空间释放即可。需要注意删除结点为根结点的时候,这时候就需要将根节点与右子树进行链接即可,而不是父节点。

        对于右子树为空的情况,与上一种情况类似,将父节点与其左子树进行链接,并将节点释放。需要注意删除结点为根结点的时候,这时候就需要将根节点与左子树进行链接即可,而不是父节点。

        对于左右子树都不为空的情况,我们就需要寻找一个替代节点,可以寻找他的左子树中中值最大的节点,亦可以是右子树中最小的节点,只有选择这两个结点来代替我们所要删除的节点才能满足二叉搜索树中值的特性。我们以左子树中值最大的节点为例,来实现删除的操作。第一步就是找到所删除结点左子树中最大的节点,然后将该节点与所删除结点进行交换,最后从原删除结点的左子树开始重新删除的操作。

        依旧有递归和非递归两个版本,具体代码如下:

        //删除非递归
		bool Erase(const K& key)
		{
			//空树
			if (_root == nullptr) return false;
			if (Find(key) == false) return false;
			//非空树
			//找key
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				//找到了
				//删除结点没有孩子或者只有一个孩子
				if (cur->_left == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_right;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_right;
					}

				}
				else if (cur->_right == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_left;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_left;
					}

				}
				//删除结点有两个孩子
				else
				{
					//找左边最大的节
					parent = cur;
					Node* LeftMax = cur->_left;
					while (LeftMax->_right)
					{
						parent = LeftMax;
						LeftMax = LeftMax->_right;
					}
					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);
		}

        bool _EraseR(Node*& root, const K& key)
		{
			//找不到
			if (root = nullptr) return false;
			if (root->_key < key)
			{
				_EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_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(LeftMax->_key, root->_key);
					EraseR(root->_left, key);
				}
				delete del;
				return true;
			}
		}

 2.3.4 打印

        对于二叉搜索树的打印我们采取中序遍历递归的方法就能够将该树中的数据按照降序的顺序打印出来。

        具体代码如下:

        void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

        void _Inorder(Node* root)
		{
			if (root == nullptr) return;
			_Inorder(root->_left);
			cout << root->_key << ' ';
			_Inorder(root->_right);
		}

2.3.5 拷贝构造和赋值重载

        对于二叉搜索树的拷贝,我们采取前序遍历的递归方式对树中的节点进行拷贝,当我们实现完树的拷贝之后,就能够借助现代写法将赋值重载一并实现。

        具体代码如下:

        Node* Copy(Node*& root)
		{
			if (root == nullptr) return nullptr;
			Node* node = new Node(root->_key);
			node->_left = Copy(root->_left);
			node->_right = Copy(root->_right);
			return node;
		}

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

2.3.6 析构函数

        对于二叉搜索树的析构来说,我们采取后续遍历的递归方式来释放结点空间。

        具体代码如下:

        ~BSTree()
		{
			Destroy(_root);
		}

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

2.4 二叉搜索树的应用

        1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到 的值。 比如:给一个单词word,判断该单词是否拼写正确,具体方式如下: 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

         2. KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方 式在现实生活中非常常见: 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文就构成一种键值对; 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对。 

2.5二叉搜索树的性能分析 

      插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

        对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多。

        但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

 

         最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:logN 

        最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N

        如果退化成单支树,二叉搜索树的性能就失去,那么该应对这种情况,在后续章节学习的AVL树和红黑树就可以很好的解决这种问题。

 三、全部代码

        对于二叉搜索树模拟实现的全部代码以及对其改造K模型与K_V模型的代码附下:

namespace Key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode(K key)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}


		BSTreeNode* _left;
		BSTreeNode* _right;
		K _key;
	};

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

		BSTree(BSTree<K>& t)
		{
			_root = Copy(t._root);
		}

		Node* Copy(Node*& root)
		{
			if (root == nullptr) return nullptr;
			Node* node = new Node(root->_key);
			node->_left = Copy(root->_left);
			node->_right = Copy(root->_right);
			return node;
		}

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

		~BSTree()
		{
			Destroy(_root);
		}

		void Destroy(Node*& root)
		{
			if (root == nullptr) return;
			Destroy(root->_left);
			Destroy(root->_right);
			if (root->_left == nullptr && root->_right == nullptr)
			{
				delete root;
				root = nullptr;
			}
		}
		//插入-非递归
		bool insert(const K& key)
		{
			//空树
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			//树不为空
			else
			{
				//寻找父亲结点
				Node* parent = nullptr;
				Node* cur = _root;
				while (cur)
				{
					if (cur->_key > key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (cur->_key < key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else return false;
				}
				//插入新节点
				if (parent->_key > key) parent->_left = new Node(key);
				else parent->_right = new Node(key);
				return true;
			}
		}


		//插入-递归
		bool insertR(const K& key)
		{

			return	_insertR(_root, key);
		}

		bool _insertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (root->_key < key)
			{
				_insertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_insertR(root->_left, key);
			}
			else return false;
		}



		//查找-非递归
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)	  cur = cur->_left;
				else if (cur->_key < key) cur = cur->_right;
				else return true;
			}
			return false;
		}
		//查找-递归
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}



		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

		//删除非递归
		bool Erase(const K& key)
		{
			//空树
			if (_root == nullptr) return false;
			if (Find(key) == false) return false;
			//非空树
			//找key
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				//找到了
				//删除结点没有孩子或者只有一个孩子
				if (cur->_left == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_right;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_right;
					}

				}
				else if (cur->_right == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_left;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_left;
					}

				}
				//删除结点有两个孩子
				else
				{
					//找左边最大的节
					parent = cur;
					Node* LeftMax = cur->_left;
					while (LeftMax->_right)
					{
						parent = LeftMax;
						LeftMax = LeftMax->_right;
					}
					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:
		void _Inorder(Node* root)
		{
			if (root == nullptr) return;
			_Inorder(root->_left);
			cout << root->_key << ' ';
			_Inorder(root->_right);
		}

		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr) return false;
			if (root->_key < key)
			{
				_FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}

		bool _EraseR(Node*& root, const K& key)
		{
			//找不到
			if (root = nullptr) return false;
			if (root->_key < key)
			{
				_EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				_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(LeftMax->_key, root->_key);
					EraseR(root->_left, key);
				}
				delete del;
				return true;
			}
		}

		Node* _root;
	};
}

namespace Key_Value
{

	template<class K,class V>
	struct BSTreeNode
	{
		BSTreeNode( K key,  V val)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_val(val)
		{}


		BSTreeNode* _left;
		BSTreeNode* _right;
		K _key;
		V _val;
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		bool Insert(const K& key, const V& value)
		{
			//空树
			if (_root == nullptr)
			{
				_root = new Node(key,value);
				return true;
			}
			//树不为空
			else
			{
				//寻找父亲结点
				Node* parent = nullptr;
				Node* cur = _root;
				while (cur)
				{
					if (cur->_key > key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (cur->_key < key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else return false;
				}
				//插入新节点
				if (parent->_key > key) parent->_left = new Node(key,value);
				else parent->_right = new Node(key,value);
				return true;
			}
		}

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


		bool Erase(const K& key)
		{
			//空树
			if (_root == nullptr) return false;
			if (Find(key) == false) return false;
			//非空树
			//找key
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				//找到了
				//删除结点没有孩子或者只有一个孩子
				if (cur->_left == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_right;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_right;
					}

				}
				else if (cur->_right == nullptr)
				{
					//特殊情况 -位于根结点
					if (cur == _root) _root = cur->_left;
					//删除结点位于左侧
					else if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					//删除结点位于右侧
					else if (parent->_right == cur)
					{
						parent->_right = cur->_left;
					}

				}
				//删除结点有两个孩子
				else
				{
					//找左边最大的节
					parent = cur;
					Node* LeftMax = cur->_left;
					while (LeftMax->_right)
					{
						parent = LeftMax;
						LeftMax = LeftMax->_right;
					}
					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;
		}
	private:
		Node* _root = nullptr;

		void _Inorder(Node* root)
		{
			if (root == nullptr) return;
			_Inorder(root->_left);
			cout << root->_key << ":" << root->_val << endl;;
			_Inorder(root->_right);
		}
	};


}

四、结语

         到此为止,关于二叉搜索树的讲解就告一段落了,至于其他的内容,小伙伴们敬请期待呀!

        关注我 _麦麦_分享更多干货:_麦麦_-CSDN博客
        大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!

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

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

相关文章

智能去毛刺:2D视觉引导机器人如何重塑制造业未来

机器人技术已经深入到各个工业领域中&#xff0c;为制造业带来了前所未有的变革。其中&#xff0c;2D视觉引导机器人技术以其精准、高效的特点&#xff0c;在去毛刺工艺中发挥着越来越重要的作用。本文将为您介绍2D视觉引导机器人技术的基本原理及其在去毛刺工艺中的应用&#…

c++初阶数据结构速成

温馨提示&#xff1a;本篇文章只给大家介绍初阶数据结构的一些常用接口 stl的简单介绍 什么是stl? STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的 组件库&#xff0c;而且是一个包罗数据结构与算法的软件框…

Redis 哨兵模式下DB库操作审计

Redis Sentinel集群环境 主机版本模式NodeSentinelredis-sentinel-node-06.2.12哨兵MasterYesredis-sentinel-node-16.2.12哨兵SlaveYesredis-sentinel-node-26.2.12哨兵SlaveYes 架构设计 命令行&程序验证 1、在redis-sentinel-node-1上使用redis-cli 连接redis-sentine…

2024台州赛CTFwp

备注&#xff1a; 解题过程中&#xff0c;关键步骤不可省略&#xff0c;不可含糊其辞、一笔带过。解题过程中如是自己编写的脚本&#xff0c;不可省略&#xff0c;不可截图&#xff08;代码字体可以调小&#xff1b;而如果代码太长&#xff0c;则贴关键代码函数&#xff09;。…

开放式蓝牙耳机哪个品牌好用?开放式耳机排行榜测评!

开放式耳机&#xff0c;因其特殊的不入耳佩戴模式&#xff0c;让使用者在享受音乐或者进行通话的过程中&#xff0c;依然可以对外界声音保持敏感。在户外运动场景下&#xff0c;这种特性优势尽显&#xff0c;既保证了耳机佩戴的稳定和舒适&#xff0c;又提高了运动的安全性。为…

Netty结构

Netty结构 引导器Bootstrap举例&#xff1a;一个简单的HTTP服务器服务端启动类服务端业务逻辑处理类 二级目录Channel初始化&#xff08;设置Channel类型&#xff09;注册ChannelHandler结合HTTP请求-响应&#xff0c;分析数据在ChannelPipeline中的流向 设置Channel参数端口绑…

java互联网医院智能导诊系统源码,Uniapp前端开发框架,支持一次编写,多端运行

智慧导诊系统源码&#xff0c;java源码&#xff0c;大屏机自动导诊&#xff0c;互联网医院智能导诊系统源码 老是全身无力&#xff0c;挂什么科&#xff1f; 经常头痛&#xff0c;挂什么科&#xff1f; 总是失眠&#xff0c;又得挂哪个科&#xff1f; 世界上最遥远的距离再加…

初识git · 多人协作

目录 前言&#xff1a; 多人协作一 多人协作二 前言&#xff1a; git从发布以来&#xff0c;强大的功能有版本回退以及分支管理&#xff0c;那么分支管理用来不仅是为了维护master的&#xff0c;更多的是多人协作的一种代表&#xff0c;所以多人协作这一章节&#xff0c;基…

2010年国赛高教杯数学建模C题输油管的布置解题全过程文档及程序

2010年国赛高教杯数学建模 C题 输油管的布置 某油田计划在铁路线一侧建造两家炼油厂&#xff0c;同时在铁路线上增建一个车站&#xff0c;用来运送成品油。由于这种模式具有一定的普遍性&#xff0c;油田设计院希望建立管线建设费用最省的一般数学模型与方法。   1. 针对两炼…

基于SpringBoot农场管理平台【附源码】

基于SpringBoot农场管理平台 效果如下&#xff1a; 系统首页界面 系统注册页面 农业生产资料详细页面 个人中心界面 管理员登录界面 管理员主界面 用户管理界面 资料分类管理界面 方法分类管理界面 计划分类管理界面 农业生产资料管理界面 研究背景 农业是人类社会发展的基石…

实现vlan间的通信

方法一&#xff1a;单臂路由 概述 单臂路由是一种网络配置&#xff0c;它允许在路由器的一个物理接口上通过配置多个子接口来处理不同VLAN的流量&#xff0c;从而实现VLAN间的通信。 原理 路由器重新封装MAC地址&#xff0c;转换Vlan标签 基础模型 1、配置交换机的链…

【openGL学习笔记】----GLFW、GLAD环境配置

glew、glad、freeglut、glfw的区别&#xff1f; glew&#xff08;The OpenGL Extension Wrangler Library&#xff09;是对底层OpenGL接口的封装&#xff0c;可以让你的代码跨平台。glad与glew作用相同&#xff0c;可以看作它的升级版。Freeglut&#xff08;OpenGL Utility To…

H5开发指南|掌握核心技术,玩转私域营销利器

随着互联网技术的不断发展和用户需求的日益增长&#xff0c;H5页面逐渐成为了企业和个人展示信息、吸引用户关注的重要手段。具有跨平台兼容性强、网页链接分享、更新迭代方便快捷、低开发成本、可搜索和优化、数据分析与追踪、灵活性与扩展性以及无需下载安装等特点。不仅可以…

pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…

Redis 三 Redis分布式锁

Redis 实战应用 文章目录 Redis 实战应用Redis 实现全局唯一IDRedis解决购物秒杀思路超卖问题一人一单集群并发的问题分布式锁Redis分布式锁的实现核心思路实现分布式锁版本一Redis分布式锁误删情况Redis分布式锁原子性问题 Redis 实现全局唯一ID 全局ID生成器&#xff0c;是一…

【排序】——1.冒泡排序法(含优化)

冒泡排序 1.原理 左边大于右边交换一趟排下来最大的交换到右边来(接下来所以文章用升序举例) 从左到右&#xff0c;相邻元素进行比较。 每次比较一轮&#xff0c;就会找到序列中最大的一个&#xff08;最小的一个——降序&#xff09;。这个数就会从序列的最右边冒出来。 以…

NetSarang Xshell v8.0060 Linux终端管理器个人免费版

NetSarang Xshell 官方个人完全免费中文版&#xff0c;Xshell特别版&#xff0c;Xshell 个人完全免费&#xff0c;Xshell 是一款最好用的Linux远程连接工具&#xff0c;免费SSH客户端、主机服务器远程管理客户端 。Xshell&#xff0c;轻松管理远程服务器&#xff0c;会话管理器…

16进制数据如何得到奇偶校验位??

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

详解mac系统通过brew安装mongodb与使用

本文目录 一、通过brew安装MongoDB二、mongodb使用示例1、启动数据库2、创建/删除数据库3、创建/删除集合 三、MongoDB基本概念1&#xff09;数据库 (database)2&#xff09;集合 &#xff08;collection&#xff09;3) 文档&#xff08;document&#xff09;4&#xff09;mong…