当前位置: 首页 > news >正文

【C++历练之路】红黑树——map与set的封装实现

W...Y的个人主页💕

gitee代码仓库分享😊 

 


前言:上篇博客中,我们为了使二叉搜索树不会出现”一边倒“的情况,使用了AVL树对搜索树进行了处理,从而解决了数据在有序或者接近有序时出现的情况。但是AVL树还会有一大缺陷就是其性能的原因,当我们在使其满足AVL树的规则时,其付出的旋转代价是非常大的,所以经常修改的结构就不适合AVL树。但是红黑树就可以补足AVL树的缺陷。

目录

1. 红黑树

1.1 红黑树的概念

1.2 红黑树的性质 

 1.3红黑树节点的定义

 1.4红黑树的插入

1.5 红黑树与AVL树的比较 

2. 红黑树模拟实现STL中的map与set

2.1 红黑树的迭代器

2.2 红黑树的改写与迭代器完整代码

2.3 map的封装

2.4 set的封装


1. 红黑树

1.1 红黑树的概念

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

假设最短路径为h,则最长路径为2h。 

1.2 红黑树的性质 

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

为什么满足上述性质,红黑树就可以保证其最长路径的节点树不会超过最短路径的两倍呢?  

 前两个性质非常通俗易懂,我们从第三个性质开始解读:

3.没有连续的两个红色节点。

4.每条路径的黑色节点数是相同的。

所以我们就可以假设一颗红黑树中每条路径有两个黑色节点(满足性质4),那最短路径只可能是全黑节点,最长路径一定是一黑一红节点(假设最短路径与最长路径都存在),那么红色节点只能在黑色节点中间插入,这样才能满足性质3,所以红黑树就可以保证其最长路径的节点树不会超过最短路径的两倍。

对比AVL树与红黑树的结构,其AVL树的高度近似logN,而红黑树的高度近似2logN,所以相对于AVL树,红黑树的搜索效率差一些,但是几乎可以忽略不计,因为logN足够小,所以他们之间的搜索差距微乎其微。

 1.3红黑树节点的定义

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

在节点定义时我们默认将节点设置成红色节点,这样如果出现连续的红色节点时,我们可以进行变色操作,通过维护一个子树来使红黑树合法,但是如果插入一个黑色节点时,我们就无法下手,因为每条路的黑色节点数必须相同,这样我们无法很好的进行操作使其合法化。 

 1.4红黑树的插入

 红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点:

class RBTree
{typedef RBTreeNode<K, V> Node;
public: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->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;
}
private:Node* _root = nullptr;
};

2. 检测新节点插入后,红黑树的性质是否造到破坏:
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何
性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连
在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点 

 情况一: cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。 

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

 如果u存在且为黑, 则cur一定不是新增节点,因为这样就不满足性质4:每条路径黑色节点个数相同。所以我们将上面图补充完整就是下图所示,先是由情况一进行调整,然后向上调整后才得到上图。所以情况一向上调整后的情况不一定又是情况一!!

这时我们就无法用情况一的做法进行调整, 如果继续用情况一法则已经违反规则了。左右极度不均衡只能进行选择,这时我们就可以类比使用AVL树中的旋转法则。(旋转+变色)

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红

在变色时我们不区分其p与u节点的左右,但是在旋转时我们就要进行区分。

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑 

 这种情况并不是一边高,而是两边都高左边高右边也高。所以我们得使用左右/右左双旋转。

p为g的左孩子,cur为p的右孩子,左右双旋+变色

p为g的右孩子,cur为p的左孩子,右左双旋+变色

针对每种情况进行相应的处理即可。

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->_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    u// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//       g//    p     u//      cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;} }else{Node* uncle = grandfather->_left;// 情况一:叔叔存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){++rotateSize;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_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;}}void RotateR(Node* parent){++rotateSize;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_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;}

1.5 红黑树与AVL树的比较 

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

2. 红黑树模拟实现STL中的map与set

我们模拟实现了红黑树,接下来就是对map与set的封装。

