【数据结构高阶】红黑树

news2024/11/25 20:21:41

目录

一、红黑树的概念

二、红黑树的性质

2.1 红黑树与AVL树的比较

三、红黑树的实现

3.1 红黑树节点的定义

3.2 数据的插入

3.2.1 红黑树的调整思路

3.2.1.1 cur为红,f为红,g为黑,u存在且为红

3.2.1.2 cur为红,f为红,g为黑,u不存在/u存在且为黑

3.2.1.2.1 g、f、cur构成一条直线

3.2.1.2.2 g、f、cur构成一条折线

3.2.2 调整部分的代码实现

3.3 红黑树的验证

3.4 测试代码

四、红黑树实现完整代码


一、红黑树的概念

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

二、红黑树的性质

● 每个结点不是红色就是黑色

● 根节点是黑色的

● 如果一个节点是红色的,则它的两个孩子结点是黑色的(不允许出现连续的红色节点)

● 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点(从根节点到每个叶子节点的空孩子路径上有相同数目的黑色节点)

● 每个叶子结点的空孩子节点都是黑色的

根据上述的五条性质,我们可以发现一个红黑树中如果有N个黑色节点,则根节点到任意一个叶子节点的距离:最短为㏒⑵N,最长为2㏒⑵N

2.1 红黑树与AVL树的比较

我们来看到下面的红黑树:

对于这棵红黑树,如果将其看成一个AVL树,是需要进行旋转的,但是在红黑树结构中却不需要

所以红黑树是近似平衡的,在搜索效率上会略逊AVL树一些,但是红黑树在结构上不要求绝对的平衡,这就造成插入相同的数据红黑树翻转的次数少于AVL树

实际使用中,在经常进行增删的场景下红黑树性能比AVL树更优,并且红黑树实现比较简单,所以实际运用中红黑树更多

三、红黑树的实现

3.1 红黑树节点的定义

enum Colour
{
	RED,
	BLACK
};

template<class Key, class Val>
struct RBTreeNode
{
	RBTreeNode<Key, Val>* _left;
	RBTreeNode<Key, Val>* _right;
	RBTreeNode<Key, Val>* _parent;//多一个指针指向其父节点,方便我们的后续操作
	pair<Key, Val> _kv;
	Colour _col;//颜色标识

	RBTreeNode(const pair<Key, Val>& kv)
		:_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_kv(kv),
		_col(RED)//构造时,优先将节点的颜色置为红色
	{}
};

在这里提一下为什么要默认将节点的颜色置为红色:

在我们向红黑树中插入一个新节点时,如果将该节点置为黑色,就肯定会影响红黑树性质中的第四条:对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

例如:

我们现在在上面这棵树中,不管在哪个叶子节点的下方插入一个黑色的新增节点,从根节点到插入的节点的空孩子的路径上的黑色节点数目会变为4,而从根节点到其他叶子节点的空孩子的路径上的黑色节点数目会都为3

所以我们将新增节点的颜色置为红色就一定不会违反第四条红黑树性质,但是第三条呢?如果插入节点的父节点是红色的怎么办?

怎么办我们后面再说,反正总归比置为黑色一定会违反第四条性质好吧

3.2 数据的插入

由于红黑树也是平衡二叉搜索树的一种,我们在插入数据时也要找到合适的位置进行插入:

template<class Key, class Val>
class RBTree
{
	typedef RBTreeNode<Key, Val> Node;
public:
	bool Insert(const pair<Key,Val>& kv)
	{
		Node* cur = _root, * parent = nullptr;
		while (cur)//找到合适的位置
		{
			if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				cout << "插入的值重复" << endl;
				return false;
			}
		}
		cur = new Node(kv);
		cur->_parent = parent;
		//将插入的节点连接上二叉树
		if (parent == nullptr)
		{
			_root = cur;
		}
		else if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}

private:
	Node* _root = nullptr;
};

插入到合适的位置之后,我们还要检查是否破坏了红黑树的结构(由于我们插入的是红色节点,所以只会出现两个红色节点连续的情况),如果出现了该情况,我们就要对其进行分类讨论并解决:

3.2.1 红黑树的调整思路

