搜索二叉树【C++】

news2025/1/11 11:42:04

文章目录

  • 二叉搜索树
  • 二叉搜索树的模拟实现
  • 构造函数
  • 拷贝构造函数
  • 赋值运算符重载函数
  • 析构函数
  • Insert
    • 循环
    • 递归
  • Erase
    • 循环
    • 递归
  • Find
    • 循环
    • 递归
  • 二叉搜索树的应用
  • K模型
    • KV模型
  • 完整代码
    • 普通版本
    • 递归版本

二叉搜索树

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

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

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

它的左右子树也分别是二叉搜索树。

在这里插入图片描述

二叉搜索树的模拟实现

构造函数

	BSTree()
		:_root(nullptr)
	{

	}

拷贝构造函数

	BSTree(const  BSTree<K>& t)
			//BSTree(  BSTree<K>  *this ,  const  BSTree<K> & t)
			//t1 =t 
		{
			_root = Copy(t._root);
		}
		
			private:
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* copyNode = new Node(root->_key);
			//递归 
			copyNode->_left = Copy(root->_left);
			copyNode->_right = Copy(root->_right);
			return  copyNode;

		}

赋值运算符重载函数

	//赋值重载 
		BSTree<K>& operator= (BSTree<K>& t)
			//t1 = t
			//深拷贝
		{
			swap(_root, t._root);
			return *this;
		}

析构函数

~BSTree()
		{
			Destroy(_root);
		}
		
		private:
				void Destroy(Node*& root) //引用的目的:将每个节点释放后同时置空
		{
			//后序遍历 
			if (root == nullptr)
			{
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}

Insert

核心思路:

如果是空树,则直接将插入结点作为二叉搜索树的根结点。

如果不是空树,则按照二叉搜索树的性质进行结点的插入。
如果待插入结点的值<根结点的值,则需要将结点插入到左子树当中。
如果待插入结点的值>根结点的值,则需要将结点插入到右子树当中。
如果待插入结点的值等于根结点的值,则插入失败。

循环

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);
			
		
			//不知道parent在那一边,需要进一步判断
			if (parent->_key > key)
			{
				//parent在左边
				parent->_left = cur;
			}
			else if (parent->_key < key)
			{
				//parent在右边
				parent->_right = cur;

			}
			else
			{
				return false;
			}
		

		return true;
	}

递归

	bool InsertR(const K& key)//递归版本
		{
			return _InsertR(_root, key);
		}
		private:
			bool _InsertR(Node*& root, const K& key) //引用的目的:不用找父节点,不需要用父节点比较大小
		{
			//结束条件
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			//往左子树走
			if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			//往右子树走
			else if (root->_key < key)
			{
				return _InsertR(root->_right, key);

			}
			else
			{
				return false;
			}
		}

Erase

先找到需要删除的节点
需要删除的节点可能会有三种情况:
1、待删除结点的左子树为空(待删除结点的左右子树均为空包含在内)

在这里插入图片描述

2、待删除结点的右子树为空。
在这里插入图片描述

1、2 两种情况,被删除的节点都只有一个孩子

3、待删除结点的左右子树均不为空,即被删除节点有两个孩子
使用替换法处理第3中情况:
1、找替换节点:替换节点一般是左子树的最大节点(最右节点),或者是右子树的最小节点(最左节点)
2、将替换的节点删除

在这里插入图片描述
特殊情况:

在这里插入图片描述

