红黑树的实现

news2024/12/24 3:46:16

文章目录

  • 红黑树
    • 前言
    • 1. 红黑树的概念及性质
      • 1.1 红黑树的概念
      • 1.2 红黑树的性质
    • 2. 红黑树的结构
      • 2.1 红黑树节点的定义
      • 2.2 红黑树的结构
    • 3. 红黑树的操作
      • 3.1 红黑树的查找
      • 3.2 红黑树的插入
        • 处理红黑树颜色的过程(重点)
          • 情况1: 只变色
          • 情况2: 变色 + 单旋
          • 情况3: 变色 + 双旋
        • 处理颜色的代码
      • 3.3 红黑树的验证
    • 4. 红黑树完整代码
    • 5. 红黑树与AVL树的比较

红黑树

前言

前面学习了AVL树, 它是一棵绝对平衡的二叉搜索树,查找的效率很高,结构完美严格,但是一旦对AVL树做插入操作就可能会引发旋转,使性能降低。所以提出了另一种实现平衡二叉搜索树的思路,即红黑树。

1. 红黑树的概念及性质

1.1 红黑树的概念

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

在这里插入图片描述

1.2 红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 — 不能出现连续的红色节点
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 — 每条路径上都有相同数量的黑色节点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

满足上面的性质,最短路径是全是黑色节点,如下图的红黑树,最左路径即最短路径有两个黑色节点,那么要满足每条路径上都有相同数量的黑色节点,所以其他路径上黑色节点与最短路径黑色节点数量相同,再添加一些红色节点,最长路径是一红一黑的相间路径,下图中其最长路径中节点个数是最短路径节点个数的两倍。由此可得: 红黑树最长路径中节点个数不会超过最短路径节点个数的两倍。

在这里插入图片描述

最短路径: 全是黑色节点

最长路径: 一个黑色节点一个红色节点,红黑相间

由此也引入两种情况:

  1. 最好情况: 左右平衡,全部是黑色节点或每条路径都是一黑一红相间,此时接近满二叉树,假设全部黑色节点有N个,红色节点数量不会超过黑色节点数量,此时整棵树的节点数量保持在[N, 2N]之间,平均下来路径高度为logN
  2. 最坏情况: 左右极其不平衡,左子树全黑,且右子树一黑一红。那么此时最长路径为2* logN

最好情况与最坏情况对于计算机来说相差并不大,比如有10亿个节点,AVL树最多查找30次左右,红黑树最多查找60次左右,两者虽然相差二倍,但在往树上插入节点时,AVL可能会引发大量的旋转使性能降低。比如上面形状的那棵树,AVL树时一定旋转,红黑树不一定旋转。

2. 红黑树的结构

2.1 红黑树节点的定义

相比于AVL树,红黑树不用定义平衡因子,但是想要表示节点的颜色,所以可以定义一个枚举类型表示节点颜色,还是向AVL树一样定义成三叉链的结构,方便后续找父节点来修改颜色及旋转。

//节点的颜色
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)       //默认颜色给红色
	{}
};

2.2 红黑树的结构

//节点的颜色
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)  //默认颜色给红色,假设是黑色,插入此节点会使这条路径黑节点数量变,其他路径也要跟着变
	{}
};
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	//一系列操作
private:
	Node* _root = nullptr;
};

当然还有另一种红黑树的实现方式:

红黑树的实现中增加一个头结点, 因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点。

在这里插入图片描述

3. 红黑树的操作

3.1 红黑树的查找

与搜索二叉树查找思路完全类似,就是注意这里是用key值来查找

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;
}

3.2 红黑树的插入

红黑树的删除比较复杂,就不实现了

插入整体的大框架与AVL树相似,还是先找到要插入的位置再去链接的过程,重点是后续处理红黑树颜色的过程

bool Insert(const pair<K, V> &kv)
{
    // 第一次插入
    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);
    if (parent->_kv.first > kv.first)
    {
        parent->_left = cur;
    }
    else
    {
        parent->_right = cur;
    }
    cur->_parent = parent;
    
    //接下来需要处理红黑树的颜色
    //...
}

处理红黑树颜色的过程(重点)

此问题的核心: 寻找叔叔节点, 总体上分为3种情况:

情况1: 只变色

在这里插入图片描述

情况2: 变色 + 单旋

在这里插入图片描述

情况3: 变色 + 双旋

在这里插入图片描述

