C++进阶学习:map和set的实现

我们知道set和map的底层其实是红黑树,在学习完红黑树这个数据结构之后,我们开始简单模拟实现一下这两个STL容器

目录

1.set和map的泛型编程思想

2.红黑树的结构  

2.1.迭代器的实现      

2.2.迭代器的 operator++

2.3.迭代器的代码 

2.4.set和map迭代器

 2.4.1.Insert的代码

2.4.2迭代器结构

​ 3.代码实现

3.1. set.h

3.2. map.h

3.3. RBTree.h


1.set和map的泛型编程思想

因为set和map底层高度封装所以我们需要通过源码的阅读来确定大致框架,我们发现map和set的内部均有一个value_type类型,其可以是key也可以是pair<>简化一下上图,我们大致定义出set和map 

我们知道泛型编程的核心就是代码的复用,在这一章节体现在同一个RBTree.h文件可以支持mySet.h和myMap.h随时用 

  而我们在前面红黑树的学习过程中,对于insert的插入位置查找,以及find的数据位置查找都需要遍历树的分支,通过的是key来寻找,如果是set类型就直接寻找,但是如果是pair类型呢?我们可能会说那就是pair<>.first来实现就好了!这个回答是没有问题的,结果却是形成了两棵树或者是需要重载两个insert,一个为bool insert(const K& key),另一个为bool insert(const pair<K, V>& kv)  ,这样无法较好泛型编程思想

2.红黑树的结构  

通过上部分的泛型编程,讲述我们在红黑树的中定义了一个新的变量KeyOfValue来实现这个对应set与map的匹配,又因为在C++学习进阶:set 和 map-CSDN博客中关于【】重载部分,我们知道了insert返回的是pair<iterator, bool>类型,所以我们开始定义迭代器!

2.1.迭代器的实现      

首先迭代器是一个类似于指针的容器(本质是指针),内部存放节点,那么底层实现也是通过树的节点,并且为了实现const_iterator,需要传入多个模版参数,因为之前我们在list章节,讲过迭代器的实现这里的话就不赘述了,就大概展示一下结构组分

2.2.迭代器的 operator++

++和--这两个操作符重载均是为了通过迭代器进行节点的遍历,而根据红黑树的规律,以及STL库中set和map迭代器遍历规律,我们需要将节点通过正向or负向中序遍历的方式来实现!

2.3.迭代器的代码 

template<class V, class Ref, class Ptr>
struct Tree_iterator
{typedef Tree_iterator<V, Ref, Ptr> Self;typedef RBTreeNode<V> Node;Node* _node = nullptr;Tree_iterator(Node* node):_node(node){}// 实现const的拷贝构造Tree_iterator(const Tree_iterator<V, V&, V*>& it):_node(it._node){}Self& operator=(const Self& s){_node = s._node;return *this;}Self& operator++(){// 右子树不为空,遍历到最左节点if (_node->_right != nullptr){Node* current = _node->_right;while (current != nullptr && current->_left != nullptr)current = current->_left;_node = current;}// 右子树为空 大的节点在祖先处else{Node* current = _node;Node* parent = current->_parent;// 如果current在parent的右进入循环,下一个节点就是祖父节点while (parent != nullptr && current == parent->_right){current = parent;parent = parent->_parent;}// 如果current是parent的左,不进入循环,下一个节点就是父亲节点_node = parent;}return *this;}Self& operator--(){if (_node->_left != nullptr){Node* current = _node->_left;while (current != nullptr && current->_right != nullptr)current = current->_right;_node = current;}else{Node* current = _node;Node* parent = current->_parent;while (parent != nullptr && current == parent->_left){current = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s) { return _node != s._node; }bool operator==(const Self& s) { return _node == s._node; }Ref operator*() { return _node->_value; }Ptr operator->() { return &_node->_value; }
};

2.4.set和map迭代器

实现完红黑树迭代器后我们开始实现set和map的迭代器,我们知道红黑树需要同时兼容set和map,而set是key类型并且key不能修改,map是pair<const Key, value>类型 ,这两个在实现时,set的迭代器不支持修改,而map的迭代器不支持key修改但是支持Value修改

 2.4.1.Insert的代码