下面我们将出现异常情况的两个节点的那个子节点叫做cur,cur的父节点叫做father(简称f),father的父节点叫做grandfather(简称g),father的兄弟节点叫做uncle(简称u)

例如:

接下来,我们分类讨论:

3.2.1.1 cur为红,f为红,g为黑,u存在且为红

下面画出的情况表示的是抽象出的情况:A、B、C、D、E都是满足构成红黑树的子树

对于这种情况我们先将f和u节点变黑,再将g节点变红即可:

调整完后,要记得再向上检查g节点的父节点是否为红色哦~(如果g节点为整棵红黑树的根,最后要将其颜色置为黑)

3.2.1.2 cur为红,f为红,g为黑,u不存在/u存在且为黑
3.2.1.2.1 g、f、cur构成一条直线

对于这种情况:若f为g的左孩子,cur为f的左孩子,则进行右单旋:

再将f变黑,g变红:

相反, f为g的右孩子,cur为f的右孩子,则进行左单旋转:

再将f变黑,g变红:

3.2.1.2.2 g、f、cur构成一条折线

f为g的左孩子,cur为f的右孩子,则做左右双旋,旋转完后将cur节点颜色置黑、g节点颜色置红:

相反, f为g的右孩子,cur为f的左孩子,则做右左双旋,旋转完后将cur节点颜色置黑、g节点颜色置红:

对于旋转操作还不熟悉的同学可以看到这里:【数据结构高阶】AVL树

3.2.2 调整部分的代码实现

template<class Key, class Val>
class RBTree
{
	typedef RBTreeNode<Key, Val> Node;
public:

	bool Insert(const pair<Key, Val>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
	}
	Node* cur = _root, * parent = nullptr;
	while (cur)//找到合适的位置
	{
		if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			cout << "插入的值重复" << endl;
			return false;
		}
	}
	cur = new Node(kv);
	//将插入的节点连接上二叉树
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;
	//开始调整
	while (parent && parent->_col == RED)//红黑树的结构出现两个连续的红色节点
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)//cur为红,p为红,g为黑,u存在且为红
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			elsecur为红,p为红,g为黑,u不存在/u存在且为黑
			{
				if (cur == parent->_left)//cur在p的左边,p也在g的左边,构成一条直线
				{
					//右单旋
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else//cur在p的右边,p在g的左边,构成一条折线
				{
					//左右双旋
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;//调整完跳出
			}
		}
		else
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)//cur为红,p为红,g为黑,u存在且为红
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			elsecur为红,p为红,g为黑,u不存在/u存在且为黑
			{
				if (cur == parent->_right)//cur在p的右边,p也在g的右边,构成一条直线
				{
					//左单旋
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else//cur在p的左边,p在g的右边,构成一条折线
				{
					//右左双旋
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;//调整完跳出
			}
		}
	}
	_root->_col = BLACK;//确保即便进行过调整后根节点颜色为黑
	return true;
}

private:
	void RotateL(Node* parent)//左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;
		parent->_right = subRL;//更新parent的右节点
		if (subRL)//防止该节点为空
		{
			subRL->_parent = parent;//更新subRL的父节点
		}
		parent->_parent = subR;//更新parent的父节点
		subR->_left = parent;//subR的左子树置为parent
		subR->_parent = pparent;//更新subR的父节点
		if (pparent == nullptr)//旋转的是整棵树
		{
			_root = subR;//更新根节点
		}
		else//将旋转后的子树链接上整个二叉树
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
		}
	}

	void RotateR(Node* parent)//右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;
		parent->_left = subLR;//更新parent的左节点
		if (subLR)//防止该节点为空
		{
			subLR->_parent = parent;//更新subLR的父节点
		}
		parent->_parent = subL;//更新parent的父节点
		subL->_right = parent;//subL的右子树置为parent
		subL->_parent = pparent;//更新subL的父节点
		if (pparent == nullptr)//旋转的是整棵树
		{
			_root = subL;//更新根节点
		}
		else//将旋转后的子树链接上整个二叉树
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}
	}

private:
	Node* _root = nullptr;
};

3.3 红黑树的验证

下面我们来写段代码来验证一课树是不是红黑树:

template<class Key, class Val>
class RBTree
{
	typedef RBTreeNode<Key, Val> Node;
public:

	bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}

		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;
			cur = cur->_left;
		}

		// 连续红色节点
		return _Check(_root, 0, benchmark);
	}

private:
	bool _Check(Node* root, int blackNum, int benchmark)//计算每条路径上黑色节点的个数
	{
		if (root == nullptr)
		{
			if (benchmark != blackNum)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == BLACK)
		{
			++blackNum;
		}

		if (root->_col == RED
			&& root->_parent
			&& root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		return _Check(root->_left, blackNum, benchmark)
			&& _Check(root->_right, blackNum, benchmark);
	}

private:
	Node* _root = nullptr;
};

3.4 测试代码

void Test_RBTree()
{
	const size_t N = 50000;
	RBTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		t.Insert(make_pair(x, x));
	}
	t.InOrder();
	cout << t.IsBalance();
}

int main()
{
	Test_RBTree();
	return 0;
}

测试效果:

四、红黑树实现完整代码

#include<iostream>

using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class Key, class Val>
struct RBTreeNode
{
	RBTreeNode<Key, Val>* _left;
	RBTreeNode<Key, Val>* _right;
	RBTreeNode<Key, Val>* _parent;//多一个指针指向其父节点,方便我们的后续操作
	pair<Key, Val> _kv;
	Colour _col;//颜色标识

	RBTreeNode(const pair<Key, Val>& kv)
		:_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_kv(kv),
		_col(RED)//默认构造时,优先将节点的颜色置为红色
	{}
};

template<class Key, class Val>
class RBTree
{
	typedef RBTreeNode<Key, Val> Node;
public:

	bool Insert(const pair<Key, Val>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
	}
	Node* cur = _root, * parent = nullptr;
	while (cur)//找到合适的位置
	{
		if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			cout << "插入的值重复" << endl;
			return false;
		}
	}
	cur = new Node(kv);
	//将插入的节点连接上二叉树
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;
	//开始调整
	while (parent && parent->_col == RED)//红黑树的结构出现两个连续的红色节点
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)//cur为红,p为红,g为黑,u存在且为红
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			elsecur为红,p为红,g为黑,u不存在/u存在且为黑
			{
				if (cur == parent->_left)//cur在p的左边,p也在g的左边,构成一条直线
				{
					//右单旋
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else//cur在p的右边,p在g的左边,构成一条折线
				{
					//左右双旋
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;//调整完跳出
			}
		}
		else
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)//cur为红,p为红,g为黑,u存在且为红
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			elsecur为红,p为红,g为黑,u不存在/u存在且为黑
			{
				if (cur == parent->_right)//cur在p的右边,p也在g的右边,构成一条直线
				{
					//左单旋
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else//cur在p的左边,p在g的右边,构成一条折线
				{
					//右左双旋
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;//调整完跳出
			}
		}
	}
	_root->_col = BLACK;//确保即便进行过调整后根节点颜色为黑
	return true;
}
	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}

	bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}

		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;
			cur = cur->_left;
		}

		// 连续红色节点
		return _Check(_root, 0, benchmark);
	}

