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

【C++笔记】红黑树封装map和set深度剖析

【C++笔记】红黑树封装map和set深度剖析

在这里插入图片描述

🔥个人主页大白的编程日记

🔥专栏C++笔记


文章目录

  • 【C++笔记】红黑树封装map和set深度剖析
    • 前言
    • 一. 源码及框架分析
      • 1.1 源码框架分析
    • 二. 模拟实现map和set
      • 2.1封装map和set
    • 三.迭代器
      • 3.1思路分析
      • 3.2 代码实现
    • 四.operator[]
      • 4.1思路分析
    • 五.源码
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了红黑树。今天我们来讲一下用红黑树封装map和set。话不多说,我们进入正题!向大厂冲锋
在这里插入图片描述

一. 源码及框架分析

我们看一下源码是如何实现红黑树封装map和set。

参考SGI-STL30版本源代码,map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等几个头文件中。

// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>
// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>
// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;
private:typedef rb_tree<key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};
// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing map
};
// stl_tree.h
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc= alloc>
class rb_tree {
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert⽤的是第⼆个模板参数左形参pair<iterator, bool> insert_unique(const value_type& x);// erase和find⽤第⼀个模板参数做形参size_type erase(const key_type& x);iterator find(const key_type& x);
protected:size_type node_count; // keeps track of size of treelink_type header;
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};

1.1 源码框架分析

  • 通过下图对框架的分析,我们可以看到源码中rb_tree用了⼀个巧妙的泛型思想实现,rb_tree是实现key的搜索场景,还是key/value的搜索场景不是直接写死的,而是由第⼆个模板参数Value决定_rb_tree_node中存储的数据类型。
  • set实例化rb_tree时第二个模板参数给的是key,map实例化rb_tree时第二个模板参数给的是pair<const key, T>,这样⼀颗红黑树既可以实现key搜索场景的set,也可以实现key/value搜索场景的map。
  • 要注意一下,源码里面模板参数是用T代表value,而内部写的value_type不是我们我们日常key/value场景中说的value,源码中的value_type反而是红黑树结点中存储的真实的数据的类型。
  • rb_tree第二个模板参数Value已经控制了红黑树结点中存储的数据类型,为什么还要传第一个模板参数Key呢?尤其是set,两个模板参数是⼀样的,这是很多同学这时的⼀个疑问。要注意的是对于map和set,find/erase时的函数参数都是Key,所以第⼀个模板参数是传给find/erase等函数做形参的类型的。对于set而言两个参数是⼀样的,但是对于map而言就完全不一样了,map insert的是pair对象,但是find和ease的是Key对象。

所以我们可以通过模版参数来控制红黑树底层的结构。
所以不用实现两颗红黑树,但是本质还是编译器根据模版参数生成两颗不同的红黑树。

二. 模拟实现map和set

这里借鉴源码的底层实现。
我们也自己模拟实现出我们的mymap和myset.

2.1封装map和set

  • 参考源码框架,map和set复用之前我们实现的红黑树。
  • 我们这里相比源码调整⼀下,key参数就用K,value参数就用V,红黑树中的数据类型,我们使用T。
  • 其次因为RBTree实现了泛型不知道T参数导致是K,还是pair<K, V>,那么insert内部进行插入逻辑比较时,就没办法进行比较,因为pair的默认支持的是key和value⼀起参与比较,我们需要时的任何时候只比较key,所以我们在map和set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给RBTree的KeyOfT,然后RBTree中通过KeyOfT仿函数取出T类型对象中的key,再进行比较,具体细节参考如下代码实现。

set.h:

template<class k>
class set
{
public:struct SetKeyofT{const k& operator()(const k& key){return key;}};bool insert(const k& kv){return _t.Insert(kv);}
private:qcj::RBTree<k,  const k, SetKeyofT> _t;
};

map.h:

template<class k,class v>
class map
{
public:struct MapKeyofT{const k& operator()(const pair<k, v>& key){return key.first;}};bool insert(const pair<k, v>& kv) {return _t.Insert(kv);}
private:qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;
};

insert:

bool Insert(const T& x) 
{if (_root == nullptr)//插入根节点{_root = new node(x);_root->col = BLACK;return true;};node* cur = _root;node* parent = nullptr;//保留父亲节点KeyofT kot;while (cur){/*介质不冗余*/if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->left;}else if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->right;}else{return false;}}cur = new node(x);cur->col = RED;if (kot(x) > kot(parent->_date)){parent->right = cur;}else//相等插入左子树{parent->left = cur;}cur->parent = parent;while (parent&&parent->parent&&parent->col == RED){node* grandfather = parent->parent;if (parent == grandfather->left){node* uncle = grandfather->right;//         g//       p    u//     c  c//u存在且为红if (uncle&&uncle->col==RED){parent->col = uncle->col=BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在为黑else{//         g//       p    //     cif (cur == parent->left){RoRateR(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//       p    //         celse{RoRateL(parent);RoRateR(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}else{node* uncle = grandfather->left;//         g//       u   p    //          c  c//u存在且为红if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在为黑else{//         g//            p    //              cif (cur == parent->right){RoRateL(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//            p    //          celse{RoRateR(parent);RoRateL(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}}_root->col = BLACK;//循环结束把根变黑return true;
}

find:

node* Find(const k& x)
{node* ret = nullptr;node* cur = _root;while (cur){if (x < kot(cur)){cur = cur->left;}else if (x > kot(cur)){cur = cur->right;}else{ret = cur;//保留当前节点cur = cur->left;//继续向左子树查找中序的第一个}}return ret;
}

这里通过仿函数就取出key控制了比较逻辑。

三.迭代器

3.1思路分析

  • operator++
  • operator- -
  • 迭代器的key修改问题

    如果我们这样实现的迭代器是可以修改的key的。
    但是红黑树的key是不能被修改的。那怎么办呢?
    我们只需要把红黑树的第二个模版参数的key改为
    const key即可这样迭代器的模版参数T的key也是const的了
    set的iterator也不支持修改,我们把set的第⼆个模板参数改成const K即可,
qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;
qcj::RBTree<k,  const k, SetKeyofT> _t;
  • 对比库里的迭代器
    为了实现反向迭代器我们可以在operator- -多加一个判断。
    同时还需要在迭代器里面加一个_root节点
    在这里插入图片描述

  • 运算符重载
    运算符重载比较简单具体参考下面的代码

3.2 代码实现

template<class T,class Ref,class Ptr>
struct RBTreeIteraor
{using node= RBNode<T>;using self= RBTreeIteraor< T, Ref, Ptr >;node* _node;node* _root;RBTreeIteraor(node* Node,node* root):_node(Node),_root(root){}self& operator++(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}}else if (_node->right!=nullptr){node* cur = _node->right;while (cur->left){cur = cur->left;}_node = cur;}else{node* parent = _node->parent;while (parent&&_node != parent->left){_node = parent;parent = _node->parent;}_node = parent;}return *this;}self& operator--(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}_node = cur;}else if (_node->left != nullptr){node* cur = _node->left;while (cur->right){cur = cur->right;}_node = cur;}else{node* parent = _node->parent;while (parent && _node != parent->right){_node = parent;parent = _node->parent;}_node = parent;}return *this;}Ref operator*(){return _node->_date;}Ptr operator->(){return &_node->_date;}bool operator==(const self& tmp) const{return _node==tmp._node;}bool operator!=(const self& tmp) const{return _node != tmp._node;}
};

四.operator[]

4.1思路分析

对于map我们还需要支持operator[]。
对于operator[]我们前面已经讲过底层实现了。
参考博文:map的operator[]底层剖析
主要就是insert来支持的。改一下返回值即可。
让insert返回迭代器和bool的pair即可。
返回insert迭代器的second也就是value即可。

v& operator[](const k& key)
{pair<iterator, bool> ret = _t.Insert(make_pair(key, v()));return ret.first->second;
}

五.源码

  • set.h
