C++进阶: 红黑树及map与set封装

红黑树总结整理

红黑色概述:

红黑树整理与AVL树类似,但在对树的平衡做控制时,AVL树会比红黑树更严格。

AVL树是通过引入平衡因子的概念进行对树高度控制。

红黑树则是对每个节点标记颜色,对颜色进行控制。

红黑树控制规则:

1.每个节点不是红色就是黑色

2.根节点必须是黑色

3.如果一个节点是红色,那么此节点的左右孩子节点必须是黑色。也就是说在一条路径上,没有连续的红色节点。

4.对任意一个节点,从该节点到其所有的NULL节点上路径的黑色节点数量均相等。

这样组合的特性是:树的最长路径不会超过最短路径的两倍。

最长路径<=2*最短路径

红黑树保证效率的方式就是遵守红黑树的4条规则。

已知红黑树从根节点到任意路径上的NULL节点中都拥有相同数量的黑色节点。

在极端情况下,一条路径全色黑色节点,我们称之为最短路径用bh表示。

又根据规则3,一个红节点它的左右两个孩子就必须是黑色节点,可以反推,一个红色节点的父亲节点是一个黑色节点。所以最长路径的组成就变成了间隔相邻的红黑节点,共有2bh个。

所以结论就为:

        红黑树的高度差控制在bh<=h<=2bh

红黑树的效率:

红黑树的效率与AVL树相差无几,假设 N为红黑树的节点数量,h为最短路径。

        所以求h就等于log以2为底的N。效率也就是logN级别

红黑树的插入:

红黑树的插入必须遵守四个规则。在插入空树时,根节点为黑色。而在插入非空树时,新增节点的初始颜色必须为红色。这是因为若初始颜色为黑色,会导致某一路径的黑色节点数量多于其他路径,违反规则4,并且很难维护。因此,插入时新节点的颜色应始终为红色。

当我们新插入节点为红色时,我们检查父节点,如果父节点为黑色,那么直接完成插入,如果父节点为红色,就违反了规则3,需要进行特殊处理。

先将新插入的节点标记为 c(cur),那么它的父亲为 p(parent) ,它的爷爷为g(grandfather)它的爷爷绝对是黑色节点,如果不为黑色,那么这颗红黑树早就违反了规则3.

并且还需要g节点找到并标记u(uncle)节点,也就是和p同级的兄弟节点。

情况一:变色

情况二:单旋+变色

c,p节点都为红色,那么u节点除了红色就只有存在且为黑色或不存在两种情况。

如果u节点不存在,那么c是新增节点

如果u存在且为黑色,那么c节点绝对不是叶子节点,c节点下还有子树。只不过是c节点的子树变化导致c变成了红色。

那么如何验证这个结论呢?

观察上图,如果u有节点且为黑色的话,此时就以及违反了规则4,所有路径的黑色节点数量不对等。

当u存在且为黑色的时候,C必然不是新增节点,它只可能是a或b子树里进行变换后当值c变成了红色。所以当我们在进行颜色变化的时候,需要循环向上变化。只有当p节点为黑色才停止。

u不存在或u存在且为黑色时候,我们单纯变色是不行的

会发现,当我们仅仅只是变色的话,就会违反规则4,根到左路径会比根到右路径的黑色节点个数多一。

当我们u为空时候,就需要进行一次右旋,让子树达到平衡后更新p节点为黑色,g节点为红色就能确保4条规则都不触犯,并且因为p已经变成了黑色,就不需要再往上跟新节点。

当u不为空时,c子树必然有黑色节点。同样的我们进行右旋后,将p变为黑色,g变成红色后还是遵守着4条规则。

这里也仅仅介绍右旋的情况,如果是在左边的话就同理进行左旋加变色,

情况三:双选+变色

c节点也不会一直如我们所愿,只插入在绝对的左边或绝对的右边,这时候就需要经过两次旋转,才能完成红黑树的变化。

当c在p的右边的时候就必须要通过双选,当c转完两次的时候,c来到最上层,此时将c节点变为黑色,将g节点变为红色,c又恰好为上层节点,就可以直接退出循环。

当u不为空且为黑色时

