RBTree(红黑树)的介绍和实现

   欢迎来到杀马特的主页:羑悻的小杀马特.-CSDN博客

目录

​编辑

一·红黑树介绍:

1.1红黑树概念:

1.2红黑树遵循的原则:

 1.3红黑树效率分析:

二.红黑树的实现:

2.1红黑树结构:

2.2红黑树节点的插入:

2.2.1 顺序性插入:

2.2.2.1变色(叔叔节点为红色):

 2.2.2.2变色(叔叔节点为红色)(简单总结版):

2.2.3.1 左单旋+变色(叔叔节点为黑色或者不存在):

 2.2.3.2左单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.4.1 右单旋+变色(叔叔节点为黑色或者不存在):

2.2.4.2 右单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.5.1 左右双旋+变色(叔叔节点为黑色或者不存在):

2.2.5.2 左右双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

2.2.6.1右左双旋+变色(叔叔节点为黑色或者不存在):

 2.2.6.2右左双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

 2.3红黑树的查找:​编辑

2.4红黑树的验证: 

2.5红黑树的删除:

三·代码总结: 

四·红黑树创建总结(仅个人理解): 


一·红黑树介绍:

1.1红黑树概念:

首先可以把它理解成一颗二叉搜索树,但是它的节点会有颜色不是红就是黑,可以这么理解:就是avl树把平衡因子去掉并改成颜色再加以修改,但是平衡还是有点差别,高度可能会差别大于2,因此只能勉勉强强这麽说。这样可以保证如果存在最短路径必然都是黑节点,如果存在最长路径必然一般黑一般红,其他路径就位于它们之间了,它根据特性也可以默认成平衡的了,这里也要注意空节点默认为黑色(这里也可有头结点也可无,如果有的话就是头结点为黑色,然后它的parent指针指向根节点,左指针指向树最左边孩子,右指针指向树最右边的孩子)。

1.2红黑树遵循的原则:

简称红黑树四大原则:

①它的结点不是红⾊就是⿊⾊ 。

②根结点是⿊⾊的。

③如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的 红⾊结点,故红红节点不能连续出现。

 ④对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点。

 1.3红黑树效率分析:

这里如果我们假设树的最短高度是h,而最长高度就是2h(假设存在的话):为什么?:这里若果是最短(由于黑色节点相同)故只需要它无红色几点即全黑设高度为h,那么最长的只能是红黑交错出现(红色不能相连)故就是2h了。 那么我们证明一下它的效率范围:

这里最短路路径就是18 10 而最长就是 18 30 40 50,我们以最坏情况考虑(因为在最短和最长之间的点是怎样分布不清楚故这样)那么最好就是2^h-1,最长是2^2h-1,也就是节点是这些,设节点为n解答时间复杂度=logn与2*logn之间,故都可以近似成o(logn)。

二.红黑树的实现:

2.1红黑树结构:

这里和avl树大致相同就是把平衡因子等换成了颜色的枚举类型了。

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

template<class K, class V>
class RBTree {
	using Node = RBTreeNode<K, V>;
public:
 private:
Node* _root=nullptr;

};

2.2红黑树节点的插入:

这里剧透一下:那么插入过程大概是怎么样的呢?首先我们先找到了大小关系合适的节点;然后如果我们插入的是黑色就破坏了黑色相等的规则,故插入的节点一定是红色,那么就可以推出它的父亲是红或者黑,如果是黑色那么可以直接成功插入了,但是如果是红色的话,爷爷节点一定是黑色,此时关键就要看叔叔节点了:这时候分为三种情况为黑,为红,不存在(为黑),故这里还可以归总成两种:1·叔叔节点为红色,2·叔叔节点为黑色。----->"调整的环节又叫"关键看叔叔"

下面说一下这个插入的思路:就是当我们插入后父亲是黑色就退出,如果是红色:那么就继续向上调整(这里可以直到如果父亲为红肯定存在爷爷即为黑,但是爷爷的父亲不一定存在,那么如果下一次向上调整爷爷变成了孩子,那么可能爷爷就是根节点,但是会被变红,故需要最后变黑),此时就要看叔叔了。根据那两种情况进行旋转或者不旋转。 

