[C++]20.实现红黑树。

实现红黑树

  • 一.基本概念:
    • 1.红黑树的概念:
    • 2.红黑树的性质:
  • 二.实现红黑树:
    • 1.基本结构:
    • 2.插入节点的多种情况:
      • 1.叔叔存在且为红:
      • 2.叔叔不存在/存在且为黑(单旋+变色)
      • 3.叔叔不存在/存在且为黑(多旋(不是一个单一方向比较长)+变色)
      • 4.总体插入代码:
    • 3.红黑树的验证:
      • 1.验证搜索树:
      • 2.验证性质:
        • 1.根节点是黑色
        • 2. 没有连续的红色节点出现
        • 3.每一条路径的黑色节点个数相同
    • 4.红黑树和AVL树的比较:
      • 1. 随机值判断:
        • 1.100
        • 2.10000
        • 3.1000000
        • 4.10000000
      • 2.有序值判断:
        • 1.100
        • 2.10000
        • 3.1000000
        • 4.10000000
  • 三.map和set的封装:
    • 1.概念:
      • 1.set
      • 2.map
    • 2.基本结构:
      • 1.set
      • 2.map
    • 3.插入
      • 1.set
      • 2.map
      • 3.总结:
    • 4.迭代器实现:
      • 1.set
      • 2.map
      • 3.红黑树部分中实现迭代器的++ -- != *
        • 1.实现operator++
        • 2.实现operator--
    • 5.查找:
      • 1.map
    • 6.operator[]的重载
      • 1.map特有:
      • 2.operator[]重载
      • 3.insert的优化

一.基本概念:

1.红黑树的概念:

红黑树是一种二叉搜索树,但是在每一个存储节点上面增加了一个成员变量用来记录当前节点的颜色,可以是RED 或者 BLACK 。 通过从根节点到任意一个叶子节点的着色限制,红黑树可以确保没有任何一条路径会比最短路径的两倍还要长,因此红黑树是接近平衡的。

2.红黑树的性质:

1.每一个节点不是红色就是黑色。
2.根节点必须是黑色。
3.如果一个节点是红色它的两个孩子节点是黑色---->不存在连续的红节点。
4.一个根节点出去的所有路径,路径的黑色节点个树必须相同。
5.每一个叶子节点都是黑色的,这个叶子是nullptr不是实际已经开辟好的节点。

二.实现红黑树:

1.基本结构:

在这里插入图片描述

2.插入节点的多种情况:

1.叔叔存在且为红:

在这里插入图片描述

2.叔叔不存在/存在且为黑(单旋+变色)

在这里插入图片描述

1.新增节点是父亲的左边,并且父亲是爷爷的左边。
进行右旋+变色处理
2.新增节点是父亲的右边,并且父亲是爷爷的右边。
进行右旋+变色处理

3.叔叔不存在/存在且为黑(多旋(不是一个单一方向比较长)+变色)

在这里插入图片描述

1.新增节点是父亲的右边,并且父亲是爷爷的左边。
进行左旋+右旋+变色处理
2.新增节点是父亲的左边,并且父亲是爷爷的右边。
进行右旋+左旋+变色处理

4.总体插入代码:

