红黑树的理解和简单实现

目录

1. 红黑树的概念和性质

2. 红黑树的插入

2.1. 情况一:新增节点的父亲为空

2.2. 情况二:新增节点的父亲非空且为黑色节点

2.3. 情况三:当父亲为红节点,叔叔存在且为红

2.3.1. 当祖父为根节点的时候

2.3.2. 当祖父不是根节点的时候

2.4. 情况四:当父亲为红节点,叔叔不存在或者存在为黑

2.5. 情况五:父亲为红、叔叔不存在或者为黑

3. 验证红黑树的插入

4. 红黑树插入的完整实现


1. 红黑树的概念和性质

红黑树是一种自平衡的二叉搜索树,它通过约束节点的颜色和结构来保持平衡。红黑树是由 Rudolf Bayer 在1972年发明的,被认为是一种优秀的平衡树结构,广泛应用于各种数据结构和算法中。

红黑树具有以下几个性质:

  1. 二叉搜索树性质:红黑树是一种二叉搜索树,即满足以下性质:

    • 左子树中的所有节点的键都小于该节点的键。
    • 右子树中的所有节点的键都大于该节点的键。
    • 左右子树都是二叉搜索树。
  2. 节点颜色性质:每个节点被标记为红色或黑色;

  3. 根节点性质:根节点为黑色

  4. 叶子节点(NIL节点)性质:叶子节点都为黑色的空节点(NIL节点),并被视为红黑树的终止节点;

  5. 红色节点性质:红节点的子节点必须是黑节点,不能出现连续的红色节点

  6. 黑节点性质:从任一节点到其每个叶子节点 (NIL 节点) 的路径上,经过的黑节点数量是相同的(黑色平衡性)

  7. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路

    径会比其他路径长出俩倍以上,因而是接近平衡的。

这些性质保证了红黑树的平衡性和有序性。红黑树通过在插入和删除操作中进行颜色调整和旋转操作来维护平衡性。与AVL树相比,红黑树对于平衡性的要求相对宽松,因此插入和删除操作的平衡调整(旋转)相对较少。

由于红黑树的平衡性,其高度相对较小,平均时间复杂度为 O(log n),提供了快速的查找、插入和删除操作。红黑树在各种应用中被广泛使用,例如C++ STL中的map和set容器,以及各种数据库、编译器和操作系统的实现

总结来说,红黑树是一种具有自平衡性质的二叉搜索树,通过约束节点的颜色和结构来保持平衡。它具有二叉搜索树的性质,并通过节点颜色、根节点性质、叶子节点性质、红色节点性质和黑节点性质来维护平衡性。红黑树提供了高效的查找、插入和删除操作,被广泛应用于不同领域的数据结构和算法中。

思考 :为什么红黑树可以保证任何一条路径不会比其他路径长出两倍以上呢?

首先我们要知道什么是路径?

路径是从根节点开始,沿着树的分支一直走到达叶子节点。注意:这里的叶子节点特指为空节点,在这里一般用NIL节点表示空节点,且NIL节点是黑色的。

对于一棵红黑树而言,它的最短路径和最长路径分别为:

  • 最短路径:就是这条路径上的节点是全黑的;
  • 最长路径:该路径是由一黑一红连续组成的。

红黑树的性质告诉我们,每条路径上的黑色节点个数是一定的,且没有连续的红色节点

那么我们假设最短路径的黑色节点个数为N。

那么最长路径的黑色节点个数也为N,其红色节点个数最多也为N,那么最长路径的节点个数为2N;

因此我们可以得出,红黑树的最长路径可以是最短路径的二倍,但是不会超过二倍, 因此红黑树的任意一条路径的节点个数不可能比其他任何一条路径长过两倍以上,以达到近似平衡的状态。

综上所述,红黑树通过对节点的着色和结构的限制,确保了树的高度相对较小,从而保证了没有一条路径会比其他路径长出两倍以上。这种限制确保红黑树保持近似平衡的状态,从而提供了较好的性能。

2. 红黑树的插入

如下图所示,这就是一颗红黑树

第一个问题,如果我们现在要插入一个新节点,我们是把这个新节点的初始颜色设置为黑色还是红色呢??? 

假设我把新增节点设置为黑色,那么就会有下面的场景:

我们发现,此时就出现了问题,插入了5之后,这棵树还是红黑树吗?

抱歉,根据红黑树的性质:每个路径上的黑色节点个数必须相等,故插入5之后 (黑色节点),此时这棵树已经不是红黑树了。

但如果我将新增节点的初始颜色设置为红色呢,又会出现什么情况?如下图所示:

 