代码思路:首先分为两大方向分别是父亲是爷爷的左孩子与右孩子,此时叔叔就相反了,然后再分叔叔为黑或者红(直接变色),为黑:则要分孩子插入的是父亲的左还是右进行对应的左 右单旋和左右 右左双旋 。

2.2.1 顺序性插入:

就是按照比原节点值小就向左走,大就向右走,然后走到空,判断插入的是左还是右进行插入即可。

if (_root == nullptr)
{
	_root = new Node(kv);
	_root->_col = BLACK;

	return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
	if (cur->_kv.first < kv.first)
	{
		parent = cur;
		cur = cur->_right;
	}
	else if (cur->_kv.first > kv.first)
	{
		parent = cur;
		cur = cur->_left;
	}
	else
	{
		return false;
	}
}

cur = new Node(kv);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
	parent->_right = cur;
}
else
{
	parent->_left = cur;
}

cur->_parent = parent;

2.2.2.1变色(叔叔节点为红色):

叔叔节点为红色的情况(这里首先要确定叔叔是爷爷的左还是右(父亲,叔叔的连接如何)):

假设插入的是c这个节点,故此时父亲是红色,故要变色(由于要保证每条路黑色节点个数不变,故我们可以先把父亲和叔叔节点变成黑色,爷爷变成红色就行了-->这样可以保证黑色节点相当于没变化。),接着就是向上调整(让爷爷节点变成孩子)看看父亲节点颜色,红色:就看叔叔颜色,进行相关变化黑色:退出。

代码实现:

if (uncle && uncle->_col == RED) {
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上处理:
				cur = grandfather;
				parent = cur->_parent;
}

 2.2.2.2变色(叔叔节点为红色)(简单总结版):

判断父亲叔叔方位,叔叔为红,父亲叔叔变黑,爷爷变红继续向上走。

2.2.3.1 左单旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

我们首先要想什么时候会左单旋然后变色呢?(首先保持好它的平衡性,故还要旋转变平衡,最后保证节点颜色规则。)  因此左单旋肯定是右边高了,而根据标题就可以知道此时叔叔节点是黑色,但是它是不是第一次插入就黑色了呢?肯定不可能,下面看一张图解释一下:

但是当叔叔不存在时候(虽然也是黑色)但是它只能是新插入的节点而不是向上调整才会出现:

这样我们就明白了把。因此接下来如果要进行左旋故父亲肯定是爷爷的右,孩子也是父亲的右(无论叔叔存在还是存在为黑都一样当黑色处理进行左旋。) 

故我们要先保持它的平衡(右边高了)(利用左旋(对g)):

①首先是叔叔为空(黑):

这样才能保持平衡后,还符合颜色规则。 

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

这样我们左单旋的所有情况就完了。

代码实现:

