红黑树(C++实现)

news2025/6/8 22:00:41

文章目录

  • 红黑树的概念
  • 红黑树的性质
  • 红黑树结点的定义
  • 红黑树的插入
  • 红黑树的查找
  • 红黑树的验证
    • 检测是否满足二叉搜索树
    • 检测是否满足红黑树的性质
  • 红黑树与AVL树的比较
  • 包含上述功能的红黑树代码

红黑树的概念

红黑树,是一棵二叉搜索树,但在每一个结点上增加一个存储位表示结点的颜色,可以是Red或Black.通过任何一条从根到叶子上的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的.

在这里插入图片描述

红黑树的性质

1: 每个结点不是红色就是黑色.

2: 根节点是黑色的

3: 如果一个结点是红色的,则它的两个孩子结点是黑色的.

4: 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点.

5: 每个叶子结点都是黑色的(此处的叶子结点指的是NIL结点,也就是空结点).

依据以上5个性质,怎么保证红黑树的最长路径不会超过最短路径的两倍呢?

我们可以假设,在红黑树中,如果每个路径的黑色结点都是N个,那么在棵红黑树中最短路径长度就为N.(假设N为2)
在这里插入图片描述
根据性质3,性质4,那么最长路径也是N个黑色结点,并且由多对一黑一红结点组成,此时,最长路径的长度即为2N.(假设N为2)
在这里插入图片描述
所以,在红黑树中,最长路径长度等于最短路径长度的两倍,不可能超过最短路径的两倍.

红黑树结点的定义

我们实现的是KV模型结构的红黑树,由于红黑树需要旋转,我们将红黑树的结点定义为三叉链结构,并且添加一个新成员_col,代表结点的颜色,方便后续的变色平衡.

enum Colour
{
	RED,
	BLACK
};
template <class K, class V>
struct RBTreeNode            //三叉链
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;         //存储的键值对
	Colour _col;           //标志着红黑树的颜色.
    
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)       //将结点默认定义为红色.
	{
	}
};

思考: 在结点的定义中,为什么要将结点的默认颜色给成红色的?

1:如果我们在红黑树的一条路径中插入一个黑色结点,由于性质4,那么便会影响红黑树的其它路径,为了保持平衡,其他路径都可能需要变色+旋转.
2: 如果我们在红黑树的一条路径中插入一个红色结点,由于性质三,最多只会影响插入的那一条路径,我们只需要对该条路径变色+旋转来保持平衡就行.

所以,为了将红黑树的插入影响降到最低,我们选择将红黑树的结点默认定义为红色.

红黑树的插入

红黑树的插入主要分为三个步骤:
1: 找到红黑树的插入位置.
2: 将插入结点插入到寻找的插入位置.
3: 检测新结点插入后,红黑树的性质是否发生改变,并对其进行相应调整.
其中步骤1和步骤2和AVL树的插入相同,但是步骤三相较AVL树的插入有了较大改变.

那么查看红黑树性质的依据是什么?

在红黑树的插入中:
1: 如果插入结点的父亲结点是黑色的,那么该次插入并没有破坏红黑树的性质,则该次插入并不需要调整平衡.
2: 如果插入结点的父亲结点是红色的,那么根据红黑树的性质2,父亲结点一定有grandfater结点且为黑色结点.
3: 如果插入结点为父亲结点是红色的,那么根据插入结点的叔叔结点(即parent的兄弟结点)的情况,进行调整平衡.

此时有三种情况:

情况一: 插入结点的叔叔存在,并且叔叔结点的颜色为红色.

为了防止出现连续的红结点,我们需要将父亲结点变黑,又为了保持每条路径的黑色结点的个数相同,我们将祖父结点变红,将叔叔结点变黑.因此,我们既可以解决连续出现红色结点的问题,又可以保持每条路径的黑色结点相同.
变色调整抽象图如下:
在这里插入图片描述
注意:
1:如果此时的祖父结点即为整棵树的根结点,:那么为了保持根节点为黑结点的性质,我们需要将祖父变为黑色结点后,退出循环.

