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

2.性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续的红色节点,可以有连续的黑色节点)
- 每条路径均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
3.红黑树 VS AVL树
红黑树 vs AVL 树核心对比:
1.平衡条件差异
- AVL 树:严格平衡(任意节点左右子树高度差≤1)
- 红黑树:弱平衡(最长路径≤2 倍最短路径)
2.高度特性
- AVL 树:高度 h=O (log n)
- 红黑树:高度 h=O (2 log n) → 实际测试中通常为 1.2-1.4 倍 AVL 树高度
性能差异分析:
-
操作复杂度对比
操作类型 AVL树 红黑树 查找 O(h) O(h) 插入 O(h) O(h) 删除 O(h) O(h) -
实际效率表现
- 查找:AVL 树略优(高度更低)
- 插入 / 删除:红黑树更优(旋转次数少)
- 综合性能:红黑树在动态场景更高效
效率差异原因:
1.旋转成本对比
- AVL 树:每次插入可能需多次旋转(平均 1.02 次)
- 红黑树:最多 3 次旋转(平均 0.2 次)
- 旋转操作对缓存不友好(破坏空间局部性)
2.常数因子影响
- 红黑树节点需维护颜色字段(增加少量内存)
- AVL 树需维护平衡因子(同样增加内存)
- 红黑树的颜色检查比 AVL 树的平衡因子计算更高效
4.实现思路与代码
节点结构定义
// 定义颜色枚举类型,包含红色和黑色两种颜色
enum Colour
{RED,BLACK
};// 定义红黑树节点的模板结构体
template<class K, class V>
struct RBTreeNode
{// 指向左子节点的指针RBTreeNode<K, V>* _left;// 指向右子节点的指针RBTreeNode<K, V>* _right;// 指向父节点的指针,用于回溯操作,方便在插入和删除时调整树的结构RBTreeNode<K, V>* _parent;// 存储键值对,键类型为K,值类型为Vpair<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>
struct RBTree
{// 重定义节点类型,方便后续使用typedef RBTreeNode<K, V> Node;
public:// 这里可以添加插入、删除、查找等操作的函数声明和实现// ......private:// 红黑树的根节点指针,初始化为空指针Node* _root = nullptr;public:// 记录旋转操作的次数,用于调试和性能分析int _rotateCount = 0;
};
1. 颜色枚举类型 Colour
定义了两种颜色:红色(RED)和黑色(BLACK)。红黑树通过节点的颜色来维护树的弱平衡性质。
2. 节点结构体 RBTreeNode
- 指针成员:
_left和_right分别指向左右子节点,用于构建树的层次结构。_parent指向父节点,这在插入和删除操作中非常重要,因为在调整树的结构时,需要回溯到父节点甚至更高层的节点来进行旋转和颜色调整。
- 键值对
_kv:存储实际的数据,键类型为K,值类型为V。红黑树可以作为一种关联容器,通过键来快速查找对应的值。 - 颜色成员
_col:节点的颜色,使用Colour枚举类型表示。新节点默认初始化为红色,因为插入红色节点时,只要父节点不是红色,就不会破坏红黑树的性质,相比插入黑色节点,调整的复杂度更低。
3. 红黑树结构体 RBTree
- 根节点指针
_root:指向红黑树的根节点,初始化为空指针,表示空树。 - 旋转计数变量
_rotateCount:用于记录旋转操作的次数。旋转操作是红黑树调整结构的关键步骤,通过旋转可以在不破坏红黑树性质的前提下,平衡树的高度,减少查找、插入和删除操作的时间复杂度。这个变量可以用于调试和性能分析,帮助我们了解红黑树在不同操作下的调整情况。
插入
代码
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);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* grandfather = parent->_parent;if (parent == grandfather->_left)//情况一:父节点是祖父的左孩子{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)// 叔叔存在且为红{// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在 或 存在且为黑{if (cur == parent->_left){// g// p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // 情况二:父节点是祖父的右孩子{Node* uncle = grandfather->_left;// uncle存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;// 确保根节点为黑return true;}
思路分析
1. 空树处理
-
若根节点
_root为空,直接创建新节点作为根,颜色设为黑色。
2. 寻找插入位置
-
从根节点开始遍历,比较键值:
-
若插入键 > 当前节点键,向右子树查找
-
若插入键 < 当前节点键,向左子树查找
-
若键已存在,返回插入失败
-
3. 插入新节点
-
创建新节点(颜色为红色),链接到父节点的左/右子节点,并设置父指针。
4. 平衡调整(关键步骤)
循环条件:父节点存在且为红色(仅需处理父为红的情况)。
情况一:父节点是祖父的左孩子
-
叔叔存在且为红:
-
变色:父节点和叔叔变黑,祖父变红。
-
继续向上处理:将祖父设为当前节点(父节点、祖父节点也要调整),继续向上调整。
-