// 增查pair<iterator, bool> insert(const V& value){// 1.先插入节点if (_root == nullptr){_root = new Node(value);// 根节点默认为黑色_root->_col = BLACK;return make_pair(iterator(_root), true);}Node* parent = nullptr;Node* current = _root;while (current != nullptr){parent = current;if (kov(current->_value) < kov(value))current = current->_right;else if (kov(current->_value) > kov(value))current = current->_left;elsereturn make_pair(iterator(current), false);}Node* newNode = new Node(value);current = newNode;// 新增节点默认为红色current->_col = RED;// 通过大小插入左右if (kov(parent->_value) < kov(value))parent->_right = current;elseparent->_left = current;current->_parent = parent;// 2.实现颜色的变换  情况来回的迭代直至不满足循环 退出时就满足了红黑树while (parent != nullptr && parent->_col == RED){/*进入循环情况:同时满足父节点不为空,且为红色原因: 如果父节点为空,则current为_root如果父节点为黑色,就不用更新颜色了*/Node* grandparent = parent->_parent;// 找到叔叔节点位置if (parent == grandparent->_left){// uncle在父亲右边Node* uncle = grandparent->_right;// 情况一:叔叔存在,且叔叔为红色if (uncle != nullptr && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;// 继续向上调整current = grandparent;parent = current->_parent;}// 情况二:叔叔不存在 或者 叔叔存在但是颜色为黑色else{if (current == parent->_left){RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}// current在右边else{RotateL(parent);RotateR(grandparent);// 通过旋转后,旋转内部就已经完成了指针的指向current->_col = BLACK;grandparent->_col = RED;}// 直接退出循环即可,也可以不退出,因为父亲已经为黑色了break;}}else{// uncle在父亲左边Node* uncle = grandparent->_left;if (uncle != nullptr && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;// 继续向上调整current = grandparent;parent = current->_parent;}else{if (current == parent->_right){RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}// current在左边else {// 注意这里单旋的节点不同RotateR(parent);RotateL(grandparent);grandparent->_col = RED;current->_col = BLACK;}break;}}}// 保持根节点始终为黑色,当current到达根节点可能为RED,这里就在外部变黑_root->_col = BLACK;return make_pair(iterator(newNode), true);}

 所以我们实现set和map时也需要按照这个规律,这里我们以set为例子,结合insert为pair<iterator, bool>类型的函数 

2.4.2迭代器结构

set的迭代器结构

这样子我们应该是实现了const_iterator的作用,那我们通过插入数据来判断一下 ,插入是成功的,不过最终会出现报错

如下图所示

那如何解决呢?

法一:

我们知道这里是,普通类型的迭代器不能作为const_iterator的接收值,也就是类型无法匹配,那么我们可以通过类型的拷贝构造,将const_iterator可以转化为原生的iterator迭代器,这样子iterator既可以作为普通的迭代器,也可以实现为const_iterator,带着这样的想法我们实现一个拷贝构造函数,代码在2.3.处有样例

法二:

另外,我们在研究pair类型时,发现pair也通过法一类似的方法实现了一个拷贝构造函数,也就是我们可以通过其他类型来实现const_iterator,我们知道迭代器的本质就是一个节点的指针,那么Node* 转化为const_iterator理论上是可行的,在通过iterator的( )默认构造函数,就可以通过iterator(pointer)

有了set的迭代器为例子,以及insert的实现,我们现在开始实现map的迭代器,map和set主要不同就是,map支持普通迭代器修改,但是会限制key的修改,也就是我们不能像set一样锁死map 

map的迭代器

我们在测试用例中尝试修改key,发现会出错,而value不受影响

 3.代码实现

3.1. set.h

#pragma once
#include<iostream>
#include "RBTree.h"
using namespace std;namespace zhong
{// set的实现template<class V>class set{// 类型匹配 对应树的 class keyOfValuestruct SetKetOf{const V& operator()(const V& value_type) { return value_type; }	// 通过定义keyOfValue kov 然后 借助()重载 来 找到key};public:// 这里体现了typename的作用// 对类模板取内嵌类型,需要加typename告知编译器这里是内嵌类型typedef typename RBTree<V, V, SetKetOf>::const_iterator iterator;typedef typename RBTree<V, V, SetKetOf>::const_iterator const_iterator;// 迭代器iterator begin() const { return _tree.begin(); }iterator end() const { return _tree.end(); }// 增查pair<iterator, bool> insert(const V& value) { return _tree.insert(value); }// iterator find(const V& value) { return _tree.find(value); }private:RBTree<V, V, SetKetOf> _tree;};void test_set1(){set<int> s1;s1.insert(2);s1.insert(3);s1.insert(4);s1.insert(1);set<int>::iterator it = s1.begin();while (it != s1.end()){// *it = 10;cout << *it << " ";++it;}cout << endl;}
}