2: 如果此时的祖父结点为整棵树的子树,那么我们需要将cur指向祖父节点,parent指向cur的父亲结点后,根据叔叔的情况继续平衡.
在这里插入图片描述
3: 当叔叔存在且为红色结点时,cur是parent的左孩子还是右孩不影响变色平衡.

情况二: 插入结点的叔叔存在,并且叔叔结点的颜色为黑色.

插入前:
1: d,e 可以是空树或者是一个红色结点.

2: c可以是根是黑节点的子树,为例图右侧四种子树的任意一种.
在这里插入图片描述
插入后
在这里插入图片描述
由插入前后例图可见,情况二的出现主要由情况一插入时变色调整导致的.
当叔叔存在且为黑色结点时,此时需要旋转+变色调整平衡,但是旋转时打又有两种情况:
(1):如果祖先,父亲,儿子三个结点的路径为一条直线,首先,于祖父所在的红黑树来说,左子树的路径的长度已经大于最短路径的两倍了,此时需要以祖先为旋转点右单旋(降左子树的高度),为了保持红黑树的每条路径的黑色结点个数相等,我们需要将父亲结点调整为黑色结点,再将祖父结点调整为红色结点.
旋转+变色抽象图如下:
在这里插入图片描述
(2)如果祖先,父亲,儿子三个结点的路径为一条折线,我们需要先以父亲为旋转点左单旋,然后再以祖父为旋转点右单旋,将祖父调整为红色结点,将孩子结点调整为黑色结点,进而保持了每条路径的的的黑色结点个数相同.
在这里插入图片描述

注意:
颜色调整后,无论这棵树是整棵树还是子树,根节点是黑色的,且已经保持平衡,所以无需继续往上处理.

情况三: 插入结点的叔叔不存在,且a/b/c/d/e为空树

情况三又分为两种情况:
1:如果祖父,父亲,孩子结点组成的路径为一条直线:

首先祖父结点左子树的路径长度大于最短路径的两倍,此时需要以祖父右单旋降左子树高度,然后将父亲结点调整为黑色结点,将祖父结点调整为红色结点.
在这里插入图片描述

2:如果祖父,父亲,孩子结点组成的路径为一条折线.
同理,首先,我们先以父亲结点为旋转点左单旋,然后再以祖父为旋转点右单旋,最后,再将孩子结点调整为黑色结点,祖父结点调整为红色结点.
在这里插入图片描述

红黑树的插入函数代码如下:


bool Insert(const pair<K, V>& kv)            //插入函数
	{
		if (_root == nullptr) //如果为空树,直接插入结点就可以了.
		{
			_root = new Node(kv);
			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* grandfater = parent->_parent;
			assert(grandfater);
			assert(grandfater->_col == BLACK);
			if ( parent == grandfater->_left ) //判断父亲在祖父的位置,如果父亲在祖父的左边
			{
				Node* uncle = grandfater->_right;
				if ( uncle && uncle->_col == RED ) //将父亲和叔叔的颜色变黑,祖父的颜色变红.
				{       
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
				
					cur = grandfater;             //继续向上寻找
					parent = grandfater->_parent;
				}
				else           //uncle不存在或者存在且为黑
				{      
				//     g 
			    //  p
               //cur

					if (cur == parent->_left)   //如果祖父,父亲,孩子路径为一条直线
					{
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					//    g
					// p
					//    cur
					else                       //如果祖父,父亲,孩子路径为一条折线.
					{ 
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;                  
						grandfater->_col = RED;  
					}
					//走到这里大该子树的根已经变成黑色结点了,不用再往上循环了.
					break;
				}
			}
			else                    //如果父亲在祖父的右边.
			{
				//   g
				// u   p 
				//       c
 				//
				Node* uncle = grandfater->_left;
				if (uncle && uncle->_col == RED)    //如果叔叔存在且为红色结点.
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					
					cur = grandfater;      //继续向上变色循环.
					parent = grandfater->_parent;
					
				}
				else                    //如果父亲不存在,或者存在且为黑色结点.
				{
					//    g 
					//      p
	                //        c 		
					if (parent->_right == cur)      //如果父亲,祖父,孩子路径为一条直线
					{
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					//    g
					//      p
					//    c
					else                          //如果父亲,祖父,孩子路径为一条折线
					{
						RotateR(parent);
						RotateL(grandfater);
						grandfater->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}

			}
		}
		_root->_col = BLACK; //走到这说明这棵树没有的祖先没有parent,或者有parent但是父亲是黑色的,不需要处理.
		return true;
	}
	void RotateL(Node* parent)         //左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

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

			subR->_parent = ppNode;
		}

	}
	void RotateR(Node* parent)             //右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}

	}

