当前位置: 首页 > 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> #…...

1.7 ChatGPT:引领AI对话革命的致胜之道

ChatGPT:引领AI对话革命的致胜之道 随着人工智能(AI)技术的迅猛发展,特别是在自然语言处理(NLP)领域,OpenAI 的 ChatGPT 已经成为了举世瞩目的技术突破。从普通的自动化客服到深入的创作与协作,ChatGPT 通过其卓越的语言理解和生成能力,改变了人们与计算机交互的方式…...

WPS数据分析000001

目录 一、表格的新建、保存、协作和分享 新建 保存 协作 二、认识WPS表格界面 三、认识WPS表格选项卡 开始选项卡 插入选项卡 页面布局选项卡 公式选项卡 数据选项卡 审阅选项卡 视图选项卡 会员专享选项卡 一、表格的新建、保存、协作和分享 新建 ctrlN------…...

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一&#xff0c;它的作用是为电脑的各个部件提供冷却&#xff0c;防止电脑过热。然而&#xff0c;有时候我们会发现电脑风扇的声音特别大&#xff0c;不仅影响我们的使用体验&#xff0c;也可能是电脑出现了一些问题。那么&#xff0c;电脑风扇声音大…...

高效实现 Markdown 转 PDF 的跨平台指南20250117

高效实现 Markdown 转 PDF 的跨平台指南 引言 Markdown 文件以其轻量化和灵活性受到开发者和技术写作者的青睐&#xff0c;但如何将其转换为易于分享和打印的 PDF 格式&#xff0c;是一个常见需求。本文整合了 macOS、Windows 和 Linux 三大平台的转换方法&#xff0c;并探讨…...

Spark Streaming的核心功能及其示例PySpark代码

Spark Streaming是Apache Spark中用于实时流数据处理的模块。以下是一些常见功能的实用PySpark代码示例&#xff1a; 基础流处理&#xff1a;从TCP套接字读取数据并统计单词数量 from pyspark import SparkContext from pyspark.streaming import StreamingContext# 创建Spar…...

自动驾驶占用网格预测

文章目录 需要阅读的文献&#xff1a;github论文仓库论文idea提取BEVFormer 需要阅读的文献&#xff1a; ⭐[ECCV 2024] SparseOcc 纯稀疏3D占用网络和 RayIoU 评估指标 ECCV 2024&#xff5c;OSP&#xff1a;自动驾驶全新建模方法&#xff0c;端到端输出任意位置的占用结果 S…...

力扣动态规划-2【算法学习day.96】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…...

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介&#xff1a;网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力&#xff1b;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介&#xff1a;系统分…...

2.5 如何评估表示学习

如何评估表示学习 评估表示学习的质量和有效性是确保模型能够成功应用于实际任务的关键步骤。表示学习的目标是从数据中学习到一种有效的、低维的表示,使得下游任务(如分类、回归、聚类等)能够更好地执行。因此,评估表示学习的效果涉及多个维度,包括表示的质量、其对下游…...

Linux-day08

第17章 大数据定制篇-shell编程 shell编程快速入门 shell变量 设置环境变量 把行号打开 set nu 位置参数变量 预定义变量 在一个脚本中执行了另外一个脚本所以卡住了 CTRLC退出 运算符 operator运算符 条件判断 流程控制 单分支多分支 case语句 for循环 反复的把取出来的i值…...

室内设计工作室排名/电商运营seo

有时候导入一些module时&#xff0c;会出现以下问题 Android dependency com.android.support:support-v4 has different version for the compile (23.3.0) and runtime (25.4.0) classpath. You should manually set the same version via DependencyResolution 1这是因为mod…...

龙岩长汀最新疫情/神马seo教程

怎么把本地项目推送到GitHub或者码云线上呢&#xff1f;来来回回错了很多次&#xff0c;终于总结了正确的步骤。首先注册好github账号&#xff0c;或者码云账号。1.然后建一个自己的本地仓&#xff0c;你可以直接右击新建文件夹&#xff0c;也可以右击打开Git bash命令行窗口通…...

梧州网站建设推广/怎么做网站优化

1.查看有无安装过mysqlrpm -qa|grep mysql2.查看有无安装包yum list mysql*3.虚拟机关掉eth0&#xff0c;联网后&#xff0c;安装mysql服务yum install mysql-serveryum install mysql-devel4.开启eth0&#xff0c;Xshell连接后&#xff0c;启动&&停止服务(1)在mysql配…...

国外网购网站/湘潭网站seo

虚拟机在它的生命周期有以下几种状态&#xff1a; undefined: 这个状态代表着虚拟机没有创建或者没有在libvirt中定义。Defined/Shutoff: 这个状态代表虚拟机定义了&#xff0c;并且在/etc/libvirt/qemu中有效的&#xff0c;我们可以把这个状态叫做停止或关闭状态。Running: 代…...

金华哪里有做网站的公司4000-262-/前端开发培训机构推荐

下载的同时记录下载次数&#xff0c;不能用超链接直接下载&#xff0c;而应该用超链接跳到一个aspx中间页 面&#xff0c;并在这个页面的CS文件中Page_Load方法中&#xff0c;用Response.Redirect("url");指向下载的文件。这样点击下载后&#xff0c;客户看 到的一直…...

关键词优化除了做网站还有什么方法/app怎么推广

什么是B*树倒排索引技术 - 已解决 - 搜搜问问B*树索引是“传统索引”。到目前为止&#xff0c;这是Oracle和大多数其他数据库中最常用的索引。需要注意的是&#xff0c;这里的“B”不代表二叉(binary),而是代表平衡(balanced).B*树索引并不是一颗二叉树。但是&#xff0c;其实现…...