我们通过观察其stl源码切入进行仿写:

 我们发现无论是map还是set都复用的一个红黑树,模板参数都是k,v模型。通过源码我们可以发现:set<k> -> rb_tree<k,k> map<k,v> ->rb_tree<k,pair<const k,v>>。所以节点中存什么内容是由v决定的,不是k决定的。将红黑树写成泛型,所以我们必须将上面写的红黑树的模板进行修改!

进行封装后就可以解决复用红黑树的模板。 

 但是使用红黑树的模板时,set和map所比较的对象不一样,因为set比较的就是key,而map比较的是value,所以我们就得使用仿函数进行操作,我们创建keyoft仿函数取出T对象中的key即可。

//set
struct SetKeyOfT{const K& operator()(const K& key){return key;}};//map
struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};

2.1 红黑树的迭代器

首先我们得封装一个红黑树的迭代器用来实现++、--、*、->、!=。这里的迭代器与list的迭代器非常类似,可以用一个指针来实现其内容。但是++与--必须确定下一个与上一个节点的关系,这里我们可以使用中序遍历解决,但是得用一个栈来辅助,这里我们不想使用这样的方法,我们可以找规律来实现:

++逻辑

1.it指向节点,右不为空,下一个就是右子树的最左节点

2.it指向节点,右为空,意味着这个节点的子树中序访问完了,下一个节点找祖先里面的孩子==父亲左的那个祖先。

--逻辑与++相反!

这样迭代器就很好解决了:

template<class T>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}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 = cur->_parent;}_node = parent;}return *this;}Self& operator--(){// return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operato == (const Self & s){return _node == s._node;}
};

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代
器,需要考虑以前问题:

begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,
可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位
置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?
能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最
后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置: 

typedef RBTreeIterator<T> iterator;iterator begin()
{Node* subLeft = _root;while (subLeft && subLeft->_left){subLeft = subLeft->_left;}return iterator(subLeft);
}iterator end()
{return iterator(nullptr);
}
//map
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
iterator begin()
{return _t.begin();
}iterator end()
{return _t.end();
}bool insert(const pair<K, V>& kv)
{return _t.Insert(kv);
}
//settypedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;iterator begin()
{return _t.begin();
}iterator end()
{return _t.end();
}bool insert(const K& key)
{return _t.Insert(key);
}

2.2 红黑树的改写与迭代器完整代码

#pragma once
#include<vector>enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;T _data;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}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 = cur->_parent;}_node = parent;}return *this;}Self& operator--(){// return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operato == (const Self & s){return _node == s._node;}
};// set->RBTree<K, K, SetKeyOfT>
// map->RBTree<K, pair<K, V>, MapKeyOfT>// KeyOfT仿函数 取出T对象中的key
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T> iterator;iterator begin(){Node* subLeft = _root;while (subLeft && subLeft->_left){subLeft = subLeft->_left;}return iterator(subLeft);}iterator end(){return iterator(nullptr);}bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data); // 红色的if (kot(parent->_data) < kot(data)){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    u// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//       g//    p     u//      cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;// 情况一:叔叔存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_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;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_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;}}private:Node* _root = nullptr;
};

2.3 map的封装

namespace why
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}bool insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}

2.4 set的封装

#include"RBTree.h"
namespace why
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}bool insert(const K& key){return _t.Insert(key);}private:RBTree<K, const K, SetKeyOfT> _t;};
}

 

相关文章:

【C++历练之路】红黑树——map与set的封装实现

W...Y的个人主页&#x1f495; gitee代码仓库分享&#x1f60a; 前言&#xff1a;上篇博客中&#xff0c;我们为了使二叉搜索树不会出现”一边倒“的情况&#xff0c;使用了AVL树对搜索树进行了处理&#xff0c;从而解决了数据在有序或者接近有序时出现的情况。但是AVL树还会…...

RDB快照是怎么实现的?

RDB快照是怎么实现的&#xff1f; 前言快照怎么用&#xff1f;执行快照时&#xff0c;数据能被修改吗&#xff1f;RDB 和 AOF 合体 前言 虽说 Redis 是内存数据库&#xff0c;但是它为数据的持久化提供了两个技术。 分别是「 AOF 日志和 RDB 快照」。 这两种技术都会用各用一…...

智能体可靠性的革命性提升,揭秘知识工程领域的参考架构新篇章