#pragma once
#include"RBTree.h"
namespace qcj
{template<class k>class set{public:struct SetKeyofT{const k& operator()(const k& key){return key;}};public:typedef typename RBTree<k,  const k, SetKeyofT>::Iteratoriterator;typedef typename RBTree<k, const k, SetKeyofT>::Const_Iteratorconst_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const k& kv){return _t.Insert(kv);}iterator find(const k& key){return _t.find(key);}private:qcj::RBTree<k,  const k, SetKeyofT> _t;};void Print(const set<int>& s){set<int>::iterator it = s.end();while (it != s.begin()){--it;// 不⽀持修改//*it += 2;cout << *it << " ";}cout << endl;}void test_set(){set<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){s.insert(e);}for (auto e : s){e += 1;cout << e << " ";}cout << endl;}}
  • map.h
#pragma once
#include"RBTree.h"
namespace qcj 
{template<class k,class v>class map{public:struct MapKeyofT{const k& operator()(const pair<k, v>& key){return key.first;}};public:typedef typename RBTree<k, pair<const  k, v>, MapKeyofT>::Iteratoriterator;typedef typename RBTree<k, pair<const  k, v>, MapKeyofT>::Const_Iteratorconst_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const pair<k, v>& kv) {return _t.Insert(kv);}iterator find(const k& key){return _t.find(key);}v& operator[](const k& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, v()));return ret.first->second;}private:qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;};void test_map(){map<int,int> dict;dict.insert({1,1});dict.insert({2,2 });dict.insert({3,3 });dict.insert({ 4,4 });dict.insert({ 5,5 });dict.insert({ 6,6 });map<int,int>::iterator it = dict.end();while (it != dict.begin()){--it;// 不能修改first,可以修改second//it->first += 'x';it->second += 1;cout << it->first << ":" << it->second << endl;}cout << endl;}
}
  • RBTree.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace qcj
{enum coloros{RED,BLACK};template<class T>struct RBNode{using node= RBNode<T>;T _date;node* left;//左节点node* right;//右节点node* parent;//父亲节点coloros col;//颜色RBNode(const T& date):_date(date), left(nullptr), right(nullptr), parent(nullptr){}};template<class T,class Ref,class Ptr>struct RBTreeIteraor{using node= RBNode<T>;using self= RBTreeIteraor< T, Ref, Ptr >;node* _node;node* _root;RBTreeIteraor(node* Node,node* root):_node(Node),_root(root){}self& operator++(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}}else if (_node->right!=nullptr){node* cur = _node->right;while (cur->left){cur = cur->left;}_node = cur;}else{node* parent = _node->parent;while (parent&&_node != parent->left){_node = parent;parent = _node->parent;}_node = parent;}return *this;}self& operator--(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}_node = cur;}else if (_node->left != nullptr){node* cur = _node->left;while (cur->right){cur = cur->right;}_node = cur;}else{node* parent = _node->parent;while (parent && _node != parent->right){_node = parent;parent = _node->parent;}_node = parent;}return *this;}Ref operator*(){return _node->_date;}Ptr operator->(){return &_node->_date;}bool operator==(const self& tmp) const{return _node==tmp._node;}bool operator!=(const self& tmp) const{return _node != tmp._node;}};template<class k, class T, class KeyofT>class RBTree{using node = RBNode<T>;public:RBTree() = default;using Iterator = RBTreeIteraor<T, T&, T*>;using Const_Iterator = RBTreeIteraor<T, const T&, const T*>;Iterator Begin(){node* cur = _root;while (cur&&cur->left){cur = cur->left;}return Iterator(cur,_root );}Iterator End(){return Iterator(nullptr,_root);}Const_Iterator Begin() const{node* cur = _root;while (cur&&cur->left){cur = cur->left;}return { cur,_root };}Const_Iterator End() const{return { nullptr,_root };}void Destory(const node* root){if (root == nullptr){return;}Destory(root->left);Destory(root->right);delete root;}~RBTree(){Destory(_root);_root = nullptr;}RBTree<k,T,KeyofT>& operator = (RBTree<k, T, KeyofT> tmp){swap(_root, tmp._root);return *this;}RBTree(const RBTree<k, T, KeyofT>& x){_root = Copy(x._root,nullptr);}node* Copy(node* x,node* parent){if (x == nullptr){return x;}node* root = new node(x->_date);root->parent = parent;root->left = Copy(x->left,root);root->right = Copy(x->right,root);return root;}void RoRateR(node* parent)//右单旋{node* subL = parent->left;node* subLR = subL->right;node* pparnet = parent->parent;parent->left = subLR;if (subLR){subLR->parent = parent;//修改父节点}subL->right = parent;parent->parent = subL;if (pparnet == nullptr)//parent就是根节点{_root = subL;subL->parent = nullptr;}else{if (pparnet->left == parent)//确定parent节点是左还是右{pparnet->left = subL;}else{pparnet->right = subL;}subL->parent = pparnet;//修改父节点}}void RoRateL(node* parent)//左单旋{node* subR = parent->right;node* subRL = subR->left;node* pparnet = parent->parent;parent->right = subRL;if (subRL)//防止subRL为空{subRL->parent = parent;//修改父节点}subR->left = parent;parent->parent = subR;if (pparnet == nullptr)//parent就是根节点{_root = subR;subR->parent = nullptr;}else{if (pparnet->left == parent)//确定parent节点是左还是右{pparnet->left = subR;}else{pparnet->right = subR;}subR->parent = pparnet;//修改父节点}}pair<Iterator,bool> Insert(const T& x) {if (_root == nullptr)//插入根节点{_root = new node(x);_root->col = BLACK;return make_pair(Iterator(_root, _root), true);};node* cur = _root;node* parent = nullptr;//保留父亲节点KeyofT kot;while (cur){/*介质不冗余*/if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->left;}else if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->right;}else{return make_pair(Iterator(cur, _root), true);}}cur = new node(x);cur->col = RED;if (kot(x) > kot(parent->_date)){parent->right = cur;}else//相等插入左子树{parent->left = cur;}cur->parent = parent;while (parent&&parent->parent&&parent->col == RED){node* grandfather = parent->parent;if (parent == grandfather->left){node* uncle = grandfather->right;//         g//       p    u//     c  c//u存在且为红if (uncle&&uncle->col==RED){parent->col = uncle->col=BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在为黑else{//         g//       p    //     cif (cur == parent->left){RoRateR(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//       p    //         celse{RoRateL(parent);RoRateR(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}else{node* uncle = grandfather->left;//         g//       u   p    //          c  c//u存在且为红if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在为黑else{//         g//            p    //              cif (cur == parent->right){RoRateL(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//            p    //          celse{RoRateR(parent);RoRateL(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}}_root->col = BLACK;//循环结束把根变黑return make_pair(Iterator(cur, _root), true);}bool check(node* cur,size_t tmp,size_t sum){if (cur == nullptr){if (tmp != sum){cout << "存在黑色结点的数量不相等的路径" << endl;return false;}return true;}if (cur->col == RED){if (cur->parent->col == RED){cout << cur->_key << ":" << "存在连续红节点" << endl;return false;}}else{sum++;}return check(cur->left, tmp, sum) && check(cur->right, tmp, sum);}bool ISRBTree(){return _ISRBTree(_root);}bool _ISRBTree(node* cur){if (cur == nullptr){return true;}if (cur->col == RED){return false;}size_t t = 0;while (cur){if (cur->col == BLACK){t++;}cur = cur->left;}return check(_root,t,0);}node* Find(const k& x){node* ret = nullptr;node* cur = _root;while (cur){if (x < kot(cur)){cur = cur->left;}else if (x > kot(cur)){cur = cur->right;}else{ret = cur;//保留当前节点cur = cur->left;//继续向左子树查找中序的第一个}}return ret;}void Inorder(){_Inorder(_root);cout << endl;}bool IsBalanceTree(){return _IsBalanceTree(_root);}void _Inorder(const node* root){if (root == nullptr){return;}_Inorder(root->left);cout << root->_key << ":" << root->_value << endl;_Inorder(root->right);}size_t Size(){return _Size(_root);}size_t _Size(const node* parent){if (parent){return 1 + _Size(parent->left) + _Size(parent->right);}else{return 0;}}size_t Height(){return _Height(_root);}size_t _Height(const node* parent){if (parent){return 1 + max(_Height(parent->left), _Height(parent->right));}else{return 0;}}bool Empty(){return _root == nullptr;}private:node* _root = nullptr;};
}

在这里插入图片描述

后言

这就是红黑树封装map和set深度剖析。大家自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

相关文章:

【C++笔记】红黑树封装map和set深度剖析

【C笔记】红黑树封装map和set深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】红黑树封装map和set深度剖析前言一. 源码及框架分析1.1 源码框架分析 二. 模拟实现map和set2.1封装map和set 三.迭代器3.1思路…...

4.若依 BaseController

若依的BaseController是其他所有Controller的基类&#xff0c;一起来看下BaseController定义了什么 1. 定义请求返回内容的格式 code/msg/data 返回数据格式不是必须是AjaxResult&#xff0c;开发者可以自定义返回格式&#xff0c;注意与前端取值方式一致即可。 2. 获取调用…...

vue项目配置多语言

本文详细介绍如何在 Vue 项目中集成 vue-i18n 和 Element-UI &#xff0c;实现多语言切换&#xff1b;首先通过 npm 安装 vue-i18n 和相关语言包&#xff0c;接着在配置文件中设置中文和英文的语言信息&#xff1b;最后在 main.js 中导入并挂载多语言实例&#xff0c;实现切换地…...

数据可视化大屏设计与实现

本文将带你一步步了解如何使用 ECharts 实现一个数据可视化大屏&#xff0c;并且如何动态加载天气数据展示。通过整合 HTML、CSS、JavaScript 以及后端接口请求&#xff0c;我们可以构建一个响应式的数据可视化页面。 1. 页面结构介绍 在此例中&#xff0c;整个页面分为几个主…...

PDF文件提取开源工具调研总结

概述 PDF是一种日常工作中广泛使用的跨平台文档格式&#xff0c;常常包含丰富的内容&#xff1a;包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用&#xff0c;尤其是RAG项目中&#xff0c;通过将非结构化数据转化为结构化和可访问的信息&#xff0…...

多监控m3u8视频流,怎么获取每个监控的封面图(纯前端)

文章目录 1.背景2.问题分析3.解决方案3.1解决思路3.2解决过程3.2.1 封装播放组件3.2.2 隐形的视频div3.2.3 截取封面图 3.3 结束 1.背景 有这样一个需求&#xff1a; 给你一个监控列表&#xff0c;每页展示多个监控&#xff08;至少12个&#xff0c;m3u8格式&#xff09;&…...

【机器学习实战入门项目】使用深度学习创建您自己的表情符号

深度学习项目入门——让你更接近数据科学的梦想 表情符号或头像是表示非语言暗示的方式。这些暗示已成为在线聊天、产品评论、品牌情感等的重要组成部分。这也促使数据科学领域越来越多的研究致力于表情驱动的故事讲述。 随着计算机视觉和深度学习的进步&#xff0c;现在可以…...

技术洞察:C++在后端开发中的前沿趋势与社会影响

文章目录 引言C在后端开发中的前沿趋势1. 高性能计算的需求2. 微服务架构的兴起3. 跨平台开发的便利性 跨领域技术融合与创新实践1. C与人工智能的结合2. C与区块链技术的融合 C对社会与人文的影响1. 提升生产力与创新能力2. 促进技术教育与人才培养3. 技术与人文的深度融合 结…...

【人工智能 | 大数据】基于人工智能的大数据分析方法

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…...

数字经济时代下的创新探索与实践:以“开源AI智能名片2+1链动模式S2B2C商城小程序源码”为核心

摘要&#xff1a;在数字经济蓬勃发展的今天&#xff0c;中国作为全球数字经济的领航者&#xff0c;正以前所未有的速度推进“数字中国”建设。本文旨在探讨“开源AI智能名片21链动模式S2B2C商城小程序源码”在数字经济背景下的应用潜力与实践价值&#xff0c;从多个维度分析其对…...

【English-Book】Go in Action目录页翻译中文

第8页 内容 前言 xi 序言 xiii 致谢 xiv 关于本书 xvi 关于封面插图 xix 1 介绍 Go 1 1.1 用 Go 解决现代编程挑战 2 开发速度 3 • 并发 3 • Go 的类型系统 5 内存管理 7 1.2 你好&#xff0c;Go 7 介绍 Go 玩具 8 1.3 总结 8 2 Go 快速入门 9 2.1 程序架构 10 2.2 主包 …...

js: 区分后端返回数字是否为null、‘-’ 或正常number类型数字。

问&#xff1a; 这是我的代码<CountTo v-if!isNaN(Number(item.num))> <span v-else>{{item.num}}</span> 我希望不是null的时候走countTo&#xff0c;是null的时候直接<span>{{item.num}}</span>显示 回答&#xff1a; 最终结果&#xff1a; …...

网络变压器的分类

网络变压器是局域网(LAN)中各级网络设备中必备的元件。它们的主要功能是传输数据&#xff0c;增强信号&#xff0c;并提供电气隔离&#xff0c;以防雷保护和匹配阻抗。网络变压器也被称为数据泵或网络隔离变压器。它们广泛应用于网络交换机、路由器、网卡、集线器等设备中。 网…...

SUCTF-SU_BBRE-好久不见21

哈哈哈哈哈哈&#xff0c;&#xff0c;&#xff0c;&#xff0c;纯汇编有大佬用工具反编译成伪代码吗。。。 题解&#xff1a; 由function2处逻辑&#xff0c;解rc4得到第一段flag We1com3ToReWorld&#xff0c;正常输入下执行完function0&#xff0c;程序结束&#xff0c;cong…...

Python 实现 NLP 的完整流程

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…...

穷举vs暴搜vs深搜vs回溯vs剪枝系列一>N 皇后

题目&#xff1a; 解析&#xff1a; 1.决策树&#xff1a; 代码设计&#xff1a; 根据决策树剪枝设计&#xff1a; 代码&#xff1a; class Solution {private List<List<String>> ret;private char[][] path;private boolean[] checkdig1,checkdig2,checkco…...

JEL分类号

JEL分类系统&#xff0c;是美国经济学会“经济文献杂志”(《经济文献杂志》)所创立的对经济学文献的主题分类系统&#xff0c;并被现代西方经济学界广泛采用。 该分类方法主要采用开头的一个英文字母与随后的两位阿拉伯数字一起对经济学各部类进行“辞书式”编码分类。 https:…...

设计和优化用于 AR、HUD 和高级显示系统的表面浮雕光栅

表面浮雕光栅是许多光学系统中的关键组件&#xff0c;在控制增强现实 &#xff08;AR&#xff09; 显示器、平视显示器 &#xff08;HUD&#xff09; 和其他先进光子器件中的光传播方面发挥着关键作用。作为在这个领域工作的工程师和设计师&#xff0c;您了解针对特定应用优化这…...

【今日分享】人工智能加速发现能源新材料的结构与性能

人工智能与材料国际学术会议(ICAIM)workshop9是由来自宁夏大学材料与新能源学院副院长王海龙教授及马薇副教授、杜鑫老师组成&#xff0c;他们将以“人工智能加速发现新能源新材料的结构与性能”为主题开展研讨工作&#xff0c;欢迎对该主题感兴趣的专家学者携稿加入。 loadin…...

Boost Asio TCP异步服务端和客户端

服务端 消息分两次发送&#xff0c;第一次发送head&#xff0c;第二次发送body。接收也是先接收head&#xff0c;然后通过head结构中的body长度字段再接收body。 TcpServer.h #pragma once #include <atomic> #include <vector> #include <unordered_set> #…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章

用 Rust 重写 Linux 内核模块实战&#xff1a;迈向安全内核的新篇章 ​​摘要&#xff1a;​​ 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言&#xff0c;受限于 C 语言本身的内存安全和并发安全问题&#xff0c;开发复杂模块极易引入难以…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

MyBatis-Plus 常用条件构造方法

1.常用条件方法 方法 说明eq等于 ne不等于 <>gt大于 >ge大于等于 >lt小于 <le小于等于 <betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2likeLIKE %值%notLikeNOT LIKE %值%likeLeftLIKE %值likeRightLIKE 值%isNull字段 IS NULLisNotNull字段…...