循环

	bool Erase(const K& 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;
			}
			//找到待删除的节点
			else
			{
				//待删除节点的左子树为空 ,即一个孩子的情况
				if (cur->_left == nullptr)
				{
					  //待删除节点是根节点
					if (cur == _root)
					{
						//将根节点改为待删除节点的右孩子
						_root = cur->_right;
					}
					//待删除节点不是根节点,并且此时parent不为nullptr
					else
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_right;
						}
						else//parent->_left ==cur
						{
							parent->_left = cur->_right;
						}
					}
					
				}
				//待删除节点的右子树为空 ,即一个孩子的情况
				else if (cur->_right == nullptr)
				{
					//待删除节点是根节点
					if (cur == _root)
					{
						//将根节点改为待删除节点的左孩子
						_root = cur->_left;
					}
					//待删除节点不是根节点,并且此时parent不为nullptr
					else
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_left;
						}
						else//parent->_left==cur
						{
							parent->_left = cur->_left;
						}
					}
				} 

				else //待删除的节点的左右孩子都不为空 (替换法:左子树的最大节点即最右节点,或者右子树的最小节点即最左节点,并且将替换的节点删除)
				{
					//替换法

					//找替代节点
					Node* parent = cur;
					//找左子树的最大节点,左子树的最大节点一定没有右孩子
					Node* leftMax = cur->_left;
					
					while (leftMax->_right)
					{
						parent = leftMax; //记录leftMax的父节点,防止删除leftMax时找不到该节点位置 
						//一直往右子树找
						leftMax = leftMax->_right;
					}
					//左子树的最大节点和待删除节点替换
					swap(cur->_key, leftMax->_key);
					//重新改变链接关系
					 
					//特殊情况 
					if (parent->_left == leftMax)
					{
						parent->_left = leftMax->_left;
					}
					else//普通情况 parent->_right== leftMax
					{
						parent->_right = leftMax->_left;
						
					}
					cur = leftMax;
				}
				//删除左子树的最大节点
				delete cur;
				return true;
			}
			
		}
		return false;
	}

递归

	bool EraseR(Node* _root, 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->_left, key);
			}
			//往右树找
			else if (root->_key < key)
			{
				return _EraseR(root->_right, 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 (root->_left)
					{
						root = root->_left;
					}
					//将左子树最大节点与被删除节点替换
					swap(leftMax->_key, root->_key);

					return _EraseR(root, key);
				}

				delete del;//?
				return true;

			}


		}

Find

循环

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

递归

	bool FindR(Node* _root, 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->_left, key);
			}
			else if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else
			{
				return true;
			}
		}

二叉搜索树的应用

K模型

K模型,即只有key作为关键码,结构中只需存储key即可,关键码即为需要搜索到的值。

比如:给定一个单词,判断该单词是否拼写正确。具体方式如下:

	void TestBSTree1()
	{
		BSTree<string, string > dict;
		dict.InsertR("insert", "插入");
		dict.InsertR("sort", "排序");
		dict.InsertR("right", "右边");
		dict.InsertR("date", "日期");
		
		string str;
		while (cin>>str)
		{
			
			auto * ret  = dict.FindR(str);
			//auto ret = dict.FindR(str);
			if (ret)
			{
				cout << ret->_value << endl;
			}
			else
			{
				cout << "无此单词" << endl;
			}
		}
	}

KV模型

KV模型,对于每一个关键码key,都有与之对应的值value,即<key, value>的键值对。

英汉词典就是英文与中文的对应关系,即<word, Chinese>就构成一种键值对。具体方式如下

1、以<单词, 中文含义>为键值对,构建一棵二叉搜索树。注意:二叉搜索树需要进行比较,键值对比较时只比较key。
2、查询英文单词时,只需给出英文单词就可以快速找到与其对应的中文含义。

完整代码

普通版本