3.2. map.h

#pragma once
#include<map>
#include<iostream>
#include"RBTree.h"
using namespace std;namespace zhong
{template<class K, class V>class map{struct MapKetOf{// 通过定义keyOfValue kov 然后 借助()重载 来 找到pair的firstconst K& operator()(const pair<K, V>& value_type) { return value_type.first; }};public:// 这里体现了typename的作用// 对类模板取内嵌类型,需要加typename告知编译器这里是内嵌类型typedef typename RBTree<K, pair<const K, V>, MapKetOf>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKetOf>::const_iterator const_iterator;V& operator[]    (const K& key){// insert返回pair<iterator, bool>,用ret接收插入节点的迭代器,解引用找到这个节点,接着 . 找到seconditerator ret = _tree.insert(make_pair(key, V())).first;return (*ret).second;//pair<iterator, bool> ret = insert(make_pair(key, V()));// return ret.first->second;}// 迭代器iterator begin() { return _tree.begin(); }iterator end() { return _tree.end(); }pair<iterator, bool> insert(const pair<K, V>& value_type) { return _tree.insert(value_type); }private:RBTree<K, pair<const K, V>, MapKetOf> _tree;};void test_map1(){map<string, string> dict;dict.insert(make_pair("name", "zhong"));dict.insert(make_pair("age", "21"));dict.insert(make_pair("id", "2022044026"));map<string, string>::iterator it = dict.begin();while (it != dict.end()){cout << (*it).first << " " << (*it).second << endl;++it;}}void test_map2(){// 测试map中的[]string str[] = { "嗯","哦","噢","喔","好","噢","噢","好" };map<string, int> countMap;for (auto& s : str){// map中实现计数要求// 借助[]重载实现countMap[s]++;// 分成多样的功能}map<string, int>::iterator ret = countMap.begin();while (ret != countMap.end()){cout << (*ret).first << " " << (*ret).second << endl;++ret;}}
}

3.3. RBTree.h

#include<iostream>
#include<vector>
using namespace std;// 实现颜色
enum Colour
{RED,BLACK
};template<class V>
struct RBTreeNode
{// 三叉链结构RBTreeNode<V>* _left;RBTreeNode<V>* _right;RBTreeNode<V>* _parent;V _value;// 实现颜色Colour _col;RBTreeNode(const V& value): _left(nullptr), _right(nullptr), _parent(nullptr), _value(value), _col(RED)	// 默认新节点红色 根节点为黑{}
};template<class V, class Ref, class Ptr>
struct Tree_iterator
{typedef Tree_iterator<V, Ref, Ptr> Self;typedef RBTreeNode<V> Node;Node* _node = nullptr;Tree_iterator(Node* node):_node(node){}// 实现const的拷贝构造Tree_iterator(const Tree_iterator<V, V&, V*>& it):_node(it._node){}Self& operator=(const Self& s){_node = s._node;return *this;}Self& operator++(){// 右子树不为空,遍历到最左节点if (_node->_right != nullptr){Node* current = _node->_right;while (current != nullptr && current->_left != nullptr)current = current->_left;_node = current;}// 右子树为空 大的节点在祖先处else{Node* current = _node;Node* parent = current->_parent;// 如果current在parent的右进入循环,下一个节点就是祖父节点while (parent != nullptr && current == parent->_right){current = parent;parent = parent->_parent;}// 如果current是parent的左,不进入循环,下一个节点就是父亲节点_node = parent;}return *this;}Self& operator--(){if (_node->_left != nullptr){Node* current = _node->_left;while (current != nullptr && current->_right != nullptr)current = current->_right;_node = current;}else{Node* current = _node;Node* parent = current->_parent;while (parent != nullptr && current == parent->_left){current = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s) { return _node != s._node; }bool operator==(const Self& s) { return _node == s._node; }Ref operator*() { return _node->_value; }Ptr operator->() { return &_node->_value; }
};template<class K, class V, class keyOfValue>
class RBTree
{typedef RBTreeNode<V> Node;
public:typedef Tree_iterator<V, V&, V*> iterator;typedef Tree_iterator<V, const V&, const V*> const_iterator;// 迭代器iterator begin(){Node* current = _root;while (current != nullptr && current->_left != nullptr)current = current->_left;return iterator(current);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* current = _root;while (current != nullptr && current->_left != nullptr)current = current->_left;return const_iterator(current);}const_iterator end() const{return const_iterator(nullptr);}// 增查pair<iterator, bool> insert(const V& value){// 1.先插入节点if (_root == nullptr){_root = new Node(value);// 根节点默认为黑色_root->_col = BLACK;return make_pair(iterator(_root), true);}Node* parent = nullptr;Node* current = _root;while (current != nullptr){parent = current;if (kov(current->_value) < kov(value))current = current->_right;else if (kov(current->_value) > kov(value))current = current->_left;elsereturn make_pair(iterator(current), false);}Node* newNode = new Node(value);current = newNode;// 新增节点默认为红色current->_col = RED;// 通过大小插入左右if (kov(parent->_value) < kov(value))parent->_right = current;elseparent->_left = current;current->_parent = parent;// 2.实现颜色的变换  情况来回的迭代直至不满足循环 退出时就满足了红黑树while (parent != nullptr && parent->_col == RED){/*进入循环情况:同时满足父节点不为空,且为红色原因: 如果父节点为空,则current为_root如果父节点为黑色,就不用更新颜色了*/Node* grandparent = parent->_parent;// 找到叔叔节点位置if (parent == grandparent->_left){// uncle在父亲右边Node* uncle = grandparent->_right;// 情况一:叔叔存在,且叔叔为红色if (uncle != nullptr && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;// 继续向上调整current = grandparent;parent = current->_parent;}// 情况二:叔叔不存在 或者 叔叔存在但是颜色为黑色else{if (current == parent->_left){RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}// current在右边else{RotateL(parent);RotateR(grandparent);// 通过旋转后,旋转内部就已经完成了指针的指向current->_col = BLACK;grandparent->_col = RED;}// 直接退出循环即可,也可以不退出,因为父亲已经为黑色了break;}}else{// uncle在父亲左边Node* uncle = grandparent->_left;if (uncle != nullptr && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;// 继续向上调整current = grandparent;parent = current->_parent;}else{if (current == parent->_right){RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}// current在左边else {// 注意这里单旋的节点不同RotateR(parent);RotateL(grandparent);grandparent->_col = RED;current->_col = BLACK;}break;}}}// 保持根节点始终为黑色,当current到达根节点可能为RED,这里就在外部变黑_root->_col = BLACK;return make_pair(iterator(newNode), true);}iterator find(const K& key){}// 外部调用封装函数// 中序遍历void inOrder() { _inOrder(_root); cout << endl; }// 判断是否平衡bool ifBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 作为辅助判断int flag = 0;Node* current = _root;while (current != nullptr){if (current->_col == BLACK)flag++;current = current->_left;}// 设置一个flag值记录某一条路径的长度// 如果flag不等于任何一条路径的长度就表示某一条路径黑色节点数有误cout << "flag大小为:" << flag << endl;// 根节点到当前节点路径上黑色节点数量int blackNum = 0;return check(_root, blackNum, flag);}// 返回树节点个数int size() { return _size(_root); }// 返回树的高度int Height() { return _Height(_root); }// 封装函数区	
private:// 左单旋void RotateL(Node* root){Node* parent = root;Node* subR = parent->_right;Node* subRL = subR->_left;Node* grandparent = parent->_parent;subR->_left = parent;parent->_right = subRL;if (subRL != nullptr)subRL->_parent = parent;parent->_parent = subR;parent = subR;if (grandparent == nullptr){_root = parent;}else{if (grandparent->_left == root)grandparent->_left = parent;elsegrandparent->_right = parent;}parent->_parent = grandparent;}// 右单旋void RotateR(Node* root){Node* parent = root;Node* subL = parent->_left;Node* subLR = subL->_right;Node* grandparent = parent->_parent;subL->_right = parent;parent->_left = subLR;if (subLR != nullptr){subLR->_parent = parent;}parent->_parent = subL;parent = subL;if (grandparent == nullptr){_root = parent;}else{if (grandparent->_left == root)grandparent->_left = parent;elsegrandparent->_right = parent;}parent->_parent = grandparent;}// 中序遍历void _inOrder(Node* root){keyOfValue kov;if (root == nullptr)return;_inOrder(root->_left);cout << kov(root->_value) << " ";_inOrder(root->_right);}// 检查是否出现连续红色节点bool check(Node* root, int blackNum, const int flag){if (root == nullptr){// cout << "黑色节点数目:" << blackNum << endl;if (blackNum != flag){cout << "路径上的黑色节点数存在不同" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){// 子节点和父节点均为红色,不符合红黑树cout << "存在连续的红色节点" << endl;return false;}if (root->_col == BLACK)++blackNum;// 传值调用,只影响函数块,上一层不影响下一层return check(root->_left, blackNum, flag) && check(root->_right, blackNum, flag);}// 计算树的节点个数int _size(Node* root){if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}// 计算树的高度int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}private:Node* _root = nullptr;keyOfValue kov;
};

到了这里通过红黑树初步封装map和set就讲到这里了,我们再来讲一下什么是封装,封装本质就是套个盒子在外面,当我们研究封装好的某一个类时,我们需要逐层剖析,找到封装前到底是什么牛鬼蛇神,进而有助于我们阅读源码

封装是面向对象编程中的一种重要特性,它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部的信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。封装可以有效地保护对象的数据安全性,同时也可以提高代码的可维护性和可重用性。

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

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

相关文章

资产管理系统部署及库存告警

1.需求&#xff1a;对电脑&#xff0c;办公设备&#xff0c;耗材等做资产盘点和整理&#xff0c;并对库存预警。 2.选型&#xff1a;snipeit 3.部署 #!/bin/bash docker run -d -p 80:80 --name"snipeit" --env-filesnipeit.env --mount sourcesnipe-vol,dst/var/l…

【算法系列篇】递归、搜索和回溯(三)

文章目录 前言什么是二叉树剪枝1. 二叉树剪枝1.1 题目要求1.2 做题思路1.3 代码实现 2. 验证二叉搜索树2.1 题目要求2.2 做题思路2.3 代码实现 3. 二叉搜索树中第k小的元素3.1 题目要求3.2 做题思路3.3 代码实现 4. 二叉树的所有路径4.1 题目要求4.2 做题思路4.3 代码实现 前言…

(八)数组和函数实践:扫雷游戏

目录 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 1.2 游戏的分析和设计 1.2.1 数据结构的分析 1.2.2 文件结构设计 2. 扫雷游戏的代码实现 3. 如何生成用户版本 4. 完整的排雷程序 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 1&#xff09;使用控制台实现经典…

数据结构和算法-栈

数据结构和算法-栈 文章目录 数据结构和算法-栈1. 栈的介绍2. 栈的应用场景3. 栈的快速入门3.1 用数组模拟栈3.2 课堂作业-用链表模拟栈 4. 栈实现综合计算器4.1 课堂作业-加入小括号5. 栈的三种表达式-**前缀、中缀、后缀表达式(逆波兰表达式)**5.1 前缀表达式(波兰表达式)5.1…

中低压MOS 适用于电子烟等产品—— 较小的开关损耗 过流能力好

工作原理&#xff1a; 当用户在吸嘴处抽吸时&#xff0c;气流经过进气孔&#xff0c;穿 过电路板上方的咪头&#xff0c;咪头即产生电信号&#xff0c;驱 动芯片板&#xff0c;让电池供电给雾化芯&#xff0c;雾化芯中的 发热丝将电能转化成热能&#xff0c;当温度达到雾化液…

LeetCode-2487. 从链表中移除节点【栈 递归 链表 单调栈】

LeetCode-2487. 从链表中移除节点【栈 递归 链表 单调栈】 题目描述&#xff1a;解题思路一&#xff1a;可以将链表转为数组&#xff0c;然后从后往前遍历&#xff0c;遇到大于等于当前元素的就入栈&#xff0c;最终栈里面的元素即是最终的答案。解题思路二&#xff1a;递归&am…

【一步到位】汽车过户全攻略:轻松搞定,告别繁琐流程

校长车行是一家昆明二手车代办公司&#xff0c;今天我们要聊一聊一个让很多人头疼的问题——汽车过户。相信很多朋友在购买二手车或者需要将车辆转让给他人时&#xff0c;都会遇到这个繁琐的流程。那么&#xff0c;如何才能轻松搞定汽车过户呢&#xff1f;接下来&#xff0c;就…

(0-1)分布

假设离散型随机变量X只可能取到0、1两个值&#xff0c;它的分布律为&#xff1a; &#xff0c;其中&#xff0c; 那么称X服从参数为p的0-1分布&#xff0c;也叫两点分布。 其实上面公式就是将下面两个式子写在一起&#xff1a;

【Hive_02】查询语法

1、基础语法2、基本查询&#xff08;Select…From&#xff09;2.1 全表和特定列查询2.2 列别名2.3 Limit语句2.4 Where语句2.5 关系运算函数2.6 逻辑运算函数2.7 聚合函数 3、分组3.1 Group By语句3.2 Having语句3.3 Join语句&#xff08;1&#xff09;等值与不等值Join&#x…

SUPER-ADAM: Faster and Universal Framework of Adaptive Gradients

这周看了啥&#xff1a; 本周主要来看看别人是如何证明收敛的&#xff0c;围绕算法SUPER-ADAM 的更新过程和论文后面的证明&#xff0c;&#xff08;这篇证明比上周的亲切多了&#xff0c;我哭死&#xff09;仔细看了证明每一步的推导&#xff08;至于作者如何想出的&#xff…

verilog基础语法之比较器

逻辑运算符以及逻辑电路概述 逻辑运算符常用于条件判断语句&#xff0c;输出为布尔值True/False。逻辑运算符是基于比较器构造的。比较器电路是产生逻辑比较的本质&#xff1b;比较器电路的复杂度与位宽和比较类型相关&#xff1b;一般情况下可以先构造基本比较器&#xff0c;…

原生Html 引入element UI + vue3 表单校验设置

效果&#xff1a; 提交时&#xff0c;检验结果展示 html源码 <!DOCTYPE html> <html> <!--带搜索输入框下拉弹窗 --> <head><meta charset"UTF-8"><!-- import Vue before Element --><script src"../js/vue3.3.8/vu…

jmeter,通过Ant插件生成html报告,展示接口详细信息

一、下载Ant 下载地址&#xff1a;Apache Ant - 二进制发行版 二、安装 1、Ant环境变量 解压Ant目录&#xff1b;配置系统环境变量&#xff0c;添加ANT_PATH&#xff0c;值为D:\Software\Ant_plugIn\apache-ant-1.10.14配置系统环境变量Path&#xff0c;添加Ant路径 %ANT_H…

Unity之OpenXR+XR Interaction Toolkit接入Meta Quest3

前言 随着备受期待的Meta Quest 3与今年10月10日发布,这款来自Meta的下一代VR游戏头戴设备承诺将彻底改变您的游戏方式。 Meta Quest 3,玩家只需轻松一触即可在虚拟现实和真实世界之间无缝切换,无需摘下头戴设备进行快速现实检查。 Meta Quest 3最引人注目的特点之一是其能…

webpack学习-5.代码分离

webpack学习-5.代码分离 1.入口起点2.防止重复2.1 入口依赖2.2 SplitChunksPlugin 3.动态导入3.1 使用符合 ECMAScript 提案 的 import() 语法3.2 使用 webpack 特定的 require.ensure 4.预获取/预加载模块5.分析bundle6.总结 1.入口起点 代码分离是 webpack 中最引人注目的特…

AIGC - 环境搭建

一. 硬件环境 1. 超微7048主板&#xff0c;最多可搭载4块GPU 2. 2个Intel的 Xen至强 14核 CPU 3. 目前安装了一块Nvidia 的P40 GPU&#xff0c;后续根据需要还最多可以扩展3块GPU 4. 4T机械 2T Nvme固态&#xff0c; 5. 4条64G DDR4内存条&#xff0c;共 196G内存…

QT多项目管理

.pro文件配置解释&#xff1a;​​​​​​ Qt 中的多项目管理_qt子目录项目-CSDN博客Qt 模块化开发之 pro 子项目开发_qt 子项目-CSDN博客关于Qt编译库&#xff08;1&#xff09;&#xff1a;在子项目中编译动态库并且使用_qt编译动态库后配置qt-CSDN博客QT release下的编译…

涵盖多种功能,龙讯旷腾Module第六期:输运性质

Module是什么 在PWmat的基础功能上&#xff0c;我们针对用户的使用需求开发了一些顶层模块&#xff08;Module&#xff09;。这些Module中的一部分是与已有的优秀工具的接口&#xff0c;一部分是以PWmat的计算结果为基础得到实际需要的物理量&#xff0c;一部分则是为特定的计…

排序算法(二)-冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序

排序算法(二) 前面介绍了排序算法的时间复杂度和空间复杂数据结构与算法—排序算法&#xff08;一&#xff09;时间复杂度和空间复杂度介绍-CSDN博客&#xff0c;这次介绍各种排序算法——冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序。 文章目录 排…

排序-归并排序与计数排序

文章目录 一、归并排序1、概念2、过程3、代码实现4、复杂度5、稳定性 二、 计数排序1、思路2、代码实现3、复杂度&#xff1a;4、稳定性 一、归并排序 1、概念 是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已…