//又分两种情况:孩子插入的是左还是右:
if (cur == parent->_right) {
				RotateL(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
}

 2.2.3.2左单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷右,孩子连接父亲右,叔叔存在且黑色(不存在),进行左旋,爷爷变红,父亲变黑。

2.2.4.1 右单旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

这里其实就和单旋一样(叔叔存在黑色或者不存在),但是此时就是父亲连接爷爷的左,孩子连接父亲左了。

下面就分情况画图简单看一下吧:

①首先是叔叔为空(黑):

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

代码实现:

//又分两种情况:孩子插入的是左还是右:
if (cur == parent->_left) {
				RotateR(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
}

2.2.4.2 右单旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷左,孩子连接父亲左,叔叔存在且黑色(不存在),进行旋右,爷爷变红,父亲变黑。

2.2.5.1 左右双旋+变色(叔叔节点为黑色或者不存在):

注:这里是由于要进行不同方向的那四种旋转我们才分孩子连接的是父亲的左还是右。

首先我们要知道为什么会出现左右双旋?个人理解:它是右旋样子下孩子插入在父亲右边才会形成的,故此时我们还要分叔叔节点为存在为黑色以及不存在情况进行画图分析。

先说一下它的情况吧:父亲连节爷爷的左边,孩子连接父亲的右边。

①首先是叔叔为空(黑):

对于叔叔是空只能是一开始第一次插入就这样出现的(上面也分析了),下面展示一下它插入并完成旋转和变色的图:

此时我们可以看出进行双旋后把孩子变黑,爷爷变红了。 

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

还是老样子叔叔为黑,则孩子节点一定是向上调整由黑变红来的,展示一下:

同理也是孩子变黑,爷爷变红,才能符合既平衡又符合黑色相同规则。 

代码实现:

RotateLR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;

2.2.5.2 左右双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷的左,孩子连接父亲的右,叔叔为黑或者不存在,此时爷爷变红,孩子变黑。

2.2.6.1右左双旋+变色(叔叔节点为黑色或者不存在):

其实此时的右左双旋和上面的左右双旋相似,只不过是父亲连接了爷爷的右,而孩子连接了父亲的左而已,下面就不过多重复了,画图看一下:

①首先是叔叔为空(黑):

②其次是叔叔为存在,黑(就是由最底下插入向上调整时候出现的):

代码实现:

else {
				RotateRL(grandfather);
				cur->_col = BLACK;
				grandfather->_col = RED;
}

 2.2.6.2右左双旋+变色(叔叔节点为黑色或者不存在)(简单总结版):

父亲连接爷爷的右,孩子连接父亲的左,叔叔为黑或者不存在,此时爷爷变红,孩子变黑。

 2.3红黑树的查找:

这里遵循avl的查找只查找pair的first(根据)而不管颜色故直接用它的查找就行。

代码实现:

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

  return nullptr;
 }

2.4红黑树的验证: 

这时候我们就要根据红黑树的四大原则了,但是这里我们只用到了三个:

①根节点为空就是不为空必须是黑色。

②每个节点(这里认为的是根节点起,到空的每条支路的黑色节点数相同)。

③红色节点的孩子一定是黑色节点,但是这里如果这么找,还要判断孩子存不存在然后看颜色,因此我们可以倒着来即找到一个节点是红色,则它的父亲不能是红色(即红色节点不相连)。

黑色节点相同的话,我们可以先往最左或者最右遍历至空,找到一条路径的黑色节点个数作为标准,然后利用递归到空记录blackcount与它比较发现不等就false,红色不相连:遍历途中,发现红色就看父亲颜色,如果为红色就false。

代码展示:

 bool is_rbtree() {
  int count = 0;
  if (_root == nullptr) return true;
  if (_root->_col == RED) return false;//根节点存在就为黑
  Node* cur = _root;
  while (cur) {
	  if (cur->_col == BLACK) count++;
	  cur = cur->_left;
  }
  return check(_root, count, 0);
  }


 bool check(Node* root, int reference, int blackcount) {//查找树中要满足黑色相同以及红的节点只能连两个黑的(转化成红的节点父亲不能是红的):这里只有都满足才返回true,故可以判断错就返false

	if (root == nullptr) {//检查每条支路黑色节点数量相同
		if (blackcount != reference) {
			cout << "发现黑色节点不等的支路" << endl;
			return false;
		}
		else return true;
		
	}
	if (root->_col == RED && root->_parent->_col == RED) {
		cout << "发现连续的红色节点" << endl;//不能有连续的红色节点
		return false;
	}

	if (root->_col == BLACK) blackcount++;
	return check(root->_left, reference, blackcount) && check(root->_right, reference, blackcount);
}

2.5红黑树的删除:

这里和上次的avl树一样采取的是替换制删除,可以根据上篇avl树的删除来仿写,但是就是把平衡因子换成了红黑色的颜色而已。 

三·代码总结: 

#pragma once
#include<iostream>
using namespace std;
enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};
template<class K, class V>
class RBTree {
	using Node = RBTreeNode<K, V>;
public:
	bool insert(const pair<K, V>& kv) {//先按照avl树插入节点,然后最后判断是否旋转来修改颜色
		//第一次插入要保证根节点为黑色:
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;

			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		
		cur->_parent = parent;

		//调整颜色等:(关键看叔叔):
		while (parent && parent->_col == RED) {//父亲为黑色则不用再次向上调整
			Node* grandfather = parent->_parent;//爷爷节点一定存在(当父亲节点为红)一定为黑:要么继续调整要么为根节点最后改黑
			//下面分两种大情况分别是父亲为爷爷的left和right:
			if (parent == grandfather->_left) {//孩子插入后左高
				///关键看叔叔节点的情况:
				//叔叔存在且为红:
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理:
					cur = grandfather;
					parent = cur->_parent;
				}
				//叔叔不存在或者为黑;
				else {
					//又分两种情况:孩子插入的是左还是右:
					if (cur == parent->_left) {
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else {
						RotateLR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
				}
			 }
			else {//孩子插入后右高
				//叔叔存在且为红:
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理:
					cur = grandfather;
					parent = cur->_parent;
				}
				//叔叔不存在或者为黑;
				else {
					//又分两种情况:孩子插入的是左还是右:
					if (cur == parent->_right) {
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else {
						RotateRL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
				}
			}
		}
		_root->_col = BLACK;//如果到头爷爷原来是黑的被改成了红且是根节点故改回黑
		return true;

	  }

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

		  return nullptr;
	  }

	  bool is_rbtree() {
		  int count = 0;
		  if (_root == nullptr) return true;
		  if (_root->_col == RED) return false;//根节点存在就为黑
		  Node* cur = _root;
		  while (cur) {
			  if (cur->_col == BLACK) count++;
			  cur = cur->_left;
		  }
		  return check(_root, count, 0);
	   }

	  int treeheight() {
		  return _treeheight(_root);
	   }
	  int size() {
		  return _size(_root);
	  }
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:


	void RotateL(Node* parent) {//左旋
		Node* subr = parent->_right;
		Node* subrl = subr->_left;
		//处理sublr:
		parent->_right = subrl;
		if (subrl) subrl->_parent = parent;//sublr为空不能访问

		Node* pp = parent->_parent;//保存parent的父节点指针
		//调节新旧“parent”位置:
		subr->_left = parent;
		parent->_parent = subr;

		if (pp == nullptr) {
			_root = subr;
			subr->_parent = nullptr;
		}
		else {
			if (parent == pp->_left) pp->_left = subr;
			else pp->_right = subr;
			subr->_parent = pp;
		}
		

	}


	void RotateR(Node* parent) {//右旋
		Node* subl = parent->_left;
		Node* sublr = subl->_right;

		parent->_left = sublr;
		if (sublr) sublr->_parent = parent;

		Node* pp = parent->_parent;
		subl->_right = parent;
		parent->_parent = subl;
		if (pp == nullptr) {
			_root = subl;
			subl->_parent = nullptr;
		}
		else {
			if (parent == pp->_left) pp->_left = subl;
			else pp->_right = subl;
			subl->_parent = pp;
		}
		
	}

	void	RotateLR(Node* parent) {//左右双旋
		Node* subl = parent->_left;
		Node* sublr = subl->_right;
		RotateL(subl);
		RotateR(parent);

	}

	void RotateRL(Node* parent) {//右左双旋
		Node* subr = parent->_right;
		Node* subrl = subr->_left;
		RotateR(subr);
		RotateL(parent);

	}
	bool check(Node* root, int reference, int blackcount) {//查找树中要满足黑色相同以及红的节点只能连两个黑的(转化成红的节点父亲不能是红的):这里只有都满足才返回true,故可以判断错就返false

		if (root == nullptr) {//检查每条支路黑色节点数量相同
			if (blackcount != reference) {
				cout << "发现黑色节点不等的支路" << endl;
				return false;
			}
			else return true;
			
		}
		if (root->_col == RED && root->_parent->_col == RED) {
			cout << "发现连续的红色节点" << endl;//不能有连续的红色节点
			return false;
		}

		if (root->_col == BLACK) blackcount++;
		return check(root->_left, reference, blackcount) && check(root->_right, reference, blackcount);
	}
	  

	int _treeheight(Node* root) {
		if (root == nullptr)  return 0;
		int leftheight = _treeheight(root->_left);
		int rightheight = _treeheight(root->_right);
		return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
	}
	int _size(Node* root) {
		if (root == nullptr) return 0;
		return _size(root->_left) + _size(root->_right) + 1;
	 }
	             

	   
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	Node* _root=nullptr;
	
 };


测试用例:

#define _CRT_SECURE_NO_WARNINGS
#include"rbtree.h"

int main() {
	RBTree<int, int> t;
	// 常规的测试用例
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的带有双旋场景的测试用例
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };

	for (auto e : a)
	{
		t.insert({ e, e });
	}

	t.InOrder();
	cout << t.is_rbtree() << endl;
	cout <<"树的节点大小:"<< t.size() << endl;
	cout <<" 树的高度:"<< t.treeheight() << endl;


}

四·红黑树创建总结(仅个人理解): 

首先的话,我们来谈一谈插入吧:这里可以这么理解:插入就按照二插搜索树插入,然后就是接下来要保证平衡和颜色了,我们插入的红色如果发现父亲是黑色就结束,如果红色,我们三步走:

第一步:判断父亲为爷爷的左还是右。

第二步:看叔叔存在为黑(或者不存在)还是为红。

第三步:看孩子连接父亲的左还是右。

下面画图帮助理解一下:

这里我们可能会说为什么只有叔叔是红色,才会孩子变爷爷向上调整,叔叔是其他就不用?因为当叔叔是其他的时候就会旋转,此时最顶上就是黑色,而旋转带来的改变犹如上调,故可以类似于做到了上调操作。 

本篇结束:希望对每一位读者通过此篇文章对有关红黑树的认识能有所增加,哪怕一丢丢,也更能体现这篇文章存在的价值,感谢各位的用心阅读以及支持!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PAT甲级-1127 ZigZagging on a Tree

