【C++ —— AVL树】
C++ —— AVL树
- AVL树的概念
- AVL树节点的定义
- AVL树的插入
- 向上调整
- 旋转
- 左单旋
- 右单旋
- 左右双旋
- 右左双旋
- AVL树的高度
- AVL树的验证
- 总结:
- 代码
AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法: 当向二叉搜索树中插入新结点后,如果能保 证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整) 即可降低树的高度,从而减少平均搜索长度。
AVL树本质上是一颗二叉查找树,但是它又具有以下特点:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1 即(-1 / 0 / 1)。
在AVL树中,任何节点的两个子树的高度最大差别为 1 ,所以它也被称为平衡二叉树.
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 o ( l o g 2 n ) o(log_2 n) o(log2n),搜素时间复杂度o( l o g 2 n log_2 n log2n)。

本文规定:
平衡因子时采用公式:平衡因子 = 右子树高度 - 左子树高度
AVL树节点的定义
template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //存储当前节点的平衡因子//构造函数AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};
这里使用了pair来存储节点的值,_bf表示改节点的平衡因子。
AVL树的插入
AVL树的插入步骤大致分为下面两种
- 找到准备插入节点的位置
- 更新节点的平衡因子
- 做出相应的调整
看下面几个实例:


因为新插入的节点会影响到树原本的平衡,所以每次插入都得更新节点的平衡因子,平衡因子的变化有以下几种:
- 首先按照搜索树的规则插入
- 更新插入节点的祖先节点的平衡因子
- 插入父亲的左边,父亲平衡因子
- - - 插入父亲的右边,父亲平衡因子
+ +
- 插入父亲的左边,父亲平衡因子
- 根据父亲的平衡因子分以下三种调整:
- 父亲平衡因子是 0 , 父亲所在子树的高度不变,稳定,不需要向上更新。
- 父亲平衡因子是 1 / -1 ,父亲所在子树的高度变了,继续向上更新直到稳定。
- 父亲平衡因子是 2 / -2 ,父亲所在子树已经不平衡了,需要旋转处理。

向上调整
while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0) //已经平衡,无需调整,结束。{break;}else if (parent->_bf == 1 || parent->_bf == -1) // 0 -> 1 / -1 需要线上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) //1 / -1 -> 2 -2 需要旋转{//旋转}}
旋转
AVL树的旋转分为两种:单旋 和 双旋
其中单旋转又分为:右单旋(RotateR) 和 左单旋(RotateL)
其中双旋转又分为:右左双旋(RotateRL) 和 左右双旋(RotateLR)
为什么会有多旋呢?
因为有些情况单旋无法解决,不得不选择双旋,具体情况下文将继续讨论。
左单旋
假设我们有下图AVL树:

现在需要插入新节点15,插入后如图:

但因为此时节点13的平衡因子为2,不稳定,需要旋转处理,如下图:

由结果可知,将节点13进行左旋转,让节点13的父亲变为节点14,节点14的左孩子变为节点13,节点14的父亲变为节点12,即可达到平衡。
动图演示:

代码:
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 (parent == _root){_root = SubR;SubR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubR;}else{ppNode->_right = SubR;}SubR->_parent = ppNode;}parent->_bf = SubR->_bf = 0;}
右单旋
假设我们有下图AVL树:

现在需要插入新节点3,插入后如图:

但因为此时节点5的平衡因子为**-2**,不稳定,需要旋转处理,如下图:

由结果可知,将节点5进行右旋转,让节点5的父亲变为节点4,节点4的右孩子变为节点5,节点4的父亲变为节点10,即可达到平衡。
动图演示:

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 (parent == _root){_root = SubL;SubL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubL;}else{ppNode->_right = SubL;}SubL->_parent = ppNode;}parent->_bf = SubL->_bf = 0;}
左右双旋
假设我们有下图AVL树:

现在需要插入新节点7,插入后如图:

但因为此时节点10的平衡因子为 -2 节点4的平衡因子是1 ,不稳定,需要先进行左旋处理,如下图:

左旋完成后,此时节点10的平衡因子仍为 -2 不稳定,再进行右单旋,如下图:

由结果可知,经过节点4的左旋处理,再经过节点10的右旋处理,最终平衡。
动图演示:

由上述的上述调整代码可知,因为节点10的平衡因子是-2,所以在进行左右双旋时,节点10是parent,我们定义 节点5为SubL,节点4为SubLR。分别对SubL和parent进行左单旋和右单旋。
但是上述是吧新添加的节点插入到了5的右边,实际上共有下面三中情况可以引起树的双旋转:
1. 插入在较高左子树的右侧的左边
2. 插入在较高左子树的右侧的右边
3. 直接当作较高左子树的右边
插入在较高左子树的右侧的左边:

2. 插入在较高左子树的右侧的右边

3. 直接当作较高左子树的右边

所以由上面三种情况 ,我们可以总结出最终的平衡因子调整规则:
- 当
subLR = 0时:调整为subLR = 0 ,subL = 0 ,parent= 0 - 当
subLR =-1时:调整为subLR =0 ,subL = 0 ,parent = 1 - 当
subLR = 1时:调整为subLR = 0 ,subL =-1,parent = 0
代码:
void RotateLR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;int bf = SubLR->_bf;RotateL(SubL);xRotateR(parent);if (bf == -1){SubLR->_bf = 0;SubL->_bf = 0;parent->_bf = 1;}else if (bf == 1){SubLR->_bf = 0;SubL->_bf = -1;parent->_bf = 0;}else if (bf == 0){SubLR->_bf = 0;SubL->_bf = 0;parent->_bf = 0;}else{assert(false);}}
右左双旋
由左右双旋的推理我们可以得出: 当将新添加的节点插入到较高右子树的左侧使需要右左双旋。
具体分为三种情况:
1. 插入在较高右子树的左侧的右边
2. 插入在较高右子树的左侧的左边
3. 直接当作较高右子树的左边
1. 插入在较高右子树的左侧的右边

2. 插入在较高右子树的左侧的左边

3. 直接当作较高右子树的左边

代码:
void RotateRL(Node* parent){Node* SubR = parent->_right;Node* SubRL = SubR->_left;int bf = SubRL->_bf;RotateR(SubR);RotateL(parent);SubRL->_bf = 0;if (bf == 1){SubRL->_bf = 0;parent->_bf = -1;SubR->_bf = 0;}else if (bf == -1){SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 1;}else{SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 0;}}
AVL树的高度
int _Height(Node* root){if (root == nullptr)return 0;return max(_Height(root->_left), _Height(root->_right)) + 1;}
AVL树的验证
bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}// 顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}
总结:

代码
#pragma once
#include <assert.h>
#include <vector>
#include <iostream>using namespace std;
template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //存储当前节点的平衡因子//构造函数AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public :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 (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更新平衡因子while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0) //已经平衡,无需调整,结束。{break;}else if (parent->_bf == 1 || parent->_bf == -1) // 0 -> 1 / -1 需要线上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) //1 / -1 -> 2 -2{//当前树已经出现了问题,需要旋转if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}break;}else{//不会出现的情况assert(false);}}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_kv.first){cur = cur->_right;}else if (key < cur->_kv.first){cur = cur->_left;}else{return cur;}}return nullptr;}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 (parent == _root){_root = SubL;SubL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubL;}else{ppNode->_right = SubL;}SubL->_parent = ppNode;}parent->_bf = SubL->_bf = 0;}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 (parent == _root){_root = SubR;SubR->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = SubR;}else{ppNode->_right = SubR;}SubR->_parent = ppNode;}parent->_bf = SubR->_bf = 0;}void RotateRL(Node* parent){Node* SubR = parent->_right;Node* SubRL = SubR->_left;int bf = SubRL->_bf;RotateR(SubR);RotateL(parent);SubRL->_bf = 0;if (bf == 1){SubRL->_bf = 0;parent->_bf = -1;SubR->_bf = 0;}else if (bf == -1){SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 1;}else{SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 0;}}void RotateLR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;int bf = SubLR->_bf;RotateL(SubL);xRotateR(parent);if (bf == -1){SubLR->_bf = 0;SubL->_bf = 0;parent->_bf = 1;}else if (bf == 1){SubLR->_bf = 0;SubL->_bf = -1;parent->_bf = 0;}else if (bf == 0){SubLR->_bf = 0;SubL->_bf = 0;parent->_bf = 0;}else{assert(false);}}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}int Size(){return _Size(_root);}void InOrder(){_InOrder(_root);cout << endl;}private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;return max(_Height(root->_left), _Height(root->_right)) + 1;}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}// 顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}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;
};void TestAVLTree1()
{//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int, int> t1;for (auto e : a){/*if (e == 4){int i = 0;}*/// 1、先看是插入谁导致出现的问题// 2、打条件断点,画出插入前的树// 3、单步跟踪,对比图一一分析细节原因t1.Insert({ e,e });cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}t1.InOrder();cout << t1.IsBalance() << endl;
}void TestAVLTree2()
{const int N = 100000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;//cout << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值for (auto e : v){t.Find(e);}// 随机值/*for (size_t i = 0; i < N; i++){t.Find((rand() + i));}*/size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}
相关文章:
【C++ —— AVL树】
C —— AVL树 AVL树的概念AVL树节点的定义AVL树的插入向上调整旋转左单旋右单旋左右双旋右左双旋 AVL树的高度AVL树的验证总结:代码 AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素…...
跨平台webSocket模块设计技术解决方案
1. 概述 目标:设计并实现一个能够在多种操作系统上运行的WebSocket通讯模块,支持与前端浏览器和HTTPS服务端进行数据交换。技术栈:C11 ,使用跨平台库如 Boost处理网络IO,使用 JSON 库如 nlohmann/json 解析消息。 2.…...
Drools规则引擎
一、Drools规则引擎 Drools官网: https://www.drools.org/Drools中文网: http://www.drools.org.cn/bilibili学习视频(黑马博学谷2020年最新Java项目Drools业务规则管理系统(BRMS)): https://www.bilibili.com/video/BV1Pa4y1a7u…...
vue学习day11-路由、路由模块的封装、声明式导航-路由的介绍、VueRouter、router-link、自定义高亮类名
32、路由 (1)路由的介绍 1)生活中的路由:设备和ip的映射关系 2)路由:一种映射关系 3)Vue中的路由:路径与组件的映射关系 (根据路由就能知道不同的路径,应…...
智慧校园学期基础数据管理
在智慧校园基础数据管理之一的学期管理功能管理中,学期的有序管理具有重要意义。它不仅是教学活动有序开展的指挥棒,更是连接学校管理者、教师与学生之间沟通的桥梁,承载着规划、跟踪与管理学期内各项事务的重要使命。 学期管理功能的首要任务…...
ISP代理和双ISP代理:区别和优势
随着互联网技术的不断发展和普及,网络代理服务成为众多用户保护隐私、提高网络性能、增强安全性的重要工具。其中,ISP代理和双ISP代理是两种常见的网络代理服务形式。本文将详细探讨ISP代理和双ISP代理的区别和优势,以便用户更好地了解并选择…...
【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】
持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】 Message Queue 消息队列异步处理应用解耦流量控制 消息中间件概念RabbitMQ概念MessagePublisherExchangeQueueBindingConnectionChannelConsumerVirtual HostBroker图…...
概率论原理精解【4】
文章目录 度量空间概述理论基础定义特点高级概念广泛应用 性质例子应用 柯西数列柯西数列的定义柯西数列的例子 参考文献 度量空间 概述 设 f : R n → R m , f ˙ ( x ) 在 { x : ∣ x − x 0 ∣ < r } 内连续,则当 ∣ t ∣ < r 时, f:R^n\righ…...
Linux云计算 |【第一阶段】ENGINEER-DAY3
主要内容: LVM逻辑卷管理、VDO、RAID磁盘阵列、进程管理 一、新建逻辑卷 1、什么是逻辑卷 逻辑卷(Logical Volume)是逻辑卷管理(Logical Volume Management,LVM)系统中的一个概念。LVM是一种用于磁盘管理…...
springboot 实体类加注解校验入参数据
导入的是springboot自身的依赖包 import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid;...
关于 Qt输入法在arm特定的某些weston下出现调用崩溃 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/140423667 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...
Android Studio关于Gradle及JDK问题解决
1.Android Studio 版本如:Android Studio Koala | 2024.1.1 2.Gradle 版本为:8.7 3.JDK 版本为:17 以上这三个必须匹配,具体可以看官网Android Studio 版本说明(https://developer.android.google.cn/studio?hlzh-…...
Leetcode 205. 同构字符串
205. 同构字符串 Leetcode 205. 同构字符串 一、题目描述二、我的想法三、其他人的题解 一、题目描述 给定两个字符串 s 和 t ,判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。 每个出现的字符都应…...
多口适配器,给您的生活增添便利
随着科技的快速发展,我们的生活已离不开各种各样的电子设备,智能手机、平板电脑、智能手表、无线耳机……它们共同构建了我们丰富多彩的数字生活。然而,面对众多设备的充电需求,传统的单一充电口已难以满足现代人的使用习惯。在这…...
探索现代Web开发:WebKit的剪贴板API革新
探索现代Web开发:WebKit的剪贴板API革新 在当今的Web开发领域,用户体验的提升是开发者们不懈追求的目标。其中一个关键的交互点便是剪贴板操作,它允许用户在网页与本地系统之间复制和粘贴数据。WebKit,作为Safari、QQ浏览器等众多…...
【电路笔记】-放大器的频率响应
放大器的频率响应 文章目录 放大器的频率响应1、概述2、定义3、电容器的影响4、低频响应5、高频响应6、总结1、概述 对于任何电子电路来说,放大器的行为都会受到其输入端子上信号频率的影响。 该特性称为频率响应。 频率响应是放大器最重要的特性之一。 在放大器设计的频率范…...
Artix7系列FPGA实现SDI视频编解码,基于GTP高速接口,提供3套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案在Xilinx--Kintex系列FPGA上的应用本方案在Xilinx--Zynq系列FPGA上的应用 3、详细设计方案设计原理框图SDI 输入设备Gv8601a 均衡器GTP 高速接口-->解串与串化SMPTE SD/HD/3G SDI IP核BT1120转…...
NET 语言识别,语音控制操作、语音播报
System.Speech. 》》System.Speech.Synthesis; 语音播报 》》System.Speech.Recognition 语音识别 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Speech.Recog…...
Unity 调试死循环程序
如果游戏出现死循环如何调试呢。 测试脚本 我们来做一个测试。 首先写一个死循环代码: using System.Collections; using System.Collections.Generic; using UnityEngine;public class dead : MonoBehaviour {void Start(){while (true){int a 1;}}}Unity对象设…...
视频监控平台LntonCVS视频融合共享平台智慧安防视频监控汇聚应用方案
LntonCVS是一款功能强大且灵活部署的安防视频监控平台。它支持多种主流标准协议,包括GB28181、RTSP/Onvif、RTMP等,同时能够兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统的安防监控功能,还支持接入AI智能分析&#x…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...
Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...
GitHub 常见高频问题与解决方案(实用手册)
1.Push 提示权限错误(Permission denied) 问题: Bash Permission denied (publickey) fatal: Could not read from remote repository. 原因: 没有配置 SSH key 或使用了 HTTPS 而没有权限…...