同样的,u为黑色那么c节点肯定不是新插入的节点,c原本为黑色节点。因为新插入的节点在c的子树内,导致子树的变化影响了C节点从黑色变为红色才发生的双选+变色。

        最后戳了c在左子树,c的情况也会在右子树出现,所实现的方法也是也要的,并不需要特别说明举例。

        以下是红黑树的代码

#pragma once
#include<iostream>
using namespace std;
enum Color { red ,black };template<class K, class V>
struct TreeNode
{pair<K, V> _val;TreeNode* _left;TreeNode* _right;TreeNode* _parent;Color _col;TreeNode(const pair<K, V>& val):_val(val),_left(nullptr),_right(nullptr),_parent(nullptr),_col(red){}
};template<class K,class V>
class RBTree
{typedef TreeNode<K, V> Node;
public:bool Insert(const pair<K,V> &val){if(_root==nullptr){_root = new Node(val);_root->_col = black;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if(cur->_val.first>val.first){cur = cur->_left;}else if (cur->_val.first < val.first){cur = cur->_right;}else{return false;}}cur = new Node(val);if (parent->_val.first>cur->_val.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//判断红黑树是否要更新while (parent&&parent->_col!=black){Node* grandfather = parent->_parent;Node* uncle;if (grandfather->_left==parent){uncle = grandfather->_right;if (uncle&&uncle->_col==red)//情况一:只变色{grandfather->_col = red;uncle->_col = parent->_col = black;cur = grandfather;parent = cur->_parent;}else{if (parent->_left==cur)//情况二:单旋+变色{TurnR(grandfather);grandfather->_col = red;parent->_col = black;break;}else//情况三:双循+变色{TurnL(parent);TurnR(grandfather);cur->_col = black;grandfather->_col = red;break;}}}else{uncle = grandfather->_left;if (uncle && uncle->_col == red)//情况一:只变色{grandfather->_col = red;uncle->_col = parent->_col = black;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur)//情况二:单旋+变色{TurnL(grandfather);grandfather->_col = red;parent->_col = black;break;}else//情况三:双循+变色{TurnR(parent);TurnL(grandfather);cur->_col = black;grandfather->_col = red;break;}}}}_root->_col = black;}bool Check(Node*_root,int ibnum,const int rnum){if (_root==nullptr){if (ibnum != rnum) {cout << "存在黑色结点的数量不相等的路径" << endl;return false;}return true;}if (_root->_col==red&&_root->_parent->_col==red){cout << _root->_val.first<< "存在连续的红色结点" << endl;return false;}if (_root->_col==black){++ibnum;}return Check(_root->_left, ibnum, rnum) && Check(_root->_right, ibnum, rnum);}bool IsBalance(){if (_root==nullptr||_root->_col==red){return false;}int rnum = 0;Node* cur = _root;while (cur){if (cur->_col == black)rnum++;cur = cur->_left;}return Check(_root, 0, rnum);}// blackNum 根到当前结点的黑色结点的数量//bool Check(Node* root, int blackNum, const int refNum)//{//	if (root == nullptr)//	{//		// 前序遍历走到空时,意味着一条路径走完了//		//cout << blackNum << endl;//		if (refNum != blackNum)//		{//			cout << "存在黑色结点的数量不相等的路径" << endl;//			return false;//		}//		return true;//	}//	// 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了//	if (root->_col == red && root->_parent->_col == red)//	{//		cout << root->_val.first << "存在连续的红色结点" << endl;//		return false;//	}//	if (root->_col == black)//	{//		blackNum++;//	}//	return Check(root->_left, blackNum, refNum)//		&& Check(root->_right, blackNum, refNum);//}//bool IsBalance()//{//	if (_root == nullptr)//		return true;//	if (_root->_col == red)//		return false;//	// 参考值//	int refNum = 0;//	Node* cur = _root;//	while (cur)//	{//		if (cur->_col == black)//		{//			++refNum;//		}//		cur = cur->_left;//	}//	return Check(_root, 0, refNum);//}void InOrder(){_InOrder(_root);}protected:void _InOrder(Node*cur){if (cur==nullptr){return;}_InOrder(cur->_left);cout << "first:" << cur->_val.first << " second:" << cur->_val.second << endl;_InOrder(cur->_right);}void TurnR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* Pparent = parent->_parent;parent->_parent = subL;if (Pparent){if (Pparent->_left == parent){Pparent->_left = subL;}else{Pparent->_right = subL;}}subL->_parent = parent;}void TurnL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL) { subRL->_parent = parent; }Node* Pparent = parent->_parent;parent->_parent = subR;if (Pparent){if (Pparent->_left == parent){Pparent->_left = subR;}else{Pparent->_right = subR;}}subR->_parent = parent;}private:Node* _root=nullptr;
};

封装map与set:

        在map与set中都是对底层的红黑树进行上层的封装。

        封装后的红黑树:

#pragma once
#include<iostream>
using namespace std;enum color
{red,black
};template<class T>
struct TreeNode
{TreeNode(const T&ky):_ky(ky),_col(red),_parent(nullptr),_left(nullptr),_right(nullptr){}T _ky;color _col;TreeNode* _parent;TreeNode* _left;TreeNode* _right;
};template<class T, class Ref,class Ptr >
struct Tree_Iterator
{typedef TreeNode<T> Node;typedef Tree_Iterator<T,Ref,Ptr> Self;Tree_Iterator(Node* node, Node* root):_node(node), _root(root){}Ref operator *(){return _node->_ky;}Ptr operator ->(){return &_node->_ky;}Self&operator++(){if (_node->_right){Node* minleft = _node->_right;while (minleft->_left){minleft = minleft->_left;}_node = minleft;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent&&parent->_right==cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self &operator --(){if (_node==nullptr){Node* cur = _root;while (cur&&cur->_right){cur = cur->_right;}_node = cur;}else if (_node->_left){Node* maxright = _node->_left;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator !=(const Self&kv ){return _node != kv._node;}bool operator ==(const Self& kv){return _node == kv._node;}Node* _node;Node* _root;};template<class K,class T,class KeyOFT>
class RBTree
{typedef TreeNode<T> Node;
public:typedef Tree_Iterator<T,T&,T*> Iterator;typedef Tree_Iterator<T, const T&, const T*> Const_Iterator;Iterator Begin(){Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}Const_Iterator CBegin()const {Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Const_Iterator CEnd()const {return Iterator(nullptr, _root);}pair<Iterator,bool> Insert(const T&ky){if(!_root){_root = new Node(ky);_root->_col = black;return make_pair(Iterator(_root,_root),true);}Node* cur = _root;Node* parent = nullptr;KeyOFT kot;while (cur){if (kot(cur->_ky) > kot(ky)){parent = cur;cur = cur->_left;}else if (kot(cur->_ky) < kot(ky)){parent = cur;cur = cur->_right;}else{return make_pair(Iterator(cur, _root), false);;}}cur = new Node(ky);Node* node = cur;if (kot(parent->_ky)>kot(cur->_ky)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&&parent->_col==red){Node* garendparent = parent->_parent;Node* uncle;if (garendparent->_left==parent)//只变色{uncle = garendparent->_right;if (uncle&&uncle->_col==red){uncle->_col = parent->_col = black;garendparent->_col = red;cur = garendparent;parent = cur->_parent;}else {if (parent->_left==cur)//单选变色{TurnR(garendparent);parent->_col = black;cur->_col = garendparent->_col = red;break;}else//双旋变色{TurnL(parent);TurnR(garendparent);cur->_col = black;parent->_col = garendparent->_col = red;break;}}}else{uncle = garendparent->_left;if (uncle && uncle->_col == red){uncle->_col = parent->_col = black;garendparent->_col = red;cur = garendparent;parent = cur->_parent;}else{if (parent->_right == cur)//单选变色{TurnL(garendparent);parent->_col = black;cur->_col = garendparent->_col = red;break;}else//双旋变色{TurnR(parent);TurnL(garendparent);cur->_col = black;parent->_col = garendparent->_col = red;break;}}}}_root->_col = black;return make_pair(Iterator(node, _root), true);}Node*Find(const K &val){Node* cur = _root;KeyOFT kot;while (cur){if (kot(cur->_ky) > val){cur = cur->_left;}else if (kot(cur->_ky) < val){cur = cur->_right;}else{return cur;}}return nullptr;}private:void TurnR(Node*parent){KeyOFT kot;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if(subLR){subLR->_parent = parent;}subL->_right = parent;Node* Pparent = parent->_parent;parent->_parent = subL;if (!Pparent){subL->_parent = nullptr;_root = subL;}else{if (kot(Pparent->_ky)> kot(subL->_ky)){Pparent->_left = subL;}else{Pparent->_right = subL;}subL->_parent = Pparent;}}void TurnL(Node*parent){KeyOFT kot;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;Node* Pparent = parent->_parent;parent->_parent = subR;if (!Pparent){subR->_parent = nullptr;_root = subR;}else{if (kot(Pparent->_ky) > kot(subR->_ky)){Pparent->_left = subR;}else{Pparent->_right = subR;}subR->_parent = Pparent;}}Node* _root = nullptr;
};

        对于map与set在底层用的都是同一颗红黑树,只是通过类模板进行了实例化两份,所以在红黑树的自身的封装中需要改动以下措施:

        1.树节点存储的值只用一个T类,因为编译器不知道要存的是Key模型,还是pair模型。

        2.为红黑树添加迭代器,map与set都支持迭代器访问数据。需要重点注意的是迭代器的++与迭代器的--,这也是封装后的红黑树的重点

                

Self& operator++()(前置递增)

  1. 当前节点有右子树

    • 找到右子树中的最左节点(即右子树中的最小节点),该节点即为当前节点的后继。

    • 例如,若当前节点为 A,右子树为 C,则后继是 C 的最左子节点(假设为 D

    if (_node->_right) {Node* minleft = _node->_right;while (minleft->_left) {minleft = minleft->_left;}_node = minleft;
    }
  2. 当前节点无右子树

    • 从当前节点向上回溯,找到第一个祖先节点,使得当前节点位于该祖先节点的左子树中。

    • 例如,若当前节点为 E,其父节点为 B,祖父节点为 A,则 A 是 E 的后继。

    else {Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = parent->_parent;}_node = parent; // 找到的祖先节点即为后继

Self& operator--()(前置递减)

  1. 当前节点为 nullptr(如 end() 迭代器)

    • 找到树中的最右节点(即最大值节点),作为前驱。

    • 例如,若树为 A(B(D, E), C),则最右节点为 C

    if (_node == nullptr) {Node* cur = _root;while (cur && cur->_right) {cur = cur->_right;}_node = cur; // 指向最大值节点
    }
  2. 当前节点有左子树

    • 找到左子树中的最右节点(即左子树中的最大节点),该节点即为当前节点的前驱。

    • 例如,若当前节点为 A,左子树为 B,则前驱是 B 的最右子节点 E

    else if (_node->_left) {Node* maxright = _node->_left;while (maxright->_right) {maxright = maxright->_right;}_node = maxright;
    }

        还需要注意对于为什么有KeyOFT,是因为我们红黑树在插入时需要进行比较大小,而map插入的是一个pair,而set插入的就只是Key。红黑树本身是并不知道该拿什么进行比较。所以这里传入一个KeyOFT,是上层map或set传入的一个函数模板,map就通过类模板过滤进行pair的first进行比较,set通过自身类模板比较的还是Key。

        封装map:

                map的封装也并没有太大难度,上述写道的类模板比较需要自己写,并且在定义成员变量红黑树的时,第二个类模板应传入的是pair,而不是V。并且map是支持运算符重载[ ],所以我们这里也同样支持,其余的也只是对红黑树的再一次调用。

#pragma once
#include"RBTree.h"namespace cat
{template<class K,class V >class mymap{struct MapOFT{const K &operator ()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapOFT>::Iterator iterator;typedef typename RBTree<K, pair<const K,const V>, MapOFT>::Const_Iterator const_iterator;pair<iterator, bool> insert(const pair<K,V> &kv){return _rbt.Insert(kv);}iterator begin(){return _rbt.Begin();}iterator end(){return _rbt.End();}const_iterator cbegin() const {return _rbt.CBegin();}const_iterator cend()const {return _rbt.CEnd();}V& operator[](const K&ky){pair<iterator, bool> ret = _rbt.Insert({ ky,V() });return ret.first->second;}private:RBTree< K, pair<const K,V>, MapOFT> _rbt;};void test_map(){mymap<int, int> m;m.insert({ 1,1 });m.insert({ 5,5 });m.insert({ 2,2 });m.insert({ 6,6 });mymap<int, int>::iterator it = m.begin();while (it != m.end()){//it->first += 1;it->second += 1;cout << it->first << ":" << it->second << endl;++it;}mymap<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];for (auto& kv : dict){cout << kv.first << " " << kv.second << endl;}cout << endl;string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };mymap<string, int> countMap;for (const auto& str : arr){countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}cout << endl;}}

        封装Set

#pragma once
#include"RBTree.h"namespace cat
{template<class K>class myset{struct SetOFT{const K& operator()(const K& ky){return ky;}};public:typedef typename RBTree<K, K, SetOFT>::Iterator iterator;typedef typename RBTree<K, K, SetOFT>::Const_Iterator const_iterator;pair<iterator,bool> insert(const K& ky){return _rbt.Insert(ky);}iterator begin(){return _rbt.Begin();}iterator end(){return _rbt.End();}const_iterator begin() const {return _rbt.CBegin();}const_iterator end() const{return _rbt.CEnd();}private:RBTree<K, K, SetOFT> _rbt;};void test_set(){myset<int> s;/*s.insert(4);s.insert(1);s.insert(5);s.insert(3);*/int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){if(e==14){int x = 1;}s.insert(e);}myset<int>::iterator it = s.begin();// *it += 10;while (it != s.end()){cout << *it << " ";++it;}cout << endl;it = s.end();while (it != s.begin()){--it;cout << *it << " ";}cout << endl;}
}

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

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

相关文章

列表标签(无序列表、有序列表)

无序列表 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><…

Kanass基础教程-创建项目

Kanass是一款国产开源免费的项目管理工具&#xff0c;工具简洁易用&#xff0c;开源免费&#xff0c;之前介绍过kanass的一些产品简介及安装配置方法&#xff0c;本文就从如何创建第一个项目来开始kanass上手之旅吧。 1. 创建项目 点击项目->项目添加 按钮进入项目添加页面…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.10 ndarray内存模型:从指针到缓存优化

2.10 ndarray内存模型&#xff1a;从指针到缓存优化 目录 #mermaid-svg-p0zxLYqAnn59O2Xe {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-p0zxLYqAnn59O2Xe .error-icon{fill:#552222;}#mermaid-svg-p0zxLYqAnn59O…

80-《红球姜》

红球姜 红球姜&#xff08;学名&#xff1a;Zingiber zerumbet (L.) Smith&#xff09;是姜科姜属多年生草本植物&#xff0c;根茎块状&#xff0c;株高可达2米。叶片披针形至长圆状披针形&#xff0c;无柄或短柄&#xff1b;总花梗长可达30厘米&#xff0c;花序球果状&#xf…

UE 5.3 C++ 对垃圾回收的初步认识

一.UObject的创建 UObject 不支持构造参数。 所有的C UObject都会在引擎启动的时候初始化&#xff0c;然后引擎会调用其默认构造器。如果没有默认的构造器&#xff0c;那么 UObject 将不会编译。 有修改父类参数的需求&#xff0c;就使用指定带参构造 // Sets default value…

点击WPS 任务栏上的图标,不是马上进入工作页面,而是呈现多个文档页面选择时的处理方法

问题&#xff1a; 点击WPS以后不是直接进入 解决&#xff1a; 首页-配置和修复工具-高级-兼容设置-改为与microsoft office 2010兼容(D)

【自然语言处理(NLP)】基于Transformer架构的预训练语言模型:BERT 训练之数据集处理、训练代码实现

文章目录 介绍BERT 训练之数据集处理BERT 原理及模型代码实现数据集处理导包加载数据生成下一句预测任务的数据从段落中获取nsp数据生成遮蔽语言模型任务的数据从token中获取mlm数据将文本转换为预训练数据集创建Dataset加载WikiText-2数据集 BERT 训练代码实现导包加载数据构建…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.5 高级索引应用:图像处理中的区域提取

2.5 高级索引应用&#xff1a;图像处理中的区域提取 目录/提纲 #mermaid-svg-BI09xc20YqcpUam7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BI09xc20YqcpUam7 .error-icon{fill:#552222;}#mermaid-svg-BI09xc20…

通过Redisson构建延时队列并实现注解式消费

目录 一、序言二、延迟队列实现1、Redisson延时消息监听注解和消息体2、Redisson延时消息发布器3、Redisson延时消息监听处理器 三、测试用例四、结语 一、序言 两个月前接了一个4万的私活&#xff0c;做一个线上商城小程序&#xff0c;在交易过程中不可避免的一个问题就是用户…

Baklib构建高效协同的基于云的内容中台解决方案

内容概要 随着云计算技术的飞速发展&#xff0c;内容管理的方式也在不断演变。企业面临着如何在数字化转型过程中高效管理和协同处理内容的新挑战。为应对这些挑战&#xff0c;引入基于云的内容中台解决方案显得尤为重要。 Baklib作为创新型解决方案提供商&#xff0c;致力于…

deepseek+vscode自动化测试脚本生成

近几日Deepseek大火,我这里也尝试了一下,确实很强。而目前vscode的AI toolkit插件也已经集成了deepseek R1,这里就介绍下在vscode中利用deepseek帮助我们完成自动化测试脚本的实践分享 安装AI ToolKit并启用Deepseek 微软官方提供了一个针对AI辅助的插件,也就是 AI Toolk…

电介质超表面中指定涡旋的非线性生成

涡旋光束在众多领域具有重要应用&#xff0c;但传统光学器件产生涡旋光束的方式限制了其在集成系统中的应用。超表面的出现为涡旋光束的产生带来了新的可能性&#xff0c;尤其是在非线性领域&#xff0c;尽管近些年来已经有一些研究&#xff0c;但仍存在诸多问题&#xff0c;如…

基于Springboot+mybatis+mysql+html图书管理系统2

基于Springbootmybatismysqlhtml图书管理系统2 一、系统介绍二、功能展示1.用户登陆2.用户主页3.图书查询4.还书5.个人信息修改6.图书管理&#xff08;管理员&#xff09;7.学生管理&#xff08;管理员&#xff09;8.废除记录&#xff08;管理员&#xff09; 三、数据库四、其它…

本地部署DeepSeek方法

本地部署完成后的效果如下图&#xff0c;整体与chatgpt类似&#xff0c;只是模型在本地推理。 我们在本地部署主要使用两个工具&#xff1a; ollamaopen-webui ollama是在本地管理和运行大模型的工具&#xff0c;可以直接在terminal里和大模型对话。open-webui是提供一个类…

游戏引擎 Unity - Unity 启动(下载 Unity Editor、生成 Unity Personal Edition 许可证)

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

【开源免费】基于Vue和SpringBoot的公寓报修管理系统(附论文)

本文项目编号 T 186 &#xff0c;文末自助获取源码 \color{red}{T186&#xff0c;文末自助获取源码} T186&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

《苍穹外卖》项目学习记录-Day11订单统计

根据起始时间和结束时间&#xff0c;先把begin放入集合中用while循环当begin不等于end的时候&#xff0c;让begin加一天&#xff0c;这样就把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间&#xff08;0点0分0秒&#xff09;小于…

【python】python油田数据分析与可视化(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【python】python油田数据分析与可视化&#xff08…

FBX SDK的使用:基础知识

Windows环境配置 FBX SDK安装后&#xff0c;目录下有三个文件夹&#xff1a; include 头文件lib 编译的二进制库&#xff0c;根据你项目的配置去包含相应的库samples 官方使用案列 动态链接 libfbxsdk.dll, libfbxsdk.lib是动态库&#xff0c;需要在配置属性->C/C->预…

一文讲解HashMap线程安全相关问题(上)

HashMap不是线程安全的&#xff0c;主要有以下几个问题&#xff1a; ①、多线程下扩容会死循环。JDK1.7 中的 HashMap 使用的是头插法插入元素&#xff0c;在多线程的环境下&#xff0c;扩容的时候就有可能导致出现环形链表&#xff0c;造成死循环。 JDK 8 时已经修复了这个问…