题目 题目大意 给出一棵树的中序和后序遍历&#xff0c;要求按层序输出这棵树&#xff0c;但是按照从左到右&#xff0c;再从右到左&#xff0c;再从左到右的顺序。 思路 由中序遍历和后序遍历可以构造出一棵二叉树。观察题目中要求的输出顺序&#xff0c;发现层数为奇数的都…

第十五章 RabbitMQ延迟消息之延迟插件

目录 一、引言 二、延迟插件安装 2.1. 下载插件 2.2. 安装插件 2.3. 确认插件是否生效 三、核心代码 四、运行效果 五、总结 一、引言 上一章我们讲到通过死信队列组合消息过期时间来实现延迟消息&#xff0c;但相对而言这并不是比较好的方式。它的代码实现相对来说比…

等保制度要求企业保障数据存储安全,天锐绿盾强大加密技术实现文档防泄密

网络安全等级保护&#xff08;等保&#xff09;制度是我国信息安全保障工作的基本制度和基本国策&#xff0c;是开展信息安全工作的基本方法&#xff0c;是促进信息化、维护国家信息安全的基本保障。等保不仅是对网络&#xff08;含信息系统、数据&#xff09;实施分等级保护、…

TypeScript数据类型限定(基本数据类型,void,数组,元组,枚举,any,unknown,never,函数,自定义数据类型,联合类型和交叉类型)

一、安装解析ts的工具包 node.js只认识js代码&#xff0c;不认识ts代码。 需要将ts代码转化为js&#xff0c;然后就可以在node.js中运行了。 安装步骤&#xff1a;打开终端&#xff0c;输入命令npm i -g typescript回车 typescript是用来解析ts的工具包。提供了tsc命令&…