红黑树的查找

查找步骤:
1: 如果所给值大于cur所指结点first,那么从左边遍历查找.
2: 如果所给值大于cur所指结点first,那么从右边遍历查找.
3: 找到了就返回.
4: 但退出循环还没找到,就说明找不到了,返回nullptr.

Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key ) //如果key大于当前结点的first
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key) //如果key小于当前结点的first
			{
				cur = cur->_left;
			}
			else           //寻找到了
			{
				return cur;
			}
		}
		return nullptr;
	}

红黑树的验证

检测是否满足二叉搜索树

红黑树也是一棵接近平衡的二叉搜索树,因此,我们可以通过中序遍历,进而查看是否满足二叉搜索树的性质.

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

检测是否满足红黑树的性质

我们可以检测红黑树的四个性质进而推出红黑树的最长路径长度不超过最短路径的两倍.最后可以判断是否平衡.
1: 性质一可以通过代码的颜色枚举中证明.
2: 性质二和性质三可以在递归中进行判断.
3: 性质四,我们可以设计一个基准值,先求出第一条路径的黑色结点个数为基准值,然后当其他的路径走完时,将其他路径黑色结点个数与该基准值分别比较.

bool PreCheck(Node* root,int& benchMark, int blackNum) //判断性质四
	{
		if (root == nullptr)
		{
			if (benchMark == 0)
			{
				benchMark = blackNum;;
				return true;
			}
			if (blackNum != benchMark)
			{
				cout << "某条黑色结点数量不相等." << endl;
				return false;
			}
			else 
			{
				return true;
			}
		}
		if ( root->_col == RED && root->_parent->_col == RED )//判断性质三
		{
			return false;
		}
		if (root->_col == BLACK)
		{
			++blackNum;
		}
		return PreCheck(root->_left,benchMark ,blackNum) && 
		PreCheck(root->_right,benchMark, blackNum);
	}
	bool _IsBalance( Node* root ) //要从红黑树的四个性质分别判断
	{
		if (root == nullptr )
		{
			return true;
		}
		if (root->_col == RED)       //判断性质一
		{
			cout << "根节点不是黑色结点" << endl;
			return false;
		}
		int benchMark = 0;         //黑色结点数量基准值.
		 return PreCheck(root,benchMark,0);
	}

红黑树与AVL树的比较

1:红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都为O(log2N),红黑树不追求绝对平衡,只需要保证最长路径的长度不超过最短路径的两倍.
2:在插入或者删除中,红黑树降低了旋转的次数,所以需要多次增删操作中,红黑树的性能比AVL树的性能更加高效,而且红黑树的实现相对AVL树来说比较简单,在实践中使用红黑树更多.

包含上述功能的红黑树代码