template<class T>
class RBTree {typedef RBTreeNode<T> Node;
public://1.插入:bool insert(pair<T, T> x){Node* newnode = new Node(x);//1.开始_root == nullptr:if (_root == nullptr){_root = newnode;_root->c = BLACK;return true;}//2.有节点的情况下进行插入:else{//2_1:向下找插入位置:Node* cur = _root;Node* parent = nullptr;while (cur){if (x.first > cur->_date.first){parent = cur;cur = cur->_right;}else if (x.first < cur->_date.first){parent = cur;cur = cur->_left;}else{return false;}}//2_2:新增节点:if (x.first > parent->_date.first){parent->_right = newnode;newnode->_parent = parent;}else if (x.first < parent->_date.first){parent->_left = newnode;newnode->_parent = parent;}//确定一下新增节点:cur = newnode;//2_3:向上进行调整:while (parent && parent->c == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//1.叔叔存在且为红:if (uncle && uncle->c == RED){//1-1:变色:parent->c = BLACK;uncle->c = BLACK;grandfather->c = RED;//1-2:数据更新cur = grandfather;parent = cur->_parent;}//2.叔叔不存在/存在且为黑:else{//右旋+变色if (cur == parent->_left){right_turn(grandfather);grandfather->c = RED;parent->c = BLACK;}//左旋+右旋+变色else{left_turn(parent);right_turn(grandfather);grandfather->c = RED;cur->c = BLACK;}break;}}else if (parent == grandfather->_right){Node* uncle = grandfather->_left;//1.叔叔存在且为红:if (uncle && uncle->c == RED){//1-1:变色:parent->c = BLACK;uncle->c = BLACK;grandfather->c = RED;//1-2:数据更新cur = grandfather;parent = cur->_parent;}//2.叔叔不存在/存在且为黑:else{else{//左旋+变色if (cur == parent->_right){left_turn(grandfather);grandfather->c = RED;parent->c = BLACK;}//右旋+左旋+变色else{right_turn(parent);left_turn(grandfather);grandfather->c = RED;cur->c = BLACK;}break;}}}}//处理根节点的特殊情况:_root->c = BLACK;return true;}}//左旋:void left_turn(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;//特殊情况的判断:if (subRL != nullptr)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;//当前的parent是子树还是根//1.作为根:if (ppNode == nullptr)_root = subR;//2.作为子树考虑左右else{if (ppNode->_left == parent)ppNode->_left = subR;else if (ppNode->_right == parent)ppNode->_right = subR;subR->_parent = ppNode;}}//右旋:void right_turn(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;//特殊情况的判断:if (subLR != nullptr)subLR->_parent = parent;Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;//当前的parent是子树还是根//1.作为根:if (ppNode == nullptr)_root = subL;//2.作为子树考虑左右else{if (ppNode->_left == parent)ppNode->_left = subL;else if (ppNode->_right == parent)ppNode->_right = subL;subL->_parent = ppNode;}}//中序遍历:void _Inorder(Node* cur){if (cur == nullptr)return;_Inorder(cur->_left);//cout << "date:" << cur->_date << "平衡因子:" << cur->_bf << endl;cout << (cur->_date).first << endl;_Inorder(cur->_right);}void Inorder(){if (_root == nullptr)return;_Inorder(_root);}private:Node* _root=nullptr;
};

3.红黑树的验证:

1.验证搜索树:

1.遍历一个vector进行数据插入
2.打印中序遍历二叉树的结果。
3.观察结果是否有序就可以确定是否是一个搜索树。

void text_1()
{RBTree<int> T;vector<int> num = { 8,6,7,11,5,10,13,12,15 };for (auto e : num){T.insert(make_pair(e,e));}T.Inorder();
}

在这里插入图片描述

2.验证性质:

1.每一个节点不是红色就是黑色。
2.根节点必须是黑色。
3.如果一个节点是红色它的两个孩子节点是黑色---->不存在连续的红节点。
4.一个根节点出去的所有路径,路径的黑色节点个树必须相同。
5.每一个叶子节点都是黑色的,这个叶子是nullptr不是实际已经开辟好的节点。

1.验证红黑树需要注意不要去验证结论,应该验证控制条件的存在。
2.结论:任意一条路径长度都不会大于最短路径的二倍.
控制条件:
1.根节点是黑色.
2.没有连续的红色节点出现。
3.每一条路径的黑色节点个数相同。

在这里插入图片描述

1.根节点是黑色

1.直接去判断根节点的颜色是不是黑色.

2. 没有连续的红色节点出现

1.当前节点的颜色是红节点,父亲是红节点就出现了连续的红节点.
2.出现了连续的红节点就返回false.