-
叔叔不存在或为黑:
-
当前节点是父的左孩子(LL型):
-
右旋祖父节点。
-
父变黑,祖父变红。


-
-
当前节点是父的右孩子(LR型):
-
先左旋父节点,转为 LL 型。
-
再右旋祖父节点。
-
当前节点变黑,祖父变红。

-
-
情况二:父节点是祖父的右孩子
-
叔叔存在且为红:处理同情况一(变色后上移)。
-
叔叔不存在或为黑:
-
当前节点是父的右孩子(RR型):
-
左旋祖父节点。
-
父变黑,祖父变红。
-
-
当前节点是父的左孩子(RL型):
-
先右旋父节点,转为 RR 型。
-
再左旋祖父节点。
-
当前节点变黑,祖父变红。
-
-
5. 确保根节点为黑
-
最终将根节点颜色设为黑色。
旋转操作
左单旋和右单旋
private:void RotateL(Node* parent){++_rotateCount;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateR(Node* parent){++_rotateCount;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}
红黑树的左单旋和右单旋跟AVL树的思路一样,唯一不同的是最后不需要调整平衡因子。
平衡验证
代码
private:bool CheckColour(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark)路径黑色节点数是否一致。return false;return true;}if (root->_col == BLACK){++blacknum;}//是否存在连续红色节点。if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColour(root->_left, blacknum, benchmark)&& CheckColour(root->_right, blacknum, benchmark);}bool IsBalance(Node* root){if (root == nullptr)return true;if (root->_col != BLACK)//确保根节点为黑色。{return false;}// 统计最左路径黑色节点数作为基准值。int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return CheckColour(root, 0, benchmark);//调用 CheckColour 递归验证。}public:bool IsBalance(){return IsBalance(_root);}
思路分析
1.IsBalance 入口函数:
-
确保根节点为黑色。
-
统计最左路径黑色节点数作为基准值。
-
调用
CheckColour递归验证。
2.CheckColour 递归检查:
-
路径黑色节点数是否一致。
-
是否存在连续红色节点。
二、map和set的封装实现
1.红黑树改造
1.1 节点结构改造
template<class T>
struct RBTreeNode {RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data; // 存储数据(set存key,map存pair)Colour _col; // 节点颜色RBTreeNode(const T& data): _data(data), _col(RED), _left(nullptr), _right(nullptr), _parent(nullptr) {}
};
设计要点:
-
使用模板参数T统一存储数据类型
-
set实例化时T=K,map实例化时T=pair<const K,V>
-
保持红黑树基础结构(三叉链、颜色标记)
1.2 红黑树模板参数设计
template<class K, class T, class KeyOfT>
class RBTree {// K: 键值类型// T: 节点数据类型// KeyOfT: 仿函数,用于提取比较键值typedef RBTreeNode<T> Node;
public:// 同一个类模板,传的不同的参数实例化出的不同类型typedef __TreeIterator<T, T*, T&> iterator;typedef __TreeIterator<T, const T*, const T&> const_iterator;//...(省略插入、查找、删除等操作)
};
关键参数说明:
| 参数 | set实例化 | map实例化 |
|---|---|---|
| K | Key类型 | Key类型 |
| T | Key类型 | pair<const Key, Value> |
| KeyOfT | SetKeyOfT(返回自身) | MapKeyOfT(返回pair.first) |
1.3 仿函数实现细节
// Set侧的键值提取
struct SetKeyOfT {const K& operator()(const K& key) {return key; // 直接返回Key本身}
};// Map侧的键值提取
struct MapKeyOfT {const K& operator()(const pair<K, V>& kv) {return kv.first; // 仅提取pair中的Key部分}
};
作用:
-
统一比较接口,避免直接比较pair类型的对象
-
保证比较逻辑的一致性(只使用Key部分进行比较)

2.迭代器系统实现
2.1 迭代器核心结构
template<class T, class Ptr, class Ref>
struct __TreeIterator {typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ptr, Ref> Self;typedef __TreeIterator<T, T*, T&> Iterator;Node* _node;// 支持普通迭代器构造const迭代器__TreeIterator(const __TreeIterator<T, T*, T&>& it): _node(it._node) {}// 解引用操作符Ref operator*() { return _node->_data; }// 成员访问操作符Ptr operator->() { return &_node->_data; }bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}Self& operator--(){if (_node->_left){Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{// 孩子是父亲的右的那个节点Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}// 前置++操作符Self& operator++() {if (_node->_right) {// 右子树存在,找右子树最左节点Node* subLeft = _node->_right;while (subLeft->_left) {subLeft = subLeft->_left;}_node = subLeft;} else {// 右子树不存在,向上查找祖先(下一个访问的是孩子父亲是左的那个祖先)Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};
遍历特性:
-
实现红黑树的中序遍历(升序访问)
-
时间复杂度:均摊O(1)
-
空间复杂度:O(1)(无需栈结构)
2.2 迭代器在树中的应用
template<class K, class T, class KeyOfT>
class RBTree {typedef RBTreeNode<T> Node;
public:// 同一个类模板,传的不同的参数实例化出的不同类型typedef __TreeIterator<T, T*, T&> iterator;typedef __TreeIterator<T, const T*, const T&> const_iterator;iterator begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return iterator(leftMin);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return const_iterator(leftMin);}const_iterator end() const{return const_iterator(nullptr);}//省略其他操作};
2.3 容器迭代器定义
// map侧迭代器定义
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;// set侧迭代器定义
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
关键差异:
| 容器 | 迭代器类型 | 可修改性 |
|---|---|---|
| map | 普通迭代器 | 可修改value,不可修改key |
| set | const迭代器 | 完全不可修改元素 |
3.插入操作深度解析
3.1 插入函数改造
pair<iterator, bool> Insert(const T& data)
{// [1] 空树处理if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root), true);}// [2] 查找插入位置Node* parent = nullptr;Node* cur = _root;KeyOfT kot;while (cur) {parent = cur;if (kot(cur->_data) < kot(data))cur = cur->_right;else if (kot(cur->_data) > kot(data))cur = cur->_left;elsereturn make_pair(iterator(cur), false);}// [3] 创建新节点cur = new Node(data);cur->_col = RED; // 新节点初始为红色// ... 连接节点 ...// [4] 平衡调整(核心逻辑)while (parent && parent->_col == RED) {// 处理红红冲突// 包含颜色翻转、旋转等操作...}_root->_col = BLACK; // 确保根节点为黑色return make_pair(iterator(newnode), true);
}
关键改进点:
-
返回值改为pair<iterator, bool>,便于获取插入位置
-
使用KeyOfT仿函数统一比较逻辑
3.2 返回值处理
// map侧直接转发
pair<iterator, bool> insert(const pair<K, V>& kv) {return _t.Insert(kv);
}// set侧需要处理const迭代器
pair<iterator, bool> insert(const K& key) {auto ret = _t.Insert(key);return pair<iterator, bool>(ret.first, ret.second);
}
特殊处理:
-
set的iterator本质是const_iterator,防止修改Key值
-
通过迭代器构造函数实现普通迭代器到const迭代器的转换(具体实现见迭代器核心结构处的代码)
4.容器完整实现
map完整实现
template<class K, class V>
class map {struct MapKeyOfT {const K& operator()(const pair<const K, V>& kv) {return kv.first;}};typedef RBTree<K, pair<const K, V>, MapKeyOfT> RBTree;
public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin() { return _t.begin(); }iterator end() { return _t.end(); }V& operator[](const K& key) {pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<K, V>& kv) {return _t.Insert(kv);}private:RBTree _t;
};
set完整实现
template<class K>
class set {struct SetKeyOfT {const K& operator()(const K& key) {return key;}};typedef RBTree<K, K, SetKeyOfT> RBTree;
public:typedef typename RBTree::const_iterator const_iterator;typedef typename RBTree<K, pair<const K, V>,SetKeyOfT>::const_iterator const_iterator;iterator begin() const { return _t.begin(); }iterator end() const { return _t.end(); }pair<iterator, bool> insert(const K& key) {auto ret = _t.Insert(key);return pair<iterator, bool>(ret.first, ret.second);}private:RBTree _t;
};
相关文章:
C++----红黑树map和set的封装
一、红黑树 1.概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出2倍࿰…...
【报错】微信小程序预览报错”60001“
1.问题描述 我在微信开发者工具写小程序时,使用http://localhost:8080是可以请求成功的,数据全都可以无报错,但是点击【预览】,用手机扫描二维码浏览时,发现前端图片无返回且报错60001(打开开发者模式查看日…...
软考 数据通信基础——信道
信道特性 带宽 在模拟信号里频率的差,表示信道能通过的频率 在数字信号里表示最大传输速率,单位用bit/s 通常用W表示 波特率 即码元速率,码元可看作一个时间周期 码元速率B2W也可写成B1/T 码元种类n和码元信息量个数N存在以下关系 Nl…...
windows 平台如何点击网页上的url ,会打开远程桌面连接服务器
你可以使用自定义协议方案(Protocol Scheme)实现网页上点击URL后自动启动远程桌面连接(mstsc),参考你提供的C代码思路,如下实现: 第一步:注册自定义协议 使用类似openmstsc://协议…...
uni-app开发的App和H5嵌套封装的App,以及原生App有什么区别
uni-app 开发的 App 和 H5 嵌套封装的 App 是两种不同的开发模式,虽然它们都可以实现跨平台开发,但在技术实现、性能、功能支持等方面有显著区别。以下是详细对比: 1. uni-app 开发的 App uni-app 是一个基于 Vue.js 的跨平台开发框架&#…...
Anaconda中虚拟环境安装g++和gcc相同版本
安装torchSDF的时候遇到的,这是g和gcc版本不一致的问题 gcc: fatal error: cannot execute cc1plus: execvp: No such file or directory compilation terminated.查看gcc, g版本 gcc --version | head -n1 g --version | head -n1发现gcc的是anaconda中的&#x…...
Docker数据管理,端口映射与容器互联
1.Docker 数据管理 在生产环境中使用 Docker,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及容器的数据管理操作。 容器中的管理数据主要有两种方式: 数据卷(Data Volumns)&a…...
部署前后端项目
部署项目 liunx 软件安装 软件安装方式 在Linux系统中,安装软件的方式主要有四种,这四种安装方式的特点如下: 建议nginx、MySQL、Redis等等使用docker安装,会很便捷,这里只演示JDK、ngxin手动的安装 安装JDK 上述我…...
从零构建逻辑回归: sklearn 与自定义实现对比
文章目录 理论基础1. 逻辑回归模型2. 损失函数3. 梯度推导(1) 计算 ∂ L ∂ y ^ \frac{\partial L}{\partial \hat{y}} ∂y^∂L(2) 计算 ∂ y ^ ∂ z \frac{\partial \hat{y}}{\partial z} ∂z∂y^(3) 计算 ∂ L ∂ z \frac{\partial L}{\partial z} ∂z∂L(4) 计…...
1256:献给阿尔吉侬的花束--BFS多组输入--memset
1256:献给阿尔吉侬的花束--BFS多组输入--memset 题目 解析代码【结构体】用book标记且计步数的代码[非结构体法] 题目 解析 标准的BFS题目,在多组输入中要做的就是先找到这一组的起点和终点,然后将其传给bfs,在多组输入中最易忘记…...
【JavaEE】SpringBoot快速上手,探秘 Spring Boot,搭建 Java 项目的智慧脚手架
1.Spring Boot介绍 在学习SpringBoot之前, 我们先来认识⼀下Spring ,我们看下Spring官⽅的介绍 可以看到,Spring让Java程序更加快速, 简单和安全。 Spring对于速度、简单性和⽣产⼒的关注使其成为世界上最流⾏的Java框架。 Spring官⽅提供了很多开源的…...
【C】初阶数据结构9 -- 直接插入排序
前面我们学习了数据结构二叉树,接下来我们将开启一个新的章节,那就是在日常生活中经常会用到的排序算法。 所谓排序算法就是给你一堆数据,让你从小到大(或从大到小)的将这些数据排成一个有序的序列(这些数据…...
Lottie与LottieFiles:快速为前端Web开发注入精美动画的利器
目录 Lottie与LottieFiles:快速为前端Web开发注入精美动画的利器 一、Lottie是什么?从GIF到JSON的动画技术演进 1、传统动画臃肿的Gif 2、Lottie的突破性创新 二、Lottie的核心组件解析(Lottie的技术架构) 1、Lottie核心三要…...
Spring boot创建时常用的依赖
新建SpringBoot Maven项目中pom常用依赖配置及常用的依赖的介绍 1.springboot项目的总(父)依赖大全 <parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>2.3.3.RELEASE<…...
音乐API
https://neteasecloudmusicapi.vercel.app/docs/#/https://neteasecloudmusicapi.vercel.app/docs/#/ 使用实例 所有榜单内容摘要 说明 : 调用此接口,可获取所有榜单内容摘要 接口地址 : /toplist/detail 调用例子 : /toplist/detail 获取歌单所有歌曲 说明 : 由于网易云…...
UML(统一建模语言)详解:从理论到实践
1. UML概述 1.1 定义与历史背景 统一建模语言(Unified Modeling Language, UML) 是一种标准化的可视化建模语言,用于描述、设计、构造和文档化软件系统。它诞生于1994-1997年,由Grady Booch、James Rumbaugh和Ivar Jacobson&…...
C语言练习题--洛谷P5143攀爬者
题目背景 HKE 考完 GDOI 之后跟他的神犇小伙伴们一起去爬山。 题目描述 他在地形图上标记了 N 个点,每个点 Pi 都有一个坐标 (xi,yi,zi)。所有点对中,高度值 z 不会相等。HKE 准备从最低的点爬到最高的点,他的攀爬满足以下条件&am…...
MySQL常见字段值处理
一、数据拼接 1、CONCAT CONCAT(string1, string2, ..., stringN),将两个或多个字符串连接在一起 自动忽略 NULL 值参数,仅拼接非 NULL 的字符串。 第一个参数必须是分隔符(字符串)。 SELECT CONCAT(Hello, , World); -- 输…...
OpenCV实现图像分割与无缝合并
一、图像分割核心方法 1、阈值分割 #include <opencv2/opencv.hpp> using namespace cv; int main() {Mat img imread("input.jpg", IMREAD_GRAYSCALE);Mat binary;threshold(img, binary, 127, 255, THRESH_BINARY); // 固定阈值分割imwrite("binary.…...
百度之星2023——公园
这道题目用bfs做反而麻烦了。 首先抓题目关键字,要求“最少”,那大概率就是最短路径问题。虽然这题是一个无权图,用bfs也能求最短路径,但是我们知道使用dijkstra是能够利用dist数组持久化最短路径的,相比每次都要bfs&…...
从零搭建微服务项目Pro(第3-1章——本地/OSS图片文件存取)
前言: 在小型demo项目中,一般将图片音频等字节流文件存放本地数据库,但企业级项目中,由于数据量容量有限,需要借助OSS来管理大规模文件。 OSS(对象存储服务,Object Storage Service࿰…...
游戏引擎学习第147天
仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾 具体来说,我们通过隐式计算来解决问题,而不是像数字微分分析器那样逐步增加数据。我们已经涵盖了这个部分,并计划继续处理音量问题。不过,实际上我们现在不需要继续处理…...
Spring boot启动原理及相关组件
优质博文:IT-BLOG-CN 一、Spring Boot应用启动 一个Spring Boot应用的启动通常如下: SpringBootApplication Slf4j public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx SpringApplication.…...
【Linux】信号处理以及补充知识
目录 一、信号被处理的时机: 1、理解: 2、内核态与用户态: 1、概念: 2、重谈地址空间: 3、处理时机: 补充知识: 1、sigaction: 2、函数重入: 3、volatile&…...
微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由
之前学习了Nacos,用于发现并注册、管理项目里所有的微服务,而OpenFeign简化微服务之间的通信,而为了使得前端可以使用微服务项目里的每一个微服务的接口,就应该将所有微服务的接口管理起来方便前端调用,所以有了网关。…...
【leetcode hot 100 19】删除链表的第N个节点
解法一:将ListNode放入ArrayList中,要删除的元素为num list.size()-n。如果num 0则将头节点删除;否则利用num-1个元素的next删除第num个元素。 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...
comctl32!ListView_OnSetItem函数分析LISTSUBITEM结构中的image表示图标位置
第一部分: BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi) { LISTSUBITEM lsi; BOOL fChanged FALSE; int i; int idpa; HDPA hdpa; if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) { …...
数据结构——多项式问题(顺序存储结构or链式存储结构)
补充:malloc函数: malloc 函数是 C 语言标准库中的一个重要函数,位于 <stdlib.h> 头文件中,主要用于在程序运行时动态分配内存。以下将详细介绍其用法。 前面的返回值指针可以自己定义,如 (int*&am…...
【学习方法】技术开发者的提问智慧:如何高效获得解答?
技术开发者的提问智慧:如何高效获得解答? 在技术开发过程中,每个人都会遇到无法解决的问题。此时,我们通常会向团队、社区或论坛求助。然而,为什么有些人的问题能迅速得到解答,而有些人的问题却石沉大海&a…...
记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)
文章目录 记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序(…...