#include <iostream>
#include <assert.h>
#include <map>
using namespace std;
enum Colour
{
	RED,
	BLACK
};
template <class K, class V>
struct RBTreeNode            //三叉链
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;         //存储的键值对
	Colour _col;           //标志着红黑树的颜色.
    
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
	{
	}
};
template <class K, class V>
struct RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key ) //如果key大于当前结点的first
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key) //如果key小于当前结点的first
			{
				cur = cur->_left;
			}
			else           //寻找到了
			{
				return cur;
			}
		}
		return nullptr;
	}
	void InOrder()
	{
		_InOrder(_root);
	}
	bool Insert(const pair<K, V>& kv)            //插入函数
	{
		if (_root == nullptr) //如果为空树,直接插入结点就可以了.
		{
			_root = new Node(kv);
			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* grandfater = parent->_parent;
			assert(grandfater);
			assert(grandfater->_col == BLACK);
			if ( parent == grandfater->_left ) //判断父亲在祖父的位置,如果父亲在祖父的左边
			{
				Node* uncle = grandfater->_right;
				if ( uncle && uncle->_col == RED ) //将父亲和叔叔的颜色变黑,祖父的颜色变红.
				{       
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
				
					cur = grandfater;             //继续向上寻找
					parent = grandfater->_parent;
				}
				else           //uncle不存在或者存在且为黑
				{      
				//     g 
			    //  p
               //cur

					if (cur == parent->_left)   //如果祖父,父亲,孩子路径为一条直线
					{
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					//    g
					// p
					//    cur
					else                       //如果祖父,父亲,孩子路径为一条折线.
					{ 
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;                  
						grandfater->_col = RED;  
					}
					//走到这里大该子树的根已经变成黑色结点了,不用再往上循环了.
					break;
				}
			}
			else                    //如果父亲在祖父的右边.
			{
				//   g
				// u   p 
				//       c
 				//
				Node* uncle = grandfater->_left;
				if (uncle && uncle->_col == RED)    //如果叔叔存在且为红色结点.
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					
					cur = grandfater;      //继续向上变色循环.
					parent = grandfater->_parent;
					
				}
				else                    //如果父亲不存在,或者存在且为黑色结点.
				{
					//    g 
					//      p
	                //        c 		
					if (parent->_right == cur)      //如果父亲,祖父,孩子路径为一条直线
					{
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					//    g
					//      p
					//    c
					else                          //如果父亲,祖父,孩子路径为一条折线
					{
						RotateR(parent);
						RotateL(grandfater);
						grandfater->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}

			}
		}
		_root->_col = BLACK; //走到这说明这棵树没有的祖先没有parent,或者有parent但是父亲是黑色的,不需要处理.
		return true;
	}
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
private:
	bool PreCheck(Node* root,int& benchMark, int blackNum) //判断性质四
	{
		if (root == nullptr)
		{
			if (benchMark == 0)
			{
				benchMark = blackNum;;
				return true;
			}
			if (blackNum != benchMark)
			{
				cout << "某条黑色结点数量不相等." << endl;
				return false;
			}
			else 
			{
				return true;
			}
		}
		if ( root->_col == RED && root->_parent->_col == RED )//判断性质三
		{
			return false;
		}
		if (root->_col == BLACK)
		{
			++blackNum;
		}
		return PreCheck(root->_left,benchMark ,blackNum) && 
		PreCheck(root->_right,benchMark, blackNum);
	}
	bool _IsBalance( Node* root ) //要从红黑树的四个性质分别判断
	{
		if (root == nullptr )
		{
			return true;
		}
		if (root->_col == RED)       //判断性质一
		{
			cout << "根节点不是黑色结点" << endl;
			return false;
		}
		int benchMark = 0;         //黑色结点数量基准值.
		 return PreCheck(root,benchMark,0);
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":"<< root->_kv.second << endl;
		_InOrder(root->_right);
	}
	void RotateL(Node* parent)         //左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

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

			subR->_parent = ppNode;
		}

	}
	void RotateR(Node* parent)             //右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}

	}
	Node* _root = nullptr;

};
//测试1
void TestRBTree1()
{
	int a[] = { 16,3,7,11,9,26,18,14,15,0,1 };
	RBTree<int, int> t1;
	for (auto e : a)
	{
t1.Insert(make_pair(e, e));
	}
	t1.InOrder();
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

//测试2
//随机数测试,使用10000个随机数测试
void TestRBTree2()
{
	size_t N = 10000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();               //x接收产生的随机数.
		t1.Insert(make_pair(x, i));   //在树中插入随机数.
	}
	t1.InOrder();
	cout << t1.Find(1)->_kv.first << endl;
	cout << "IsBanlance:" << t1.IsBalance() << endl;
}

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

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