引言&#xff1a;知识工程的演变与重要性 知识工程&#xff08;Knowledge Engineering&#xff0c;KE&#xff09;是一个涉及激发、捕获、概念化和形式化知识以用于信息系统的过程。自计算机科学和人工智能&#xff08;AI&#xff09;历史以来&#xff0c;知识工程的工作流程因…...

Shell 初始化配置指北 | Ubuntu

唠唠闲话 概要&#xff1a;在不同的Shell环境&#xff08;如Bash和Zsh&#xff09;中设置环境变量、设置初始脚本&#xff0c;以及如何根据不同的使用场景&#xff08;用户级或系统级&#xff09;管理和设置初始运行命令。 p.s. 如果你很熟悉 Linux&#xff0c;推荐跳到最后一…...

[嵌入式系统-69]:RT-Thread-组件:网络组件“组”,RT-Thread系统通向外部网络世界的入口

目录 RT-Thread 提供的网络世界入口 - 网络组件 1. 总概 2. AT 3. Lwip&#xff1a; 轻量级IP协议栈 4. W5500 5. Netdev 6. RT-Thread SAL&#xff08;Socket Abstraction Layer&#xff09;套接字和BSD套接字区别 RT-Thread SAL 套接字接口示例 BSD 套接字接口示例 …...

Linux学习笔记1---Windows上运行Linux

在正点原子的教程中学习linux需要安装虚拟机或者在电脑上安装一个Ubuntu系统&#xff0c;但个人觉得太麻烦了&#xff0c;现在linux之父加入了微软&#xff0c;因此在Windows上也可以运行linux 了。具体方法如下&#xff1a; 一、 在Windows上的设置 在window的搜索框内&#…...

Java算法-力扣leetcode-135. 分发糖果

135. 分发糖果 n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xff0c;计算并…...

企业为什么需要主数据管理工具?十大热门主数据管理工具盘点

主数据管理是一套综合性的策略和技术&#xff0c;用于协调和管理企业内用于识别关键业务实体&#xff08;如客户、产品、供应商和员工&#xff09;的一致性、准确性和统一性的数据。主数据管理的目的是创建一个“单一真相源”&#xff0c;确保在不同部门和系统之间共享的数据保…...

免费思维13招之一:体验型思维

思维01:体验型思维 第一大战略:体验型思维。 体验型思维是免费思维中最简单的思维,我们先从最简单的讲起,由简入繁,简单的我们少讲,复杂的我们多讲。 那么,什么是体验型思维呢? 很简单,就是先让客户进行体验,再进行成交的方式。这一种思维,具体的可以分为两种:…...

面试C++(基础篇)-NULL与nullptr的区别?

3: NULL与nullptr的区别&#xff1f; 在C中&#xff0c;NULL和nullptr都用于表示空指针&#xff0c;但它们之间存在一些关键的区别&#xff1a; 1. 来源和含义&#xff1a; • NULL&#xff1a;在C中&#xff0c;NULL最初是从C语言中继承过来的&#xff0c;定义在<cstddef…...

「AIGC」深度学习

深度学习是机器学习的一个子领域&#xff0c;它基于人工神经网络的学习算法。深度学习在图像和语音识别、自然语言处理、医学图像分析、药物发现、自动驾驶汽车等领域取得了显著的进展。以下是围绕深度学习的几个关键主题的阐述。 学习路线 基础数学&#xff1a; 了解线性代数…...

mysql5.7数据库安装及性能测试

mysql5.7数据库安装及性能测试 记录Centos7.9下安装mysql 5.7并利用benchmark工具简单测试mysql的性能。 测试机&#xff1a;centos7.9 配置&#xff1a;4C8G40G 1. 下安装mysql5.7 安装mysql5.7&#xff1a; # 通过官方镜像源安装$ wget http://dev.mysql.com/get/mysql57-com…...

聪明与诚实:社会信任的桥梁

在现代社会中&#xff0c;我们经常听到这样的评价&#xff1a;“某人真聪明。”然而&#xff0c;当我们深入思考时&#xff0c;会发现“聪明”这个词背后所承载的含义并不单一。聪明和狡诈往往被混淆&#xff0c;而诚实的价值却时常被忽视。在一个高度诚信的社会里&#xff0c;…...