#pragma once 

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 )
	{
		//空树
		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);
			
		
			//不知道parent在那一边,需要进一步判断
			if (parent->_key > key)
			{
				//parent在左边
				parent->_left = cur;
			}
			else if (parent->_key < key)
			{
				//parent在右边
				parent->_right = cur;

			}
			else
			{
				return false;
			}
		

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

	bool Erase(const K& 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;
			}
			//找到待删除的节点
			else
			{
				//待删除节点的左子树为空 ,即一个孩子的情况
				if (cur->_left == nullptr)
				{
					  //待删除节点是根节点
					if (cur == _root)
					{
						//将根节点改为待删除节点的右孩子
						_root = cur->_right;
					}
					//待删除节点不是根节点,并且此时parent不为nullptr
					else
					{
						

						if (parent->_right == cur)
						{
							parent->_right = cur->_right;
						}
						else//parent->_left ==cur
						{
							parent->_left = cur->_right;
						}


					}
					
				}
				

				//待删除节点的右子树为空 ,即一个孩子的情况
				else if (cur->_right == nullptr)
				{
					//待删除节点是根节点
					if (cur == _root)
					{
						//将根节点改为待删除节点的左孩子
						_root = cur->_left;
					}
					//待删除节点不是根节点,并且此时parent不为nullptr
					else
					{
						
						if (parent->_right == cur)
						{
							parent->_right = cur->_left;
						}
						else//parent->_left==cur
						{
							parent->_left = cur->_left;
						}

					}
					
				} 

				else //待删除的节点的左右孩子都不为空 (替换法:左子树的最大节点即最右节点,或者右子树的最小节点即最左节点,并且将替换的节点删除)
				{
					//替换法

					//找替代节点
					Node* parent = cur;
					//找左子树的最大节点
					Node* leftMax = cur->_left;
					
					while (leftMax->_right)
					{
						parent = leftMax; //记录leftMax的父节点,防止删除leftMax时找不到该节点位置 
						//一直往右子树找
						leftMax = leftMax->_right;
					}
					//左子树的最大节点和待删除节点替换
					swap(cur->_key, leftMax->_key);
					//重新改变链接关系
					 
					//特殊情况 
					if (parent->_left == leftMax)
					{
						parent->_left = leftMax->_left;
					}
					else//普通情况 
					{
						parent->_right = leftMax->_left;
						//parent->_right =nullptr;
					}
					cur = leftMax;


				
				}
				//删除左子树的最大节点
				delete cur;
				return true;
			}
			
		}
		return false;
	}



	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	
	void _InOrder(Node *root  )
	{
		if (root == nullptr)
		{
			return; 
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);

	}


private:
	Node* _root;
};

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

	t.Erase(4);
	t.InOrder();

	t.Erase(6);
	t.InOrder();

	t.Erase(7);
	t.InOrder();

	t.Erase(3);
	t.InOrder();

	for (auto e : a)
	{
		t.Erase(e);
	}
	t.InOrder();

}

递归版本