相关文章

《Netty》从零开始学netty源码(三十五)之DefaultChannelPromise

DefaultChannelPromise 在分析Unsafe或者ChannelPipeline的方法时经常出现Promise类&#xff0c;它用来保存channel的状态值&#xff0c;默认使用的是DefaultChannelPromise&#xff0c;接下来分析这个类。 类结构图 属性值 DefaultChannelPromise中有一个属性result&#xf…

Python之测试框架搭建【一】(毛坯框架)

前言一、框架基础&#xff08;从最小步骤入手_毛坯框架&#xff09; 1&#xff09;首先我们自己建立一个项目&#xff0c;这个项目里面有testcases文件夹&#xff0c;testdatas文件夹2&#xff09;在testdatas里面放测试数据3&#xff09;建立一个main文件4&#xff09;输出测试…

ASP.NET 记录 HttpRequest HttpResponse HttpServerUtility

纯属个人记录,会有错误 HttpRequest Browser是获取客户端浏览器的信息 Cookies是获取客户端的Cookies QueryString是获取客户端提交的数据 ServerVariables是获取服务器端或客户端的环境变量信息 Browser 语法格式: Request.Browser[“浏览器特性名”] 常见的特性名 名称说…

【grafana】使用多级变量解决Granfana模板变量中的大小限制

前言 最近对公司的Kafka 集群集成了 exporter prometheus granfana 服务&#xff0c;我们将 topic 变量抽象成模板变量&#xff0c;此时发现 granfana 变量有 10k 的个数限制。导致我们显示 topic 名称不全。 效果如下&#xff1a;全选后只有 10k 个变量。 解决 一番百度…

【Python】【进阶篇】二十二、Python爬虫的BS4解析库

目录二十二、Python爬虫的BS4解析库22.1 BS4下载安装22.2 BS4解析对象22.3 BS4常用语法1) Tag节点22.4 遍历节点22.5 find_all()与find()1) find_all()2) find()22.6 CSS选择器二十二、Python爬虫的BS4解析库 Beautiful Soup 简称 BS4&#xff08;其中 4 表示版本号&#xff0…

C语言函数大全-- k 开头的函数

C语言函数大全 本篇介绍 C语言函数大全-- k 开头的函数 1. kcalloc 1.1 函数说明 函数声明函数功能void *kcalloc(size_t n, size_t size, gfp_t flags);它是 Linux 内核中的一个函数&#xff0c;用于在内核空间分配一块连续的指定大小的内存&#xff0c;它与标准库函数 ca…

RK3568平台开发系列讲解(设备驱动篇)内核空间与用户空间数据交互实验

🚀返回专栏总目录 文章目录 一、内核空间与用户空间二、用户空间和内核空间数据交换三、实验程序沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍对内核空间与用户空间的数据交换功能。 一、内核空间与用户空间 Linux 系统将可访问的内存空间分为了两个部…

指针的运算

在C语言中&#xff0c;指针是一个非常重要的概念。指针可以用来访问和修改内存中的数据&#xff0c;也可以用来进行指针的运算。在本篇博客中&#xff0c;我们将探讨指针的解引用、指针与整数的加减运算、同类型指针间的减法运算和指针的关系运算。 指针的解引用 在C语言中&a…

网络安全运维人员面临的痛点分析

1. 工作压力大&#xff1a;网络安全运维人员需要时刻关注系统的安全状况&#xff0c;发现并修复漏洞&#xff0c;保护系统免受攻击和数据泄露的风险。这需要他们不断学习新知识、掌握新技能&#xff0c;并且在短时间内快速响应和处理各种安全问题&#xff0c;这种高强度的工作压…

Arcgis小技巧【12】——ArcGIS标注的各种用法和示例