处理颜色的代码

这里颜色处理要结合之前AVL树中的旋转,同时需要将旋转函数中平衡因子的更新去掉

while (parent && parent->_col == RED)
{
    Node *grandfather = parent->_parent;

    // 找叔叔  =>  看父亲在祖父的哪边
    if (grandfather->_left == parent)
    {
        Node *uncle = grandfather->_right;

        // 3种情况

        // 情况1: u存在且为红, 变色处理, 并继续向上处理
        // 变色: p,u变黑, g变红
        if (uncle && uncle->_col == RED)
        {
            parent->_col = BLACK;
            uncle->_col = BLACK;
            grandfather->_col = RED;

            // 继续向上调整
            cur = grandfather;
            parent = cur->_parent;
        }
        else // 情况2+3: u不存在/u存在且为黑, 旋转 + 变色
        {
            //     g
            //   p   u
            // c
            if (cur == parent->_left)
            {
                RotateR(grandfather);
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else
            {
                //     g
                //   p   u
                //     c
                RotateL(parent);
                RotateR(grandfather);
                cur->_col = BLACK;
                grandfather->_col = RED;
            }

            break;
        }
    }
    else //(grandfather->_right == parent)
    {
        Node *uncle = grandfather->_left;

        // 3种情况

        // 情况1: u存在且为红, 变色处理, 并继续向上处理
        // 变色: p,u变黑, g变红
        if (uncle && uncle->_col == RED)
        {
            parent->_col = BLACK;
            uncle->_col = BLACK;
            grandfather->_col = RED;

            // 继续向上调整
            cur = grandfather;
            parent = cur->_parent;
        }
        else // 情况2+3: u不存在/u存在且为黑, 旋转 + 变色
        {
            //     g
            //   u   p
            //         c
            if (cur == parent->_right)
            {
                RotateL(grandfather);
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else
            {
                //     g
                //   u   p
                //     c
                RotateR(parent);
                RotateL(grandfather);
                cur->_col = BLACK;
                grandfather->_col = RED;
            }
            break;
        }
    }
}
_root->_col = BLACK;   //根节点是黑色的

3.3 红黑树的验证

红黑树的验证分为两步:

  • 验证其为二叉搜索树

    • 若中序遍历可得到一个有序序列,就说明为二叉搜索树
  • 验证其是否满足红黑树的性质

重点实现验证满足红黑树的性质:

  1. 检查根节点
  2. DFS检查黑色节点数量,同时用一个基准值来记录最左路径的黑色节点数量,各个路径与之比较黑色节点数量
  3. 检查不能存在连续的红色节点(反向检查)
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);
}

bool _Check(Node *root, int blackNum, int benchmark) // 基准值
{
    if (root == nullptr) // 空树也是红黑树
    {
        if (benchmark != blackNum)
        {
            cout << "某条路径黑色节点的数量不相等" << endl;
            return false;
        }
        return true;
    }

    // DFS检查黑色节点数量
    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);
}

4. 红黑树完整代码

#include<iostream>
#include<utility>
#include<assert.h>
#include<stdlib.h>
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)
		, _col(RED)       //默认颜色给红色
	{}
};



template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}


	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 Insert(const pair<K, V>& kv)
	{
		//第一次插入
		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);
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;


		//处理红黑树颜色
		while (parent && parent->_col==RED)
		{
			Node* grandfather = parent->_parent;

			//找叔叔  =>  看父亲在祖父的哪边
			if (grandfather->_left == parent)   
			{
				Node* uncle = grandfather->_right;

				//3种情况

				//情况1: u存在且为红, 变色处理, 并继续向上处理
				//变色: p,u变黑, g变红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上调整
					cur=grandfather;
					parent = cur->_parent;
				}
				else    //情况2+3: u不存在/u存在且为黑, 旋转 + 变色
				{
					//     g
					//   p   u
					// c
					if(cur==parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
					//     g
					//   p   u
					//     c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else    //(grandfather->_right == parent)
			{
				Node* uncle = grandfather->_left;

				//3种情况

				//情况1: u存在且为红, 变色处理, 并继续向上处理
				//变色: p,u变黑, g变红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else      //情况2+3: u不存在/u存在且为黑, 旋转 + 变色
				{
					//     g
					//   u   p
					//         c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   u   p
	                    //     c

						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}
		_root->_col = BLACK;   //根节点是黑色的

		return true;
	}

	void Inorder()
	{
		_Inorder(_root);
	}

	int Height()
	{
		return _Height(_root);
	}

	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;

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

		Node* ppnode = parent->_parent;

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


		if (ppnode == nullptr)				//parent本身就是根
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else								//parent只是一棵子树
		{
			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 (ppnode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}
	}


	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}


	bool _Check(Node* root, int blackNum, int benchmark)  //基准值
	{
		if (root == nullptr)    //空树也是红黑树
		{
			if (benchmark != blackNum)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}
			return true;
		}

		//DFS检查黑色节点数量
		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);
	}

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

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

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

		_Inorder(root->_left);
		cout << root->_kv.first << " ";
		_Inorder(root->_right);
	}

	Node* _root = nullptr;
};