基于SpringBoot+Vue+Uniapp微信小程序快递管理系统(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

量化策略交易之PTrade量化软件如何获取逐笔委托行情!get_individual_entrust

get_individual_entrust– 获取逐笔委托行情 get_individual_entrust(stocksNone, data_count50, start_pos0, search_direction1, is_dictFalse) 使用场景 该函数在交易模块可用 接口说明 该接口用于获取当日逐笔委托行情数据。 注意事项&#xff1a; 1、沪深市场都有逐…

C++,STL 033(24.10.15)

内容 queue容器&#xff08;队列&#xff09;的常用接口。 代码 #include <iostream> #include <string> #include <queue> // 注意包含queue容器&#xff08;队列&#xff09;的头文件using namespace std;class Person { public:string m_Name;int m_Age…

Android targetSdkVersion 升级为34 问题处理

原因是发布到GooglePlay遭到拒绝&#xff0c;需要最低API level为34。之前为31&#xff0c;感觉还挺高的&#xff0c;但是GooglePlay需要的更高。 记录下处理问题&#xff1a; 1.升级gradle版本为8.0.2 之前是&#xff1a; classpath com.android.tools.build:gradle:7.1.0-…

【C语言】数据类型

C语言常用数据类型 int 整型 4字节float 浮点型 4字节double 双精度浮点类型 8字节char 字符型 1字节构造类型&#xff1a;数组&#xff08;多个相同类型的变量组合&#xff09;构造类型&#xff1a;结构体&#xff08;多个不同类型的变量组合&#xff09; #include <stdi…

react18+react-transition-group实现路由切换过度

效果如下 官网安装对应的插件 创建对应的样式 .fade-enter {opacity: 0; } .fade-exit {opacity: 1; } .fade-enter-active {opacity: 1; } .fade-exit-active {opacity: 0; } .fade-enter-active, .fade-exit-active {transition: opacity 500ms; }const location useLoca…

Mysql—高可用集群MHA

1:什么是MHA&#xff1f; MHA&#xff08;Master High Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切…

【py】使用numpy读取文件,并统计

我们需要编写一个脚本来读取文本文件&#xff0c;然后进行字数统计和词频统计。 以下是一个简单的Python脚本&#xff0c;它使用numpy来处理数据。 首先&#xff0c;确保你已经安装了numpy库。如果没有安装&#xff0c;可以通过运行pip install numpy来安装。 然后&#xff0c…

Gin框架操作指南06:POST绑定(下)

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;没用过Gin的读者强烈建议先阅读第一节&#xff1a;Gin操作指南&#xff1a;开山篇。 本节继续演示POST绑定&#xff0c;包括将request-body绑定到不同的结构体中&#x…

小猿口算辅助工具(nodejs版)

github 地址&#xff1a;https://github.com/pbstar/xyks-helper 实现原理 通过屏幕截图截取到题目区域的两个数字&#xff0c;然后通过 ocr 识别出数字&#xff0c;最后通过计算得出答案&#xff0c;并通过模拟鼠标绘制答案。 依赖插件 node-screenshots&#xff1a;屏幕截…

微知-Mellanox驱动中的iSCSI是什么?有哪三种网络存储有哪三种?iSER是什么?(iSCSI协议(总线),SAN 存储区域网络)

背景 本文根据Mellanox网卡驱动中关于iSCSI模块&#xff0c;来介绍iSCSI是什么&#xff1f;该技术发展演进背景&#xff1f; 关于iSCSI iSCSI是一种协议&#xff0c;SCSI是总线。比如常说的SAS&#xff08;Serial Attach SCSI&#xff09;存储盘对比与家用的SATA&#xff0…

Facebook上的隐私保护:如何加强个人数据的安全性?

在数字化时代&#xff0c;个人数据的保护已成为用户日益关注的话题&#xff0c;尤其是在社交媒体平台如Facebook上。用户在享受社交媒体带来的便利时&#xff0c;如何有效保护个人隐私&#xff0c;维护自身的数据安全&#xff0c;成为了一个亟需解决的问题。 Facebook的隐私保护…

算法备案不再难!一篇文章让你成为备案达人

随着互联网的迅猛发展&#xff0c;算法推荐已成为众多互联网信息服务的重要组成部分。然而&#xff0c;算法推荐技术的广泛应用也带来了一系列风险和挑战。为了保障公众利益&#xff0c;规范互联网信息服务算法推荐活动&#xff0c;相关部门出台了《互联网信息服务算法推荐管理…

Dubbo接口级和应用级注册,Dubbo消费者注册到Nacos

学习文档 视频学习 代码演示环境 Dubbo 3.2.9Nacos 2.3.0 一、什么是接口级和应用级 假设有一个服务A&#xff0c;里面提供了2个Dubbo接口XdxOneService、XdxTwoService&#xff0c;Dubbo生产者把服务注册到Nacos&#xff08;或其它的注册中心&#xff09; 以应用级别注册&a…

MySQL之Buffer Pool缓冲池详解

为什么要有 Buffer Pool&#xff1f; 虽然说 MySQL 的数据是存储在磁盘里的&#xff0c;但是也不能每次都从磁盘里面读取数据&#xff0c;这样性能是极差的。 要想提升查询性能&#xff0c;加个缓存就行了嘛。所以&#xff0c;当数据从磁盘中取出后&#xff0c;缓存内存中&am…

软件功能测试重点和流程有哪些?专业软件测评服务公司推荐

软件功能测试就是对产品的各功能进行验证&#xff0c;根据功能测试用例&#xff0c;逐项测试&#xff0c;检查产品是否达到用户要求的功能。功能测试也叫黑盒测试或数据驱动测试&#xff0c;只需考虑需要测试的各个功能&#xff0c;不需要考虑整个软件的内部结构及代码.一般从软…