基于单片机的无线数据传输系统设计

摘要:基于单片机的无线数据传输系统的设计,实现了温度和湿度的自动采集、无线通讯和报警功能。该系统包括了LCD1602显示电路、DHT11温湿度采集电路等,完成了基于无线数据传输的方法来实现温湿度的采集。 关键词:温湿度检测;N RF 24 L 01;单片机 0 引言 随着科技水平的提高,…...

【IP:Internet Protocol,子网(Subnets),IPv6:动机,层次编址:路由聚集(rout aggregation)】

文章目录 IP&#xff1a;Internet Protocol互联网的的网络层IP分片和重组&#xff08;Fragmentation & Reassembly&#xff09;IP编址&#xff1a;引论子网&#xff08;Subnets&#xff09;特殊IP地址IP 编址: CIDR子网掩码&#xff08;Subnet mask&#xff09;转发表和转发…...

智启算力平台基本操作

智启算力平台 智启算力平台路径搭载数据集搭载镜像配置 智启算力平台 开发文档 帮助文档 - OpenI - 启智AI开源社区 路径搭载 OpenIOSSG/promote: 启智AI协作平台首页推荐组织及推荐项目申请。 - notice/Other_notes/SDKGetPath.md at master - promote - OpenI - 启智AI开…...

微信小程序 【关键部分】

1. 动机 最近在开发小程序&#xff0c;小程序既需兼顾针对新用户的内容预览&#xff0c;又要为注册用户提供服务&#xff0c;简单梳理下&#xff0c;基本需求如下&#xff1a; 小程序共三个tab页&#xff0c;所有用户都可以浏览首页内容&#xff0c;了解我们可以提供的优质服…...

JavaEE技术之MySql高级(索引、索引优化、sql实战、View视图、Mysql日志和锁、多版本并发控制)

文章目录 1. MySQL简介2. MySQL安装2.1 MySQL8新特性2.2 安装MySQL2.2.1 在docker中创建并启动MySQL容器&#xff1a;2.2.2 修改mysql密码2.2.3 重启mysql容器2.2.4 常见问题解决 2.3 字符集问题2.4 远程访问MySQL(用户与权限管理)2.4.0 远程连接问题1、防火墙2、账号不支持远程…...

OCR文本识别模型CRNN

CRNN网络结构 论文地址&#xff1a;https://arxiv.org/pdf/1507.05717 参考&#xff1a;https://blog.csdn.net/xiaosongshine/article/details/112198145 git:https://github.com/shuyeah2356/crnn.pytorch CRNN文本识别实现端到端的不定长文本识别。 CRNN网络把包含三部分&…...

【数据结构】闲谈A股实时交易的数据结构-队列

今天有点忙&#xff0c;特意早起&#xff0c;要不先写点什么。看到个股的红红绿绿&#xff0c; 突然兴起&#xff0c;要不写篇文章分析下A股交易的简易版数据结构。 在A股实时股票交易系统中&#xff0c;按照个人理解&#xff0c;大致会用队列来完成整个交易。队列&#xff08;…...

深入探索van Emde Boas树:原理、操作与C语言实现

van Emde Boas (vEB) 树是一种高效的数据结构&#xff0c;用于处理整数集合。它是由荷兰计算机科学家Jan van Emde Boas在1977年提出的。vEB树在处理整数集合的查找、插入、删除和迭代操作时&#xff0c;能够以接近最优的时间复杂度运行。vEB树特别适合于那些元素数量在某个较小…...

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-14-主频和时钟配置

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…...

tomcat打开乱码修改端口

将UTF-8改成GBK 如果端口冲突&#xff0c;需要修改tomcat的端口...

03 JavaSE-- 访问控制权限、接口、抽象类、内部类、Object类、异常

1. Exception 异常 在 Java 中&#xff0c;异常分为两种主要类型&#xff1a;强制性异常&#xff08;Checked Exceptions&#xff09;和非强制性异常&#xff08;Unchecked Exceptions&#xff09;。 强制性异常&#xff08;Checked Exceptions&#xff09;&#xff1a; 强制…...

free5gc+ueransim操作