3.每一条路径的黑色节点个数相同

1.先去计算出来最左或者最右路径的黑色节点的个数.
2.进行传参在递归过程到叶子节点去判断计算路径和递归路径的黑色节点的个数是否相同.

bool _IsRBTree(Node* cur , int count , int& refnumber){if (cur == nullptr){//记录数据的判断:if (count == refnumber)return true;cout << "当前黑色节点的路径长度不匹配" << endl;return false;}//判断没有连续的红色节点出现:如果当前子节点为红,父节点就不可以是红。if (cur->c == RED && cur->_parent->c == RED){cout<< "存在连续的红色节点出现问题" << endl;return false;}//记录长度:if (cur->c == BLACK)count++;return _IsRBTree(cur->_left,count,refnumber);return _IsRBTree(cur->_right, count, refnumber);}bool IsRBTree(){//1.根节点不是黑色判断:if (_root->c == RED){cout << "根节点不是黑色" << endl;return false;}//2.计算最左或者最右路径的黑节点个数:Node* cur = _root;while (cur){if (cur->c == BLACK)black++;cur = cur->_left;}int count = 0;return _IsRBTree(_root,count,black);}

4.红黑树和AVL树的比较:

1.插入相同的数据比较红黑树和AVL树.
2.比较插入数据的总时间
3.比较插入数据的高度
4.比较插入数据的旋转次数

1. 随机值判断:


void text_4()
{AVL_Tree<int> T1;RBTree<int> T2;vector<int> num;//生成随机值:int count = 100;srand(time(nullptr));for (int i = 0; i < count; i++){num.push_back(i+rand());}int b1 = clock();for (auto e : num){//cout << "e:" << e << endl;T1.Insert(make_pair(e, e));}int e1 = clock();for (auto e : num){//cout << "e:" << e << endl;T2.insert(make_pair(e, e));}int e2 = clock();//插入时间:cout << "AVLinserttime :" << e1 - b1 << endl;cout << "RBinserttime :" << e2 - e1 << endl;//高度:cout << "AVLhight: " << T1.hight() << endl;cout << "RBhight: " << T2.hight() << endl;//旋转次数:cout << "AVLspin:" << T1.spin() << endl;cout << "RBspin:" << T2.spin() << endl;}
1.100

在这里插入图片描述

2.10000

在这里插入图片描述

3.1000000

在这里插入图片描述

4.10000000

在这里插入图片描述

2.有序值判断:

void text_4()
{AVL_Tree<int> T1;RBTree<int> T2;vector<int> num;//生成随机值:int count = 10000000;//srand(time(nullptr));for (int i = 0; i < count; i++){num.push_back(i);}int b1 = clock();for (auto e : num){//cout << "e:" << e << endl;T1.Insert(make_pair(e, e));}int e1 = clock();for (auto e : num){//cout << "e:" << e << endl;T2.insert(make_pair(e, e));}int e2 = clock();//插入时间:cout << "AVLinserttime :" << e1 - b1 << endl;cout << "RBinserttime :" << e2 - e1 << endl;//高度:cout << "AVLhight: " << T1.hight() << endl;cout << "RBhight: " << T2.hight() << endl;//旋转次数:cout << "AVLspin:" << T1.spin() << endl;cout << "RBspin:" << T2.spin() << endl;}
1.100

在这里插入图片描述

2.10000

在这里插入图片描述

3.1000000

在这里插入图片描述

4.10000000

在这里插入图片描述

三.map和set的封装:

1.概念:

1.set

1.set底层使用红黑树进行数据的存储。
2.进一步的封装是因为BRTree的结构并不适合关联式容器set。
3.set同时需要实现多种容器的方法所以进行进一步的封装BRTree是非常有必要的。
4.进一步的封装有利于控制模板参数类型。

2.map

1.map底层使用红黑树进行数据的存储。
2.进一步的封装是因为BRTree的结构并不适合关联式容器map。
3.map同时需要实现多种容器的方法所以进行进一步的封装BRTree是非常有必要的。
4.进一步的封装有利于控制模板参数类型。

2.基本结构:

1.set

#include"RBTree.h"namespace sfpy {template<class T>class myset {public:private:RBTree<T,T> _t;};
}

2.map

#include"RBTree.h"namespace sfpy {template<class T , class V >class mymap {public:private:RBTree<T, pair<T, V>> _t;};
}

在这里插入图片描述

3.插入

1.set

在这里插入图片描述

如何统一数据大小的比较?
1.在set中实现一个仿函数并且重载operator()提取可以进行比较的数值。
2.直接实现一个内部类并且重载operator()方法并且考虑返回的类型值。

在这里插入图片描述

2.map

在这里插入图片描述

如何统一数据大小的比较?
1.在set中实现一个仿函数并且重载operator()提取可以进行比较的数值。
2.map中的问题:pair这个数据类型进行大小的比较是first大就大,在first相同的情况下,second大就大,类型本身进行大小的比较是不符合我们当前的要求的,进行仿函数重载提取数据直接进行比较。
3.直接实现一个内部类并且重载operator()方法并且考虑返回的类型值。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.总结:

1.分别在myset和mymap中去实现两个类并且重载operator()进行比较数据值的提取。
2.在operator()定义的时候要考虑返回值的类型是否是可以进行比较的数值。
3.优点:通过一个模板参数可以实例化两份不同的树,不需要自己手写。

4.迭代器实现:

1.迭代器本质就是对指针的二次封装,并且重载适合的方法,为什么需要进行二次封装?
2.因为:本身的结构并不可以满足++ – * 等需求!
2.对于链表,顺序表结构,指针和迭代器进行++ – * 操作效果相同不需要非常复杂的进行封装。
3.对于set和map这样的结构底层是通过红黑树进行实现的,就红黑树来说没有好的方法提供给我们去实现++ – * 等操作。

1.set

namespace sfpy {template<class T>class myset {public:struct bitter {const T& operator()(const T& x){return x;}};//1.插入:bool _insert(T x){return _t.insert(x);}//2.迭代器:typedef typename RBTree<T, T, bitter>::_iterator iterator;iterator begin(){return _t.look_left();}iterator end(){return nullptr;}private:RBTree<T,T, bitter> _t;};
}

2.map

namespace sfpy {template<class T , class V >class mymap {public:struct bitter {const T operator()(const pair<T,V>& x){return x.first;}};//1.插入:bool _insert(pair<T, V> x){return _t.insert(x);}//2.迭代器:typedef  typename RBTree<T, pair<T, V>, bitter>::_iterator iterator;iterator begin(){return _t.look_left();}iterator end(){return nullptr;}private:RBTree<T, pair<T, V>, bitter> _t;};
}

3.红黑树部分中实现迭代器的++ – != *

1.实现operator++

实现operator++
1.中序遍历:左子树 根节点 右子树
2.假设:当前节点是8说明左树已经遍历完了
–>判断当前右子树为不为空
---->右子树不是空找右子树的最左节点。
3.假设:当前节点是11,11(cur)是父亲(parent)的右
可以正常回去—>进行cur和parent的更新向上找下一个节点
—>直到parent->left == cur 说明当前的parent节点还没有遍历到。

在这里插入图片描述

2.实现operator–

实现operator–
1.++ 找右数的最左节点 ,–找左数的最右节点。
2.其他内容保持和++相反。

template<class T , class ret , class ptr>
struct RBT_Iterator {typedef RBTreeNode<T> Node;//返回一个迭代器类型typedef RBT_Iterator<T,ret,ptr> self;Node* node;RBT_Iterator(Node* x = nullptr):node(x){}self& operator++(){//左 中 右if (node->_right){Node* cur = node->_right;//Node* cur = node;//找当前节点的最左节点:while (cur->_left){cur = cur->_left;}node = cur;}//cur->right 没有的!else{Node* cur = node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}node = parent;}return *this;}self& operator--(){//右 中 左if (node->_left){Node* cur = node->_left;//找当前节点的最右节点:while (cur->_right){cur = cur->_right;}node = cur;}//cur->right 没有的!else{Node* cur = node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}node = parent;}return *this;}ret operator*(){return node->_date;}ptr operator->(){return &(node->_date);}bool operator!=(const self& x){return node != x.node;}
};template<class K , class V , class Bitter>
class RBTree {typedef RBTreeNode<V> Node;
public:typedef RBT_Iterator<V, const V& ,const V*> _iterator;//1.插入:

5.查找:

1.我们的set和map在封装红黑树的时候对当前封装的红黑树的类模板进行重新的规划。
2.对于set :RBTree<T,T, bitter> _t; ,set不需要实现find set存数据所决定。
3. 对于map ==RBTree<T, pair<T, V>, bitter> _t;==主要是为了方便实现find的查找功能。

1.map

//3.查找:V find(T x){pair<T, V> ret = _t._find(make_pair(x, V()));return ret.second;}//找数据:V _find(V x){Bitter kot;Node* cur = _root;Node* parent = nullptr;while (cur){if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->_right;}else if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->_left;}else{return cur->_date;}}return V();}

6.operator[]的重载

1.map特有:

1.operator[]是通过修改insert返回值类型为pair<iterator,bool>类型进行的一个实现:
2.返回值ret接受到返回值,访问迭代器中并且因为迭代器重载了operator->拿到对应的数据。
3.注意:新增节点和查询节点的一个区别,通过新增一个pair类型的数据去进行查询,查询到就就返回查询到的节点的迭代器,查询不到就返回新增。

2.operator[]重载

//4.重载operator[]--->insert进行重写V& operator[](T x){pair<iterator,bool> ret =  _t.insert(make_pair(x,V()));return ret.first->second;}

3.insert的优化

//1.插入:pair<_iterator,bool> insert(V x){Node* newnode = new Node(x);Bitter kot;//1.开始_root == nullptr:if (_root == nullptr){_root = newnode;_root->c = BLACK;return make_pair(_iterator(newnode), true);}//2.有节点的情况下进行插入:else{//2_1:向下找插入位置:Node* cur = _root;Node* parent = nullptr;while (cur){if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->_right;}else if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->_left;}else{return make_pair(_iterator(cur), false);}}//2_2:新增节点:if (kot(x) > kot(parent->_date)){parent->_right = newnode;newnode->_parent = parent;}else if (kot(x) < kot(parent->_date)){parent->_left = newnode;newnode->_parent = parent;}//确定一下新增节点:cur = newnode;//2_3:向上进行调整:while (parent && parent->c == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//1.叔叔存在且为红:if (uncle && uncle->c == RED){//1-1:变色:parent->c = BLACK;uncle->c = BLACK;grandfather->c = RED;//1-2:数据更新cur = grandfather;parent = cur->_parent;}//2.叔叔不存在/存在且为黑:else{//右旋+变色if (cur == parent->_left){right_turn(grandfather);grandfather->c = RED;parent->c = BLACK;}//左旋+右旋+变色else{left_turn(parent);right_turn(grandfather);grandfather->c = RED;cur->c = BLACK;}break;}}else if (parent == grandfather->_right){Node* uncle = grandfather->_left;//1.叔叔存在且为红:if (uncle && uncle->c == RED){//1-1:变色:parent->c = BLACK;uncle->c = BLACK;grandfather->c = RED;//1-2:数据更新cur = grandfather;parent = cur->_parent;}//2.叔叔不存在/存在且为黑:else{//左旋+变色if (cur == parent->_right){left_turn(grandfather);grandfather->c = RED;parent->c = BLACK;}//右旋+左旋+变色else{right_turn(parent);left_turn(grandfather);grandfather->c = RED;cur->c = BLACK;}break;}}}//处理根节点的特殊情况:_root->c = BLACK;return make_pair(_iterator(newnode), true);}}

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

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

相关文章

C/C++中{}的用法总结(全)

C基础专栏&#xff1a;http://t.csdnimg.cn/UjhPR 目录 1.定义初始化列表&#xff08;Initializer List&#xff09; 2.类成员初始化列表 3.无默认构造函数的类的默认初始化&#xff08;C11 及以后版本&#xff09; 4.初始化器列表构造函数&#xff08;C11 及以后版本&…

Docker学习之镜像管理(超详解析)

Docker镜像生命周期&#xff08;可以把docker镜像理解为虚拟机镜像&#xff09; 实验内容&#xff1a; 搜索官方仓库镜像 [rootlocalhost ~]# docker search busybox //以查找busybox为例 搜索说明&#xff1a;name镜像名称 description镜像说明 stars点赞数量 official…

四川宏博蓬达法律咨询有限公司:法律服务的行业翘楚

在当今社会&#xff0c;法律服务已经成为人们生活中不可或缺的一部分。随着法律意识的提高&#xff0c;选择一家专业、可靠的法律咨询公司显得尤为重要。四川宏博蓬达法律咨询有限公司&#xff0c;作为业内的佼佼者&#xff0c;以其卓越的服务质量和广泛的业务范围&#xff0c;…

Nacos 集群搭建

1 . 集群结构图 : 其中包括3个nacos结点&#xff0c;然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx ; 我们计划的集群结构 : 三个nacos结点的地址 : 节点ipportnacos1192.168.150.18845nacos2192.168.150.18846nacos3192.168.150.18847 2 . 搭建集群 搭…

2024最新轻量应用服务器简介_轻量应用服务器购买指南

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且价格特别便宜&#xff0c;大带宽&#xff0c;但是限制月流量&#xff0c;轻量2核2G3M带宽61元一年、2核2G4M优惠价99元一年&#xff0c;540元三年、2核4G5M带宽165元一年&#xff0c;…

JAVA初阶数据结构(链表)练习(这些可以作为java包中的方法)

这里的每一个题大家都要仔细完成&#xff0c;这些题目每个我都至少思考了两个小时左右&#xff08;沉重心&#xff0c;慢慢来&#xff09; 1.反向链表的实现&#xff08;对链表进行翻转&#xff09;&#xff08;力扣有&#xff09; &#xff08;1&#xff09;图示 &#xff0…

2024考研国家线公布,各科分数线有哪些变化?考研国家线哪些涨了,哪些跌了?可视化分析告诉你

结论在文章结尾 2024考研国家线 一、近五年国家线趋势图-学术硕士 文学 管理学 工学照顾专业 体育学 交叉学科 军事学 历史学 理学 享受少数名族照顾政策的考生 中医类照顾专业 教育类 艺术类 医学 工学 哲学 法学 农学 经济学 二、近五年国家线趋势图-专业硕士 中医 应用心理 …

web项目的搭建

使用Webstorm并创建Next.js文件 1、配置nodejs环境、安装webstorm【配置node.js可以使用nvm去管理nodejs的版本】 2、需要破解webstorm&#xff0c;可能会导致原本的idea失效&#xff0c;注册码过期 3、taobao的npm过期&#xff0c;导致npm is sass执行不成功&#xff0c;需…

jvaweb 3-13

Element 后端开发 maven maven的作用 下载并配置环境变量 创建Maven项目 Maven坐标 依赖 Maven的依赖传递特性 以图表形式展示依赖 排除依赖 依赖范围 生命周期

普通小白现在做抖音小店还能赚到钱吗?千万不要忽视这几点

大家好&#xff0c;我是电商花花。 赚钱做生意的秘密&#xff0c; 很简单&#xff0c;就是积少成多&#xff0c;以小见大&#xff0c;然后和时间成为朋友。 而做抖音小店也是这样的一个道理&#xff0c;不管是新手小白还是老电商玩家&#xff0c;都是从一点一滴做起来的&…

如何使用vue定义组件之——父组件调用子组件

首先&#xff0c;我们需要创建两个组件模板template&#xff1a; <template id"father"><div><h3>我是父组件</h3><h3>访问自己的数据:</h3><h3>{{ msg }}</h3></div></template><template id"…

性能测试-数据库

一、数据库事务机制 ACID描述 1、原子性Atomicity&#xff1a;事务通常由多个语句组成。原子性保证将每个事务视为一个“单元”&#xff0c;该事务要么完全成功&#xff0c;要么完全失败 2、一致性Consistency&#xff1a;“一致”是指数据库中的数据是正确的&#xff0c;不存…

【2024-03-12】设计模式之模板模式的理解

实际应用场景&#xff1a;制作月饼 过程描述&#xff1a; 一开始&#xff0c;由人工制作月饼&#xff0c; 第一个&#xff1a;根据脑子里面月饼的形状&#xff0c;先涅出月饼的形状&#xff0c;然后放入面粉和馅料把开口合并起来。 第二个&#xff1a;根据脑子里面月饼的形状&…

BigDL-LLM 安装指南——在iGPU集成显卡下使用BigDL-LLM大模型库加速LLM

文章目录 iGPU是什么&#xff1f;一、环境准备1.1 Visual Studio 2022 Community 安装1.2 安装或更新最新版本的GPU驱动程序1.3 安装英特尔oneAPI工具包2024.0版本1.4 安装Anaconda 二、BigDL -LLM 安装2.1 创建虚拟环境2.2 激活虚拟环境2.3 安装bigdl-llm[xpu] 三、运行环境配…

iOS 17.4 Not Installed

iOS15以后&#xff0c;下载了xcode安装好后&#xff0c;并不会自动下载好模拟器&#xff0c;需要手动下载。 有两种下载方式 xcode下载 xcode -> Settings 打开面板 xcode下载虽然方便&#xff0c;但是有个问题是&#xff0c;这里下载如果断网了不会断点续传&#xff0c;…

MyBatis 框架之一:简介及环境搭建详细步骤

1. MyBatis 是什么&#xff1f; MyBatis 是一个流行的、轻量级的 Java 持久层框架&#xff0c;它简化了与数据库交互和 SQL 映射的过程。MyBatis 取代了 JDBC 原始 API 的大量繁琐工作&#xff0c;允许开发者将更多的精力放在业务逻辑上而不是处理数据访问细节。 mybatis 是一款…

激光雷达研究笔记1:资料整理与学习_windows机对其评估使用

到手了一款型号为LDROBOT LiDAR LD19 的单线激光雷达模块&#xff0c;现对其进行学习使用&#xff1a; 1.对产品手册的研究&#xff1a; 通读产品手册后&#xff0c;我提炼了几方面有用的信息&#xff1a; 1.测量依据&#xff1a; 以下是激光雷达点云扫描示意图&#xff1a; 上…

Linux信号机制(二)

目录 一、信号的阻塞 二、信号集操作函数 三、sigprocmask函数 四、pause函数 五、sigsuspend函数 一、信号的阻塞 有时候不希望在接到信号时就立即停止当前执行&#xff0c;去处理信号&#xff0c;同时也不希望忽略该信号&#xff0c;而是延时一段时间去调用信号处理函数。…

【Sql Server】通过Sql语句批量处理数据,使用变量且遍历数据进行逻辑处理

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

Qt配置OpenCV,无需编译

网上很多关于Qt配置OpenCV的教程都需要下载CMake编译Opencv&#xff0c;但是这种做法出错的概率很大&#xff0c;很多时候会受CMake或者Opencv版本的影响&#xff0c;又或者是没有使用Qt自带的Cmake-gui.exe程序&#xff0c;我在编译的时候也是频繁出错。 本文推荐的方法是使用…