5. 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

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

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

相关文章

ubuntu深度学习使用TensorFlow卷积神经网络——图片数据集的制作以及制作好的数据集的使用

首先我事先准备好五分类的图片放在对应的文件夹&#xff0c;图片资源在我的gitee文件夹中链接如下&#xff1a;文件管理: 用于存各种数据https://gitee.com/xiaoxiaotai/file-management.git 里面有imgs目录和npy目录&#xff0c;imgs就是存放5分类的图片的目录&#xff0c;里面…

哈工大计组大作业-RISC处理器设计

RISC_CPU_HIT RISC处理器设计 地址&#xff1a;https://github.com/944613709/Design-of-RISC-Processor.git 要求 根据计算机组成原理课程所学的知识&#xff0c;设计一个给定指令系统的处理器&#xff0c;深刻理解处理器结构和计算机系统的整体工作原理。 1. 处理器功能及…

软件工程(二) 软件开发模型

软件开发模型主要了解如下这些模型,加粗表示重点要掌握的模型。 瀑布模型: 迭代模型/迭代开开发方法 演化模型: 快速开发应用 增量模型: 构建组装模型/基于构建的开发方法 螺旋模型: 统一过程/统一开发方法 原型模型: 敏捷开发方法 喷泉模型: 模型驱动的开发方法 V模型: 基于架…

C/C++每日一练(20230516) 最佳时机、两数相加、后序遍历

目录 1. 买卖股票的最佳时机 &#x1f31f; 2. 两数相加 &#x1f31f;&#x1f31f; 3. 二叉树的后序遍历 &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 买卖股票…

D2. Red-Blue Operations (Hard Version)(思维/贪心/前缀和)

建议先从easy version开始做。 easy version题解 题目 题意 给定长度为n(1<n<200000)的数组 1<a[i]<10^9 q个查询(1<q<200000)&#xff0c;每次查询&#xff0c;给定整数k(1<k<10^9) 初始时&#xff0c;每个元素状态都是红色。 对于第i次操作&…

【小沐学Unity3d】Unity插件之天气系统UniStorm

文章目录 1、简介1.1 描述1.2 兼容性1.3 价格1.4 特点1.5 示例 3、安装3.1 新建Unity项目3.2 安装插件UniStorm3.3 介绍UniStorm工具栏3.4 入门使用 4、脚本开发4.1 接口简介4.1.1 天气4.1.2 时间4.1.3 程度4.1.4 季节4.1.5 系统4.1.6 其他 4.2 接口测试4.2.1 测试1 结语 1、简…

教你如何使用Nodejs搭建HTTP web服务器并发布上线公网

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 转载自内网穿透工具的文章&#xff1a;使用Nodejs搭建HTTP服务&#xff0c;并实现公网远程访问「内网穿透」 前言 Node.js…

60岁的机器视觉工程师,你还在敲代码?不想做机器视觉工程师,还可以做什么?机器视觉工程师职业生命线有多长​?

如果按程序员参加工作时间为22岁计算,平均退役年龄为35岁计算的话,程序员的职业寿命大概为14年。为什么程序员的职业生命线如此短暂呢?大致有以下几点—— 1、编程技术层出不穷,迭代速度非常快,这时候就需要我们不断的学习,不断地保持学习能力,当随着年龄的增长我们的学…

【数据结构】链表:带头双向循环链表的增删查改

本篇要分享的内容是带头双向链表&#xff0c;以下为本片目录 目录 一、链表的所有结构 二、带头双向链表 2.1尾部插入 2.2哨兵位的初始化 2.3头部插入 2.4 打印链表 2.5尾部删除 2.6头部删除 2.7查找结点 2.8任意位置插入 2.9任意位置删除 在刚开始接触链表的时候&…