启动free5gc容器 cd ~/free5gc-compose docker-compose up -d 记录虚拟网卡地址&#xff0c;eth0 ifconfig 查看并记录amf网元的ip地址 sudo docker inspect amf "IPAddress"那一行&#xff0c;后面记录的即是amf的ip地址 记录上述两个ip地址&#xff0c;完成UER…...

麦肯锡精英高效阅读法笔记

系列文章目录 如何有效阅读一本书笔记 读懂一本书笔记 麦肯锡精英高效阅读法笔记 文章目录 系列文章目录序章 无法读书的5个理由无法读书的理由① 忙于工作&#xff0c;没时间读书无法读书的理由② 不知应该读什么无法读书的理由③ 没读完的书不断增多无法读书的理由④ 工作繁…...

高速、简单、安全的以太彩光,锐捷网络发布极简以太全光 3.X 方案

从 2021 年 3 月正式推出到现在&#xff0c;锐捷网络极简以太全光方案已经走进第四个年头。IT 仍在不断向前发展&#xff0c;数字化进程深入&#xff0c;数字化业务增多&#xff0c;更广泛的终端设备接入企业级园区网络&#xff0c;对园区网络提出了更高的要求&#xff0c;例如…...

图书管理系统

一、图书管理系统菜单 &#x1f353;管理员菜单 1.查找图书 2.新增图书 3.删除图书 4.显示图书 0.退出系统 --------------------------------------------------------------------------------------------------------------------------------- &#x1f33c;用户菜…...

图解HTTP(2、简单的 HTTP 协议)

HTTP 协议用于客户端和服务器端之间的通信 请求访问文本或图像等资源的一端称为客户端&#xff0c;而提供资源响应的一端称为服务器端。 通过请求和响应的交换达成通信 请求必定由客户端发出&#xff0c;而服务器端回复响应报文 请求报文是由请求方法、请求 URI、协议版本、…...

小鹅知识付费系统登录,网课怎么推广与宣传?有啥获客方法?

现在很多教育机构都开始做网络课程&#xff0c;同行之间的竞争也愈发激烈&#xff0c;机构的网课想要盈利就需要对课程进行宣传推广&#xff0c;网课要怎么推广和宣传呢&#xff1f; 在线课程要想推广获客方法有几种&#xff0c;不同推广方法获客效果也是不同的&#xff0c;只有…...

韩顺平0基础学Java——第5天

p72——p86 今天同学跟我说别学java&#xff0c;真的吗&#xff1f;唉&#xff0c;先把这视频干完吧。 逻辑运算符练习 x6&#xff0c;y6 x6&#xff0c;y5 x11&#xff0c;y6 x11&#xff0c;y5 z48 错了&a…...

单片机为什么能直接烧录程序?

在设计芯片的时候&#xff0c;关于烧录的环节是一个不得不考虑的问题。首先排除掉&#xff0c;由外部硬件直接操控FLASH的方案&#xff0c;这个方案有很多缺点。 1、每个IC使用的FLASH型号各不相同&#xff0c;每种型号的FLASH的烧录命令和流程都有差别&#xff0c;这会导致烧…...

【Linux】25. 网络基础(一)