标注是将描述性文本放置在地图中的要素上或要素旁的过程。 本文整理了ArcGIS中的各种标注方法、可能遇到的问题和细节&#xff0c;内容比较杂&#xff0c;想到哪写到哪。 一、正常标注某一字段值的内容 右键点击【属性】&#xff0c;在【标注】选项卡下勾选【标注此图层中的的…

pr 是什么——可以做什么

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…

Boosting算法预测银行客户流失率

Boosting算法预测银行客户流失率 描述 为了防止银行的客户流失&#xff0c;通过数据分析&#xff0c;识别并可视化哪些因素导致了客户流失&#xff0c;并通过建立一个预测模型&#xff0c;识别客户是否会流失&#xff0c;流失的概率有多大。以便银行的客户服务部门更加有针对…

TS+Axios的封装(vue3.x环境)

1. 创建axios实例 # 项目src下新建utils/request.ts # npm i axios 是必须滴 import axios from axiosconst baseURL https://ilovesh.com // 基地址 const instance axios.create({baseURL,timeout: 3000 // 请求超时时间 (3秒超时) })export { baseURL, instance } 2. 添加…

龙蜥社区理事长马涛当选开放原子开源基金会 TOC 副主席

近日&#xff0c;开放原子开源基金会举办了技术监督委员会&#xff08;以下简称“TOC”&#xff09;第六十九次全体会议&#xff0c;会上针对新一届 TOC 成员改选进行了公正公平的投票&#xff0c;龙蜥社区理事长马涛当选开放原子开源基金会 TOC 副主席。 &#xff08;图左二/龙…

(排序8)归并排序(非递归实现),归并排序的外部排序应用(文件排序)的简单了解

归并排序递归版本 void _MergeSort(int* arr, int left , int right, int* tmp) {if (left > right){return;}int mid (left right) / 2;_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid 1, right, tmp);int begin1 left;int end1 mid;int begin2 mid 1;int e…

一文掌握 Python、Anaconda、PyCharm 的安装和使用

本文将介绍一个较为全面的 Python 开发环境的搭建与基本使用&#xff0c;包括 Python 解释器的安装使用、PyCharm 编辑器的安装使用、Anaconda 的安装使用和库管理工具 pip 的使用。 PyCharm Anaconda 是一个很完备的 Python 开发环境&#xff0c;也是绝大多数开发者使用的开发…

E6410安装PVE直通显卡安装LibreELEC系统

文章目录查看cpu是否支持直通音频笔记本关盖设定BIOS设定启动内核IOMMU支持添加驱动黑名单绑定核显到vfio模块然后新建一个虚拟机安装LibreELEC注意事项重启 VM 时 GPU 直通失败如果直通后遇到问题了&#xff0c;开机出现各种问题&#xff0c;进不去系统&#xff0c;或者网页进…

【MySQL数据库原理】数据库批量导入美团NLP分类数据集Meituan-Dianping/asap

目录 数据集简单介绍完整代码实现参考资料数据集简单介绍 美团点评数据集 (Meituan-Dianping/asap) 是一个中文自然语言处理 (NLP) 数据集,由美团点评公司收集和发布。该数据集用于评估和开发中文文本分类和情感分析模型,包括情感极性分类、食物安全文本分类和自定义分类等任…

【分布式应用】Zabbix——安装

目录 第一章zabbix概述 1.1.什么是监控概念 1.2.zabbix概述 1.3.zabbix 主要特点 1.4.zabbix主要功能 1.5.zabbix运行机制 1.6.zabbix 监控原理 1.7zabbix的架构&#xff0c;数据流向以及原理在图中做总结 1.8.数据采集模式 1.9.zabbix监控模式 第二章.安装 部署 za…

Hyper-v下安装CentOS-Stream-9

1、我不想要动态扩展的硬盘&#xff0c;固定大小硬盘性能更高&#xff0c;所以这里我先创建一个固定硬盘&#xff08;如果你想用动态扩展的硬盘&#xff0c;那么可以省略前面几步&#xff0c;直接从第7步开始&#xff0c;并在第12步选择创建可动态扩展的虚拟硬盘&#xff09;&a…