#pragma once 
#include<string>
using namespace std;
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
	{
	public:
		typedef  BSTreeNode<K> Node;
	public:
		BSTree()
			:_root(nullptr)
		{

		}

		~BSTree()
		{
			Destroy(_root);
		}
		//拷贝构造
		BSTree(const  BSTree<K>& t)
			//BSTree(  BSTree<K>  *this ,  const  BSTree<K> & t)
			//t1 =t 
		{
			_root = Copy(t._root);
		}
		//赋值重载 
		BSTree<K>& operator= (BSTree<K>& t)
			//t1 = t
		{
			swap(_root, t._root);
			return *this;
		}
		bool EraseR(Node* _root, const K& key)//递归版本
		{
			return _EraseR(_root, key);
		}

		bool InsertR(const K& key)//递归版本
		{
			return _InsertR(_root, key);
		}

		bool FindR(Node* _root, const K& key)//递归版本
		{
			return _FindR(_root, key);
		}

	private:
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* copyNode = new Node(root->_key);
			//递归 
			copyNode->_left = Copy(root->_left);
			copyNode->_right = Copy(root->_right);
			return  copyNode;

		}

		void Destroy(Node*& root) //引用的目的:将每个节点释放后同时置空
		{
			//后序遍历 
			if (root == nullptr)
			{
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}
		bool _InsertR(Node*& root, const K& key) //引用的目的:不用找父节点,不需要用父节点比较大小
		{
			//结束条件
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			//往左子树走
			if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			//往右子树走
			else if (root->_key < key)
			{
				return _InsertR(root->_right, key);

			}
			else
			{
				return false;
			}
		}

		bool _EraseR(Node*& root, const K& key)//引用的目的:不用找父节点,不需要用父节点比较大小
		{
			//结束条件
			if (root == nullptr)
			{
				return false;
			}
			//往左树找
			if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			//往右树找
			else if (root->_key < key)
			{
				return _EraseR(root->_right, 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 (root->_left)
					{
						root = root->_left;
					}
					//将左子树最大节点与被删除节点替换
					swap(leftMax->_key, root->_key);

					return _EraseR(root, key);
				}

				delete del;//?
				return true;

			}


		}

		bool  _FindR(Node* root, const K& key)
		{
			//结束条件
			if (root == nullptr)
			{
				return false;
			}

			if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else
			{
				return true;
			}
		}



	public:
		//中序遍历
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

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

		}

	public:
		Node* _root;
	};


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

		t.InOrder();
		//没有引用,释放了,只是指针没有置空,尤其是根节点_root,我们还能通过他找到
		/*t.Destroy(t._root);*/

		t.EraseR(t._root, 4);
		t.InOrder();

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

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

		t.EraseR(t._root, 3);
		t.InOrder();

		for (auto e : a)
		{
			t.EraseR(t._root, e);
		}
		t.InOrder();
	}

	void TestBSTree2()
	{
		int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
		BSTree<int> t;
		for (auto e : a)
		{
			t.InsertR(e);
		}
		t.InOrder();
		BSTree<int> t1(t);
		t.InOrder();
		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
	{
	public:
		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;

				// 1、左为空
				// 2、右为空
				// 3、左右都不为空
				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)
			{
				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 TestBSTree1()
	{
		
		BSTree<string, string > dict;
		dict.InsertR("insert", "插入");
		dict.InsertR("sort", "排序");
		dict.InsertR("right", "右边");
		dict.InsertR("date", "日期");
		
		string str;
		while (cin>>str)
		{
			
			auto * ret  = dict.FindR(str);
			//auto ret = dict.FindR(str);
			if (ret)
			{
				cout << ret->_value << endl;
			}
			else
			{
				cout << "无此单词" << endl;
			}
		}
	}
	void TestBSTree2()
	{
		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/1030550.html

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

相关文章

Spring Security 的身份验证绕过漏洞CVE-2023-34035

文章目录 0.前言漏洞漏洞介绍描述 1.参考文档2.基础介绍2.1 组件简介&#xff1a;2.2 漏洞简介&#xff1a; 3.解决方案3.1. 升级版本 0.前言 背景&#xff1a;公司收到关于 Spring Security 的一个身份验证绕过漏洞的通知&#xff0c;该漏洞被标识为 CVE-2023-34035 漏洞 高 …

滴滴可观测平台 Metrics 指标实时计算如何实现了又准又省?

在滴滴&#xff0c;可观测平台的 Metrics 数据有一些实时计算的需求&#xff0c;承载这些实时计算需求的是一套又一套的 Flink 任务。之所以会有多套 Flink 任务&#xff0c;是因为每个服务按照其业务观测需要不同的指标计算&#xff0c;也就对应了不同数据处理拓扑。我们尽力抽…

ruoyi-vue-pro yudao 项目商城 mall 模块启用及相关SQL脚本

目前ruoyi-vue-pro 项目虽然开源&#xff0c;但是商城 mall 模块被屏蔽了&#xff0c;查看文档却要收费 199元&#xff08;知识星球&#xff09;&#xff0c;价格有点太高了吧。 分享下如何启用 mall 模块&#xff0c;顺便贴上sql相关脚本。 一、启用模块 修改根目录 pom.xm…

时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测

时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序…

工具篇 | Gradle入门与使用指南

介绍 1.1 什么是Gradle&#xff1f; Gradle是一个开源构建自动化工具&#xff0c;专为大型项目设计。它基于DSL&#xff08;领域特定语言&#xff09;编写&#xff0c;该语言是用Groovy编写的&#xff0c;使得构建脚本更加简洁和强大。Gradle不仅可以构建Java应用程序&#x…

亚马逊攀岩绳ASTM F2116 测试报告 EN892测试办理

攀岩绳是与攀岩安全带和锚点相连的一种装备&#xff0c;用于保护攀岩者&#xff0c;使其不会从高处跌落。攀岩绳由承重内芯和围绕内芯编织的护套组成。 亚马逊关于攀岩绳的政策 根据亚马逊的要求&#xff0c;所有攀岩绳均应经过检测&#xff0c;并且符合下列特定法规或标准要求…

使用GSAP创建惊艳的动画效果(一)

目录 GSAP简介GSAP的语法方法目标变量transform(变换)其它属性 vue中使用GSAP安装GSAP引用GSAP使用GSAP GSAP简介 GSAP是一个非常流行的js动画库&#xff0c;被广泛用于创建跨浏览器和跨平台的高性能动画。它的主要特点包括&#xff1a; 提供丰富的属性和方法&#xff0c;可用…

vue2中年份季度选择器(需要安装element)

调用 <!--父组件调用--><QuarterCom v-model"quart" clearable default-current/> 组件代码 <template><div><span style"margin-right: 10px">{{ label }}</span><markstyle"position:fixed;top:0;bottom:0…

人工智能安全-2-非平衡数据处理(2)

5 算法层面 代价敏感&#xff1a;设置损失函数的权重&#xff0c;使得少数类判别错误的损失大于多数类判别错误的损失&#xff1b; 单类分类器方法&#xff1a;仅对少数类进行训练&#xff0c;例如运用SVM算法&#xff1b; 集成学习方法&#xff1a;即多个分类器&#xff0c;然…

【Java 基础篇】Java网络编程实时数据流处理

在现代计算机应用程序中&#xff0c;处理实时数据流是一项关键任务。这种数据流可以是来自传感器、网络、文件或其他源头的数据&#xff0c;需要即时处理并做出相应的决策。Java提供了强大的网络编程工具和库&#xff0c;可以用于处理实时数据流。本文将详细介绍如何使用Java进…

Android之AMessage机制存/取原理(四十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

从零学习开发一个RISC-V操作系统(三)丨嵌入式操作系统开发的常用概念和工具

本篇文章的内容 一、嵌入式操作习系统开发的常用概念和工具1.1 本地编译和交叉编译1.2 调试器GDB&#xff08;The GNU Project Debugger&#xff09;1.3 QEMU模拟器1.4 项目构造工具Make 本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习笔记&#xff0c;计划从RISC…

Gnuplot:安装与使用备忘

异常信息 问题起源于 signalp-4.1 调用 gnuplot 进行画图&#xff0c;在画图的时候提示不支持 png 格式结果&#xff1a; $ gnuplotG N U P L O TVersion 5.0 patchlevel 0 last modified 2015-01-01Copyright (C) 1986-1993, 1998, 2004, 2007-2015Thomas Williams, Coli…

服务网格的工作原理:解析服务网格的核心组件和通信模式

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

实用!Python大型Excel文件处理:快速导入、导出与批量处理

Python 是一种功能强大的编程语言&#xff0c;它提供了丰富的库和工具&#xff0c;使得处理大型 Excel 文件变得容易和高效。下面将介绍如何使用 Python 快速导入、导出和批量处理大型 Excel 文件。下面是一些建议和实践经验&#xff0c;希望能对你有所帮助。 一、Excel 文件处…

【C语言】求一个整数的二进制序列中1的个数的三种方法

方法一&#xff1a;逐位%2法 该方法的初步测试代码如下: int NumberOf1(int n) {int count 0;while (n){if (n % 2 1){count;}n n / 2;}return count; } 众所周知&#xff0c;数据在内存里以补码的形式存储&#xff0c;这是为了简化计算机的结构设计&#xff0c;同时也提…

SpringBoot集成Prometheus实现监控

SpringBoot配置Prometheus pom.xml 引入监控以及prometheus依赖 <dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId></dependency><dependency><groupId>org.springfram…

Excel——时间戳与标准北京时间的互相转换

一、背景 在excel中将13位毫秒级别的时间戳转换为标准的日期格式(yyyy-mm-dd hh:mm:ss.000)&#xff0c;使用如下模板 TEXT(<source_cell>/1000/8640070*36519,"yyyy-mm-dd hh:mm:ss.000") 在excel中将10位秒级别的时间戳转换为标准的日期格式(yyyy-mm-dd h…

【华为云云耀云服务器L实例评测】- 云原生实践,快捷部署人才招聘平台容器化技术方案!

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

WebGL 选中一个表面

目录 选中一个表面 示例程序&#xff08;PickFace.js&#xff09; 代码详解 示例效果 选中一个表面 ​​​​​​​WebGL 选中物体_山楂树の的博客-CSDN博客可以使用同样的方法来选中物体的某一个表面。这一节在PickObject程序的基础上编写了PickFace程序&#xff0c;后…