网络基础(一) 计算机网络背景 网络发展 独立模式: 计算机之间相互独立; 网络互联: 多台计算机连接在一起, 完成数据共享; 其实本质上一台计算机内部也是一个小型网络结构(如果我们将计算机内部某个硬件不存放在电脑中&#xff0c;而是拉根长长的线进行连接。这其实也就是网…...

项目经理【人】任务

系列文章目录 【引论一】项目管理的意义 【引论二】项目管理的逻辑 【环境】概述 【环境】原则 【环境】任务 【环境】绩效 【人】概述 【人】原则 【人】任务 一、定义团队的基本规则&塔克曼阶梯理论 1.1 定义团队的基本规则 1.2 塔克曼阶梯理论 二、项目经理管理风格 …...

Linux学习(嵌入式硬件知识)

GPU和CPU GPU&#xff08;Graphics Processing Unit&#xff0c;图形处理单元&#xff09;和 CPU&#xff08;Central Processing Unit&#xff0c;中央处理单元&#xff09;是计算机中两种不同的处理器。它们在功能、设计和用途上有所不同。 CPU&#xff08;中央处理单元&…...

英语学习笔记4——Is this your ...?

Is this your …? 词汇 Vocabulary suit /sut/ n. 西装&#xff0c;正装 suit 的配套&#xff1a; shirt n. 衬衫tie n. 领带&#xff0c;领结belt n. 腰带trousers n. 裤子shoes n. 鞋子 school /skuːl/ n. 学校 所有学校 搭配&#xff1a;middle school 初中    hig…...

Hive Bucketed Tables 分桶表

Hive Bucketed Tables 分桶表 1.分桶表概念 2.分桶规则 3.语法 4.分桶表的创建 5.分桶表的好处...

【拆位法 决策包容性 位运算】2871. 将数组分割成最多数目的子数组

本文涉及知识点 拆位法 贪心 位运算 决策包容性 位运算、状态压缩、子集状态压缩汇总 LeetCode2871. 将数组分割成最多数目的子数组 给你一个只包含 非负 整数的数组 nums 。 我们定义满足 l < r 的子数组 nums[l…r] 的分数为 nums[l] AND nums[l 1] AND … AND nums[r…...

Java 线程池 ( Thread Pool )的简单介绍

想象一下&#xff0c;你正指挥着一支超级英雄团队&#xff0c;面对蜂拥而至的敌人&#xff08;任务&#xff09;&#xff0c;不是每次都召唤新英雄&#xff08;创建线程&#xff09;&#xff0c;而是精心调配现有成员&#xff0c;高效应对。这就是Java线程池的魔力&#xff0c;…...

鸿蒙内核源码分析(时间管理篇) | 谁是内核基本时间单位

时间概念太重要了&#xff0c;在鸿蒙内核又是如何管理和使用时间的呢? 时间管理以系统时钟 g_sysClock 为基础&#xff0c;给应用程序提供所有和时间有关的服务。 用户以秒、毫秒为单位计时.操作系统以Tick为单位计时&#xff0c;这个认识很重要. 每秒的tick大小很大程度上决…...

安装numpy遇到的问题

安装numpy的时候提示无法安装如下&#xff1a; (venv) E:\works\AI\venv\Scripts>pip install numpy pandas matplotlib jupyter -i https://pypi.douban.com/simple Looking in indexes: https://pypi.douban.com/simple WARNING: Retrying (Retry(total4, connectNone, r…...

页面嵌套,界面套娃,除了用iframe,还有其他方式吗?

UIOTOS可以了解下&#xff0c;uiotos.net&#xff0c;通过连线来代替脚本逻辑开发&#xff0c;复杂的交互界面&#xff0c;通过页面嵌套轻松解决&#xff0c;是个很新颖的思路&#xff0c;前端零代码&#xff01; 蓝图连线尤其是独创的页面嵌套和属性继承技术&#xff0c;好家…...

上传文件至linux服务器失败

目录 前言异常排查使用df -h命令查看磁盘使用情况使用du -h --max-depth1命令查找占用空间最大的文件夹 原因解决补充&#xff1a;删除文件后&#xff0c;磁盘空间无法得到释放 前言 使用XFTP工具上传文件至CentOS服务器失败 异常 排查 使用df -h命令查看磁盘使用情况 发现磁盘…...

渗透 如何防御ARP欺骗,LLMNR-MDNS-NBNS等协议的作用

一. 如何防御ARP欺骗&#xff1f; 1.使用双向IP/MAC绑定&#xff1b; 2.使用静态ARP缓存表&#xff1b; 3.使用ARP服务器&#xff0c;通过服务器来查找ARP转换表来响应其他机器的广播&#xff1b; 4.使用ARP欺骗防护软件&#xff1b; 5.在网关设备上部署防ARP欺骗攻击功能…...

【C++ 所有STL容器简介】

【C 所有STL容器简介】 1. vector2. list3. deque4. set / multiset5. map / multimap6. unordered_set / unordered_multiset7. unordered_map / unordered_multimap8. stack9. queue10. priority_queue C 标准模板库&#xff08;STL&#xff09;提供了一系列常用的容器&#…...

Django调用SECRET_KEY对数据进行加密

对数据进行加密 在Django中进行加密可以直接调用django配置文件中的SECRET_KEY , 同时还需要导入itsdangerous模块中的TimedJSONWebSignatureSerializer进行加密 1. 实现加密方法 , 生成用户加密链接 # 生成用户加密链接 def generate_verify_email_url(user):# 调研加密方法…...

芸众商城电商专业版400+插件源码+搭建教程

介绍&#xff1a; 芸众商城社交电商系统SAAS平台前端基于vue开发&#xff0c;后端基于研发积分商城系统源码 php&#xff0c;本文安装芸众商城全插件&#xff08;400多个&#xff09;商业版平台源码&#xff0c;可同时支持多端口部署运行&#xff1b;使用宝塔面板一键部署的形…...

【机器学习与实现】线性回归示例——波士顿房价分析

目录 一、创建Pandas对象并查看数据的基本情况二、使用皮尔逊相关系数分析特征之间的相关性三、可视化不同特征与因变量MEDV&#xff08;房价中值&#xff09;间的相关性四、划分训练集和测试集并进行回归分析 一、创建Pandas对象并查看数据的基本情况 boston.csv数据集下载&a…...

Redis核心数据结构——跳表(生成数据到文件和从文件中读取数据、模块合并、)

生成文件和从文件中读取数据。 需求如下&#xff1a; 你的任务是实现 SkipList 类中的数据持久化成员函数和数据加载成员函数。 持久化数据成员函数签名&#xff1a;void dump_file(); 该成员函数负责将存储引擎内的数据持久化到文件中。数据的持久化格式是将每个键值对写入文…...

微信小程序下载文件详解

在微信小程序中&#xff0c;下载文件通常涉及使用 wx.downloadFile API。这个 API 可以将网络资源下载到本地临时文件路径&#xff0c;然后你可以使用 wx.saveFile 将临时文件保存到本地持久存储位置。下面是一个下载文件的详细过程&#xff1a; 使用 wx.downloadFile 下载文件…...

软考--试题六--中介者模式(Mediator)

中介者模式(Meditor) 意图 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互 结构 适用性 1、一组对象以定义良好但是复杂的方式进行通信&#xff0c;产生的相互依赖关…...

华为设备使能Auto-Config功能

Auto-Config is working. Before configuring the device, stop Auto-Config. If you perform configurations when Auto-Config is running, the DHCP, routing, DNS, and VTY configurations will be lost. Do you want to stop Auto-Config? [y/n] 背景信息 此任务的应用场…...

又一个行业被Ai背刺了:Comfyui生成你的专属模特!

工作流获取方式放在文末了 Ai技术的进步&#xff0c;带来了许多之前无法实现的新技术和新成品&#xff0c;这也使得曾经需要漫长的时间和复杂的创作过程才能够完成的工作呗Ai技术轻松代替。 于此同时&#xff0c;不同行业也在这个过程中受到了不同程度的冲击。 今天给大家分…...

Facebook广告运营黑五类怎么投?

哈喽呀&#xff0c;很多小伙伴不知道黑五具体是哪些今天就跟大家来说说&#xff0c;黑五类是指一些擦边的受到限制的产品&#xff0c;指的是药品、医疗器械、丰胸、减肥、增高这五类产品。 黑五类产品可以在哪些平台进行投放&#xff1a; 目前黑五类可以广告投放的跨境电商平台…...

设计模式与软件体系结构课后练习参考答案

目录 软件设计模式第二章 创建型软件设计模式1. 工厂模式2. 生成器模式3. 单例模式 第三章 结构型软件设计模式1. 组合模式2. 适配器模式3. 外观模式4. 桥接模式 第四章 行为型软件设计模式1. 迭代器模式2. 访问者模式3. 中介者模式4. 策略模式5. 状态模式 案例分析工厂模式案例…...

MT3035 逆波兰式

思路&#xff1a; 两个栈str1和sr2&#xff0c;分别存放运算符和结果。 如果是数字&#xff0c;直接放入str2中。 如果是运算符&#xff1a; 1. ( &#xff1a;直接放入 str1 2. /-/*// 看栈顶元素&#xff0c;若当前字符优先级比栈顶大&#xff0c;则压到str1中&#x…...