我们发现,当我们把新增节点的初始颜色设置为红色的时候,插入之后会有两种情况:

  • 第一种情况:插入之后违反了红黑树的规则,即不能出现连续的红色节点;
  • 第二种情况:插入之后没有违反任何规则。

综上所述,我们将新增节点的初始颜色设置为红色。因为如果新增节点是黑色,那么一定会违反红黑树的规则,反之,如果是红色,则可能插入之后不违反任何规则,因此我们将新增节点设置为红色。

严格意义上讲,红黑树的插入会有五种情况,让我们一一进行理解。

2.1. 情况一:新增节点的父亲为空

这种情况最为简单,就是当是一颗空树的时候,直接插入即可。并将新增节点的颜色更新为黑即可。

2.2. 情况二:新增节点的父亲非空且为黑色节点

这种情况也很简单,直接插入红色节点即可,不会破环红黑树的规则。

2.3. 情况三:当父亲为红节点,叔叔存在且为红

当新增节点的父亲节点和叔叔节点非空且为红时,变色即可。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

声明:

g --- grandfather(祖父节点 ), p --- parent(父节点) ,u --- uncle(叔叔节点),c --- cur(新增节点)。

变色规则:p、u变黑,g变红

如图所示:

2.3.1. 当祖父为根节点的时候

2.3.2. 当祖父不是根节点的时候

总结,当p、u为红的时候,那么将p、u变黑、将g变红,c = g; p = c->parent,继续判断,如果符合前面的条件(p、u为红)继续向上更新,最后为了避免不同的情况,将根节点的颜色更新为黑即可。

2.4. 情况四:当父亲为红节点,叔叔不存在或者存在为黑

父亲为红、叔叔不存在或者存在且为黑,且g、p、c在同一条直线上,单旋处理,处理完,插入结束。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

单旋非为左单旋和右单旋两种情况,具体操作如图所示:

其中,a、b、c、d、e代表红黑树子树。

总结:当g、p、c在同一条直线上,就进行单旋,虽然有两种情况,但是最后它们的变色情况是一致的,p:由红变黑 g:由黑变红。

2.5. 情况五:父亲为红、叔叔不存在或者为黑

父亲为红、叔叔不存在或者存在为黑,且g、p、c不在同一条直线上,需要双旋处理,处理完,插入结束。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

双旋非为左右双旋和右左双旋两种情况,具体操作如图所示:

其中,a、b、c、d、e代表红黑树子树。

总结:当g、p、c不在同一条直线上(一条折线就是双旋),就进行双旋,虽然有两种情况,但是最后它们的变色情况是一致的,c由红变黑  g由黑变红  p不变,保持红色

3. 验证红黑树的插入

思路:只要满足下面的所有条件就是红黑树。

  1. 第一个条件:
    1. 根节点是黑色的。
  2. 第二个条件:
    1. 红色的节点的左右子树都必须是黑色的,也就是说父子节点只能有一个红色节点;

    2. 思路: 只要某节点是红色的,那么只要判断它的父亲即可,如果父亲是红色的,那么就不是红黑树,反之,则符合红黑树。

  3. 第三个条件:
    1. 每一条路径的黑色节点的个数是相等的,步骤:

    2. 步骤一: 先求一条路径上的黑色节点个数作为基准值;

    3. 步骤二: 用该基准值分别于其他所有路径的黑色节点个数进行比较;

    4. 路径:从根节点到 "叶子节点"。注意:这里的叶子节点不是传统意义上的叶子节点,在这里把空节点当作叶子节点。

代码如下:

bool is_balance_tree()
{// 检查根节点if (_root->_col != BLACK){std::cout << "根节点是红色,异常" << std::endl;return false;}	return _is_balance_tree(_root);
}bool _is_balance_tree(Node* root)
{if (!root)return true;else{// basic_value作为这颗红黑树的黑色节点个数的基准值int basic_value = _get_black_node_num(root);return _is_check_rb_tree(root, 0, basic_value);}
}bool _is_check_rb_tree(Node* root, int black_num, int basic_value)
{// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基准值if (root == nullptr){if (black_num != basic_value){std::cout << "某条路径中的黑色节点个数不相等,异常" << std::endl;return false;}elsereturn true;}else{if (root->_col == BLACK)++black_num;// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲// 如果这个节点的父亲也是红色的,那么就说明出现异常if (root->_col == RED && root->_parent->_col == RED){std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;return false;}return _is_check_rb_tree(root->_left, black_num, basic_value)&& _is_check_rb_tree(root->_right, black_num, basic_value);}
}// 获得一条路径的黑色节点的个数
int _get_black_node_num(Node* root)
{if (root == nullptr)return 0;else{int ret = 0;while (root){if (root->_col == BLACK)++ret;root = root->_left;}return ret;}
}

4. 红黑树插入的完整实现

#pragma once
#include <iostream>
#include <utility>
#include <assert.h>
#include <queue>
#include <vector>
#include <time.h>namespace Xq
{enum color{RED,BLACK};template<class K, class V>struct rb_tree_node{rb_tree_node<K, V>* _left;rb_tree_node<K, V>* _right;rb_tree_node<K, V>* _parent;std::pair<K, V> _kv;color _col;rb_tree_node(const std::pair<K, V>& kv = std::pair<K, V>()):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}};template<class K, class V>class rb_tree{private:typedef rb_tree_node<K, V> Node;public:rb_tree(Node* root = nullptr) :_root(root){}bool insert(const std::pair<K, V>& kv){if (!_root){_root = new Node(kv);_root->_col = BLACK;return true;}else{Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv);if (kv.first > parent->_kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 当父节点parent的颜色为红色时,需要调整while (parent && parent->_col == RED){// 因为父节点parent->_col == RED,那么说明parent一定有父节点Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;// case 1: 当父节点为红,且叔叔不为空且为红// Solution : 变色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;if (grandfather == _root)grandfather->_col = BLACK;// 继续向上判断,如果依旧符合case 1,继续变色cur = grandfather;parent = cur->_parent;continue;}// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上// Solution: 单旋, 在这里就是对grandfather右单旋else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK)){//     g             p//   p   u  --->   c   g// c                     uright_rotate(grandfather);parent->_col = BLACK;grandfather->_col = RED;// 旋转完,更新完颜色,调整就结束break;}// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上// Solution: 双旋,在这里就是先对p进行左单旋,在对g进行右单旋else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK)){//    g     先对p进行左单旋       g       在对g进行右单旋        c// p     u    --->            c     u        --->          p     g    //   c                      p                                       uleft_rotate(parent);right_rotate(grandfather);cur->_col = BLACK;grandfather->_col = RED;// 旋转完,更新完颜色,调整就结束break;}else{// 非法情况assert(false);}}// 当parent是grandfather的右孩子,那么uncle就是grandfather的左孩子else{Node* uncle = grandfather->_left;// case 1: 当父节点为红,且叔叔不为空且为红// Solution : 变色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;if (grandfather == _root)grandfather->_col = BLACK;// 继续向上判断,如果依旧符合case 1,继续变色cur = grandfather;parent = cur->_parent;continue;}// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上// Solution: 单旋, 在这里就是对grandfather左单旋else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK)){//   g    对g进行左单旋       p// u   p      --->         g     c//       c               u left_rotate(grandfather);parent->_col = BLACK;grandfather->_col = RED;//旋转并将颜色更新后,就退出调整break;}// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上// Solution: 双旋,在这里就是先对p进行右单旋,在对g进行左单旋else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK)){//    g       先对p进行右单旋        g          在对g进行左单旋          c// u     p      --->             u     c          --->            g      p//     c                                  p                    u                            right_rotate(parent);left_rotate(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}else{// 非法情况,断死assert(false);}}}_root->_col = BLACK;return true;}}void level_order(){_level_order(_root);}bool is_balance_tree(){if (_root->_col != BLACK){std::cout << "根节点是红色的,异常" <<std::endl;return false;}return _is_balance_tree(_root);}int get_rb_tree_high(){return _get_rb_tree_high(_root);}private:void _level_order(Node* root){if (!root)return;else{std::queue<Node*> qu;qu.push(root);while (!qu.empty()){Node* front = qu.front();qu.pop();if (front){qu.push(front->_left);qu.push(front->_right);}if (!front)std::cout << "N ";elsestd::cout << front->_kv.first << " ";}std::cout << std::endl;}}void left_rotate(Node* parent){Node* cur = parent;Node* cur_right = cur->_right;Node* cur_right_left = cur_right->_left;Node* cur_parent = cur->_parent;cur->_right = cur_right_left;if (cur_right_left)cur_right_left->_parent = cur;cur_right->_left = cur;cur->_parent = cur_right;if (!cur_parent){cur_right->_parent = nullptr;_root = cur_right;}else{if (cur_parent->_left == cur){cur_parent->_left = cur_right;}else{cur_parent->_right = cur_right;}cur_right->_parent = cur_parent;}}void right_rotate(Node* parent){Node* cur = parent;Node* cur_left = cur->_left;Node* cur_left_right = cur_left->_right;Node* cur_parent = cur->_parent;cur->_left = cur_left_right;if (cur_left_right)cur_left_right->_parent = cur;cur_left->_right = cur;cur->_parent = cur_left;if (!cur_parent){cur_left->_parent = nullptr;_root = cur_left;}else{if (cur_parent->_kv.first > cur_left->_kv.first){cur_parent->_left = cur_left;}else{cur_parent->_right = cur_left;}cur_left->_parent = cur_parent;}}bool _is_balance_tree(Node* root){if (!root)return true;else{int basic_value = _get_black_node_num(root);return _is_check_rb_tree(root, 0, basic_value);}}bool _is_check_rb_tree(Node* root, int black_num, int basic_value){// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基本值if (root == nullptr){if (black_num != basic_value){std::cout << "路径中的黑色节点个数不相等,异常" << std::endl;return false;}elsereturn true;}else{if (root->_col == BLACK)++black_num;// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲// 如果这个节点的父亲也是红色的,那么就说明出现异常if (root->_col == RED && root->_parent->_col == RED){std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;return false;}return _is_check_rb_tree(root->_left, black_num, basic_value)&& _is_check_rb_tree(root->_right, black_num, basic_value);}}int _get_black_node_num(Node* root){if (root == nullptr)return 0;else{int ret = 0;while (root){if (root->_col == BLACK)++ret;root = root->_left;}return ret;}}int _get_rb_tree_high(Node* root){if (root == nullptr)return 0;else{int left_high = _get_rb_tree_high(root->_left);int right_high = _get_rb_tree_high(root->_right);return left_high > right_high ? ++left_high : ++right_high;}}private:Node* _root;};
}

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

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

相关文章

AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘

问题描述 修改图片大小的时候&#xff0c;代码报错&#xff1a;AttributeError: module PIL.Image has no attribute ANTIALIAS 解决方案 在pillow的10.0.0版本中&#xff0c;ANTIALIAS方法被删除了。 方法1&#xff1a;修改版本&#xff08;不推荐&#xff09; pip instal…

docker 方式 elasticsearch 8.13 简单例子

安装 docker 虚拟机安装 elastic search 安装本地 # 创建 elastic 的网络 docker network create elastic # 用镜像的方式创建并启动容器 docker run -d --name es --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" -e "xpack.secur…

嵌入式全栈开发学习笔记---C语言笔试复习大全16

目录 指针和数组 用指针来表示数组 用数组来表示指针 笔试题19 上一篇复习了指针使用时的相关注意事项&#xff0c;这一篇我们开始复习指针和数组。 说明&#xff1a;我们学过单片机的一般都是有C语言基础的了&#xff0c;网上关于C语言的资料有很多&#xff0c;大家如果对…

【半夜学习MySQL】数据库中的数据类型(含数值类型、文本二进制类型、时间类型、String类型详谈)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 数据类型分类数值类型bit类型tinyint类型int类型float类型decimal类型 文本、二进制类型char类型varchar类型 时间类型Strin…

1.1. 离散时间鞅-条件期望

1.1. 离散时间鞅-条件期望 条件期望1. 条件期望的定义1.1. 条件期望的定义1.2. 条件期望的存在唯一性 2. 条件期望的示例2.1. X ∈ F X \in \mathcal{F} X∈F&#xff0c; X X X与 F \mathcal{F} F独立的情形2.2. X X X是有限 σ \sigma σ代数情形2.3. X X X是随机变量生成…

[Flutter GetX使用] Getx路由和状态管理-GetController使用过程中的踩坑记录

文章目录 问题 - Get.find() 报错!原因总结A:路由和控制器设计a1:项目中的Get路由aa1.项目路由结构aa2.本项目路由的注意点: B: GetController的冷知识C: 总结来看D: 一些参考资料 问题 - Get.find() 报错! 刚接触Getx, 遇到 Get.find()确找不到, 进而报错的问题, 一时间有点没…

智慧旅游平台开发微信小程序【附源码、文档说明】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

关于DDD和COLA的一些总结和思考

1|0思维&#xff1a;面向对象和面向过程 领域驱动设计本质上是讲的面向对象&#xff0c;但是谈面向对象&#xff0c;始终无法绕开面向过程&#xff0c;所以我们先好好说一下面向过程和面向对象这两个概念。 什么是面向过程呢&#xff0c;其实就是我们学习编程时最初被植入的逻辑…

【安全每日一讲】加强数据安全保护 共享数字化时代便利

前言 数据安全是数据治理的核心内容之一&#xff0c;随着数据治理的深入&#xff0c;我不断的碰到数据安全中的金发姑娘问题&#xff08;指安全和效率的平衡&#xff09;。 DAMA说&#xff0c;降低风险和促进业务增长是数据安全活动的主要驱动因素&#xff0c;数据安全是一种资…

sbt安装

一、sbt介绍 在Spark中&#xff0c;sbt&#xff08;Scala Build Tool&#xff09;是一个用于构建Scala项目的工具。它是Spark项目的主要构建工具之一&#xff0c;用于编译Scala代码、管理依赖项、打包应用程序以及执行其他与项目构建相关的任务。 sbt的用途在Spark开发中主要…

基于Nios软件实现流水灯+串口输出

基于NIOS-II软核实现流水灯串口输出 引言&#xff1a; ​ 在现代电子设计领域&#xff0c;FPGA&#xff08;现场可编程门阵列&#xff09;因其灵活性和并行处理能力而成为实现复杂数字系统的首选平台。Nios II&#xff0c;作为Altera&#xff08;现为Intel旗下&#xff09;提供…

VMware虚拟机故障:“显示指定的文件不是虚拟磁盘“,处理办法

一、故障现象 由于虚拟机宕机&#xff0c;强制重新启动虚拟机后显示错误&#xff0c;没有办法启动虚拟机。 虚拟机有快照&#xff0c;执行快照还原&#xff0c;结果也不行&#xff0c;反复操作&#xff0c;在虚拟机文件目录出现很多莫名文件 二、故障原因 根据故障提示&#…

数据结构(C):玩转链表

目录 &#x1f37a;0.前言 1.链表的概念 2.链表的分类 2.1带头不带头 2.2单向和双向 2.3循环和不循环 2.4主要使用的链表 3.链表的实现 3.1申请一个链表 3.2头插和尾插 3.2.1函数的形参问题 3.2.2二级指针问题解决 3.3头删和尾删 3.4打印链表 3.5查找 3.5销…

【谷粒商城】03创建商品模块

1.创建模块 2.创建项目微服务 商品服务、仓储服务、订单服务、优惠券服务、用户服务 共同&#xff1a; 1&#xff09;、web、openfeign 2&#xff09;、每一个服务&#xff0c;包名 com.atguigu.gulimall.xxx(product/order/ware/coupon/member) 3&#xff09;、模块名&#x…

​《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制德国每日风能和太阳能产量3D线图

在MATLAB中&#xff0c;要绘制3D线图&#xff0c;可以使用 plot3 函数。 在《MATLAB科研绘图与学术图表绘制从入门到精通》书中通过绘制德国每日风能和太阳能产量3D线图解释了如何在MATLAB中绘制3D线图。 购书地址&#xff1a;https://item.jd.com/14102657.html

完美解决Windows10下-更换JDK环境变量后,在cmd下执行仍java -version然出现原来版本的JDK的问题

一、错误场景预演 本人欲将 JDK 1.8 通过安装包的方式升级为 JDK 22。 本地旧版本&#xff1a;1.8.0_221预升级版本&#xff1a;22.0.1 1.1、查看本地旧版本 在配置环境变量之前&#xff0c;首先我们要明确&#xff0c;本地存在旧版本&#xff0c;如果本地没有 Java&#x…

MFC通过继承现有控件自定义控件

在MFC 自定义控件&#xff0c;可以通过继承MFC提供的控件类&#xff08;如CButton、CEdit、CListBox等&#xff09;并重写其成员函数和消息处理函数来实现。 以下是一个基本的步骤指南&#xff0c;用于在MFC中创建自定义控件&#xff1a; 确定要继承的基类&#xff1a; 首先…

vm16安装最新版本的ubuntu虚拟机,并安装g++的步骤记录

背景 低版本的ubuntu安装G一直不成功&#xff0c;干脆安装最新版的 官网下载 bing搜索ubuntu 下载完成 vm16新建虚拟机 一直下一步&#xff0c;安装完成 终端输入命令 sudo apt-get update ᅟᅠ       sudo apt install gcc ᅟᅠ      sudo apt install g

树莓派点亮FPGA小灯

树莓派点亮FPGA小灯 引言&#xff1a; ​ 本次实验的目的是通过树莓派和FPGA之间的串口通信&#xff0c;控制FPGA开发板上的小灯。实验将展示如何使用树莓派发送特定的字符信号&#xff0c;通过串口传输至FPGA&#xff0c;并在FPGA上实现逻辑解析&#xff0c;以点亮指定的小灯。…

【QT】QT背景介绍

本专栏内容为&#xff1a;QT学习专栏 通过本专栏的深入学习&#xff0c;你可以了解并掌握QT。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;QT &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f…