private:
	void RotateL(Node* parent)//左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;
		parent->_right = subRL;//更新parent的右节点
		if (subRL)//防止该节点为空
		{
			subRL->_parent = parent;//更新subRL的父节点
		}
		parent->_parent = subR;//更新parent的父节点
		subR->_left = parent;//subR的左子树置为parent
		subR->_parent = pparent;//更新subR的父节点
		if (pparent == nullptr)//旋转的是整棵树
		{
			_root = subR;//更新根节点
		}
		else//将旋转后的子树链接上整个二叉树
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
		}
	}

	void RotateR(Node* parent)//右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;
		parent->_left = subLR;//更新parent的左节点
		if (subLR)//防止该节点为空
		{
			subLR->_parent = parent;//更新subLR的父节点
		}
		parent->_parent = subL;//更新parent的父节点
		subL->_right = parent;//subL的右子树置为parent
		subL->_parent = pparent;//更新subL的父节点
		if (pparent == nullptr)//旋转的是整棵树
		{
			_root = subL;//更新根节点
		}
		else//将旋转后的子树链接上整个二叉树
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}
	}

	void _InOrder(Node* root)
	{
		if (root == NULL)//如果是空树就直接结束
		{
			return;
		}
		_InOrder(root->_left);//先递归遍历其左子树
		cout << root->_kv.first << " ";//再遍历其根节点
		_InOrder(root->_right);//最后递归遍历其右子树
	}

	bool _Check(Node* root, int blackNum, int benchmark)
	{
		if (root == nullptr)
		{
			if (benchmark != blackNum)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == BLACK)
		{
			++blackNum;
		}

		if (root->_col == RED
			&& root->_parent
			&& root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		return _Check(root->_left, blackNum, benchmark)
			&& _Check(root->_right, blackNum, benchmark);
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

Seata配置

参考教程 seata 分布式事务的环境搭建与使用 Seata 1.4.0 nacos配置和使用&#xff0c;超详细 Seata 1.4.2 的安装 Nacos的配置和使用 官网下载地址 本文以v1.4.1为例 1.数据库及表的创建 创建seata数据库&#xff0c;创建以下表&#xff08;右键连接-》新建数据库seata-》…

MVC Gantt Wrapper:RadiantQ jQuery

The RadiantQ jQuery Gantt Package includes fully functional native MVC Wrappers that let you declaratively and seamlessly configure the Gantt component within your aspx or cshtm pages just like any other MVC extensions. 如果您还没有准备好转向完全基于客户端…

天池SQL训练营(二)-SQL基础查询与排序

-天池龙珠计划SQL训练营 Task02&#xff1a;SQL基础查询与排序 SQL训练营页面地址&#xff1a;https://tianchi.aliyun.com/specials/promotion/aicampsql 一、SELECT语句基础 1.1 从表中选取数据 SELECT语句 从表中选取数据时需要使用SELECT语句&#xff0c;也就是只从表…

unity 2d 入门 飞翔小鸟 死亡 显示GameOver(十四)

1、添加Img create->ui->img 把图片拖进去 2、和分数一样、调整位置 3、修改角色脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Fly : MonoBehaviour {//获取小鸟&#xff08;刚体&#xff09;private Rigidbod…

题目:肖恩的乘法表(蓝桥OJ 3404)

题目描述&#xff1a; 解题思路&#xff1a; 本题采用二分中的二分答案。且本题check()用不到开数组&#xff0c;所以不需要开数组&#xff0c;脑海中想象一个数组就好了 题解&#xff1a; #include<bits/stdc.h> using namespace std; using ll long long;ll n, m , k…

C# 图解教程 第5版 —— 第17章 转换

文章目录 17.1 什么是转换17.2 隐式转换17.3 显示转换和强制转换17.4 转换的类型17.5 数字的转换17.5.1 隐式数字转换17.5.2 溢出检测上下文17.5.3 显示数字转换 17.6 引用转换17.6.1 隐式引用转换17.6.2 显式引用转换17.6.3 有效显式引用转换 17.7 装箱转换17.7.1 装箱是创建副…

Python 全栈体系【四阶】(三)

第三章 matplotlib 一、基本绘图 1. 绘图核心 API 案例&#xff1a; 绘制简单直线 import numpy as np import matplotlib.pyplot as plt# 绘制简单直线 x np.array([1, 2, 3, 4, 5]) y np.array([3, 6, 9, 12, 15])plt.plot(x, y) plt.show() # 显示图片&#xff0c;阻塞…

《Mamba: Linear-Time Sequence Modeling with Selective State Spaces》阅读笔记

论文标题 《Mamba: Linear-Time Sequence Modeling with Selective State Spaces》 作者 Albert Gu 和 Tri Dao 初读 摘要 Transformer 架构及其核心注意力模块 地位&#xff1a;目前深度学习领域普遍的基础模型。 为了解决 Transformers 在长序列上的计算效率低下的问题…

【视频笔记】古人智慧与修行

古人的智慧 相由心生、老子悟道、佛祖成佛 多一些思考&#xff0c;多一些精神修炼。 除非我们今天能够产生与人类科技发展相并行的精神变革&#xff0c;否则永远可能也无法跳脱出历史的轮回。 视频来源 曾仕强教授周易的智慧 太极两仪四象八卦 一生二&#xff0c;二生三&…

gRPC基本用法:以人脸识别为例,搭建一个简单的gRPC服务

标题 0. gRPC简介 相关网站&#xff1a; 中文文档&#xff1a;gRPC 官方文档中文版_V1.0 官网&#xff1a;gRPC 介绍&#xff08;以下引自官方文档中文版中的介绍&#xff09;&#xff1a; gRPC是一个高性能、开源和通用的 RPC 框架&#xff0c;面向移动和 HTTP/2 设计。目前提…

LANDSAT_7/02/T1/RAW的Landsat7_C2_RAW类数据集

Landsat7_C2_RAW是指Landsat 7卫星的数据集&#xff0c;采用的是Collection 2级别的数据处理方法&#xff0c;对应的是Tier 1级别的原始数据&#xff08;RAW&#xff09;。该数据集包括了Landsat 7卫星从1999年4月15日开始的所有数据&#xff0c;共涵盖了全球范围内的陆地和海洋…

性格内向怎么办?如何改变性格内向?

性格内向和外向并没有优劣之分&#xff0c;性格内向也不是坏事&#xff0c;但从人际交往和事务处理的角度来看&#xff0c;内向性格确实不如外向的有优势&#xff0c;所以这也是很多人希望改变自己内向性格的原因。 此外性格内向也容易称为心理问题的替代词&#xff0c;比如&a…

Data Mining数据挖掘—2. Classification分类

3. Classification Given a collection of records (training set) – each record contains a set of attributes – one of the attributes is the class (label) that should be predicted Find a model for class attribute as a function of the values of other attribu…

【Linux】进程间通信之共享内存/消息队列/信号量

文章目录 一、共享内存的概念及原理二、共享内存相关接口说明1.shmget函数2.ftok函数3.shmat函数4.shmdt函数5.shmctl函数 三、用共享内存实现server&client通信1.shm_server.cc2.shm_client.cc3.comm.hpp4.查看ipc资源及其特征5.共享内存的优缺点6.共享内存的数据结构 四、…

ADAudit Plus:强大的网络安全卫士

随着数字化时代的不断发展&#xff0c;企业面临着越来越复杂和多样化的网络安全威胁。在这个信息爆炸的时代&#xff0c;保护组织的敏感信息和确保网络安全已经成为企业发展不可或缺的一环。为了更好地管理和监控网络安全&#xff0c;ADAudit Plus应运而生&#xff0c;成为网络…

【队列】数据也得排队

目录 引言 队列的概念 队列的实现 单向链表队列 结构 初始化 入队 出队 取队头 取队尾 求个数 判空 内存释放 总结 引言 队列&#xff0c;这个看似普通的数据结构&#xff0c;其实隐藏着无尽的趣味和巧思。就像单向链表这把神奇的魔法钥匙&#xff0c;它能打开队…

解决Git提交错误分支

如果 Git 提交到错误的分支&#xff0c;可以通过以下步骤将其转移到正确的分支上&#xff1a; 1.检查当前所在的分支&#xff0c;可以通过 git branch 命令查看。 git branch2.切换到正确的分支&#xff0c;可以通过 git checkout <正确的分支名> 命令进行切换。 git …

vue3-vite前端快速入门教程 vue-element-admin

Vue3快速入门学习 初始化项目 # 创建项目 npm create vitelatest my-vue-app -- --template vue # 安装依赖 npm i # 运行 npm run dev 模板语法 文本插值​ 最基本的数据绑定形式是文本插值&#xff0c;它使用的是“Mustache”语法 (即双大括号)&#xff1a; <span&g…

三相不平衡电压的正负序分析

1、什么是正负序&#xff1f; ABC 正序 ACB 负序 2、在abc坐标系下 接着利用矢量的旋转消去其它分量。。。 同理&#xff0c;得到其它的所有正负序的分量abc 3、在α/β坐标系下&#xff0c; 依次算出正负序的α/β来表示的abc 有一点需要特别注意&#xff0c;可以看到…

贪心其实很简单

关卡名 认识贪心思想 我会了✔️ 内容 1.复习一维数组&#xff0c;对数组进行多轮插入或者删除时会频繁移动数据&#xff0c;理解双指针是如何避免该问题的 ✔️ 2.理解滑动窗口的原理和适用场景 ✔️ 3.掌握窗口变与不变的两种情况是如何用来解题的 ✔️ 1.难以解释的贪心…