86. print输出函数知识拓展(有练习题)

86. print输出函数知识拓展&#xff08;有练习题&#xff09; 文章目录 86. print输出函数知识拓展&#xff08;有练习题&#xff09;1. print函数语法2. 横着输出数字序列3. 竖着输出数字序列4. 循环输出5. 总结 1. print函数语法 print[prɪnt]&#xff1a;打印&#xff0c;…

少儿编程scratch -- 基础篇

1.开篇 花费40分钟 首先&#xff0c;我们学的是scratch(划痕&#xff09;&#xff0c;Scratch 是麻省理工学院的“终身幼儿园团队”在 2007 年 [5] 发布的一种图形化编程工具&#xff0c;主要面对全球青少年开放&#xff0c;是图形化编程工具当中最广为人知的一种&…

ShardingSphere-JDBC整合SpringBoot JPA读写分离失败

问题 最近在整合ShardingSphere JDBC和Spring Boot的时候遇到一个问题,当ORM框架使用JPA时,读写分离会失效,查询仍然走主库并不会走从库!同样的配置使用Mybatis就没有任何问题。 在查阅各种资料后,初步确定未JPA事务问题 ShardingSphere负载均衡算法 我当前使用的版本是…

FIDO认证 无密码的愿景

目录 1、简介 2、关于FIDO联盟 3、FIDO如何工作 3.1 FIDO 注册 3.2 FIDO登录 4、FIDO2 4.1 W3C WebAuthn 4.2 CTAP2 4.3 CTAP1 4.4 FIDO UAF 4.5 FIDO U2F 5、FIDO身份验证的优势 5.1 安全 5.2 便利性 5.3 隐私政策 5.4 可扩展性 1、简介 FIDO&#xff08;在线…

Python 爬虫(一):爬虫伪装

1 简介 对于一些有一定规模或盈利性质比较强的网站&#xff0c;几乎都会做一些防爬措施&#xff0c;防爬措施一般来说有两种&#xff1a;一种是做身份验证&#xff0c;直接把虫子挡在了门口&#xff0c;另一种是在网站设置各种反爬机制&#xff0c;让虫子知难而返。 2 伪装策…

【2023 · CANN训练营第一季】初识新一代开发者套件 Atlas 200I DK A2 第二章——安装Atlas 200I DK A2跑通第一个案例

准备相关软件 包括一台PC机&#xff08;空间大于10g)&#xff0c;读卡器&#xff0c;32gsd卡&#xff0c;一根网线。 具体步骤&#xff1a; 开始烧录开发板镜像&#xff1a;将sd卡插入读卡器&#xff0c;将读卡器插入PC机的USB接口&#xff0c;根据相关链接在PC机下载制卡工具…

混合云到底是什么?

大型企业不能再仅仅拥有本地系统就可以逃脱&#xff1b;因此&#xff0c;将一些数字化运营迁移到云端成为了必要。 第一步涉及选择是使用公共云还是私有云&#xff0c;或者两者都使用。使用两者是混合云。 但混合云到底是什么&#xff1f;在回答之前&#xff0c;让我们看看这…

图生图—AI图片生成Stable Diffusion参数及使用方式详细介绍

本文为博主原创文章&#xff0c;未经博主允许不得转载。 本文为专栏《Python从零开始进行AIGC大模型训练与推理》系列文章&#xff0c;地址为“https://blog.csdn.net/suiyingy/article/details/130169592”。 Stable Diffusion webui的详细安装步骤以及文生图&#xff08;txt2…

【Linux】Linux入门学习之常用命令四

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

Jenkins教程-Docker安装Jenkins,并构建任务等

本文主要介绍如何在docker中安装Jenkins&#xff0c;启动容器后&#xff0c;如何使用Jenkins构建自己的项目&#xff0c;创建任务等 文章目录 前言Docker安装Jenkins访问Jenkins并配置Jenkins构建项目创建任务源码管理构建触发器BuildPost Steps 设置手工触发构建 前言 参考链…

Redis集群安全加固

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130692909 一、为Redis启用密码验证 默认情况下&#xff0c;Redis不执行任何密码身份验证。能够通过IP端口连接Redis的客户端均可以直接访问Redis中的数据。在启动Redis服务时&#xff0c;通过设置req…