unordered_map与unordered_set的实现(含迭代器)

unordered_map与unordered_set的实现


文章目录

  • unordered_map与unordered_set的实现
  • 前言
  • 一、问题一
    • HashTable.h
  • 二、问题二&问题三
    • 1.封装时如何取出key
    • 2.不同类型key如何建立对应关系
  • 三、问题四&问题五
    • 问题四
    • 问题五
  • 四、实现代码
    • MyUnorderedSet.h
    • MyUnorderedMap.h
    • HashTable.h


前言

在C++11中新增了两个很好用的容器,分别是unordered_map与unordered_set,和map和set有不同之处

map与set的底层实现是平衡树——红黑树,并且采取中序遍历时默认有序

而本文的unordered_map与unordered_set底层实现是哈希思想(拉链法),并且他的存储方式不支持排序,所以是unordered

两者的查找效率综合比较,会发现unordered_map与unordered_set的效率综合会更高,所以在C++11中支持了unordered_map与unordered_set

本文中是用哈希桶的拉链法来封装unordered_map与unordered_set
这里的封装与红黑树封装map和set的封装相似,但此处会更难

具体通过解决问题来实现

  1. HashTable的迭代器实现
  2. 封装时HashTable如何取出map的key和set的key
  3. 取出key后如何针对不同类型key建立映射关系
  4. 如何解决Set中key不能被修改,Map中key不能被修改,value能被修改的问题
  5. Insert插入返回值问题以及Map[]的重载实现

关于哈希思想及其具体实现细节看我的上篇文章:数据结构之哈希表


一、问题一

这里就一步步,先实现iterator再实现const_iterator版本了,而是直接放出能适配iterator与const_iterator的版本,本质上就是用类模板泛型编程,需要什么就调用什么,是什么类型就返回什么类型的迭代器

这里尤为注意的一点是,这里的迭代器是单向迭代器,只支持++,而由于底层是哈希表的拉链法实现的,是数组与链表结合的方式
在实现运算符重载++时,本质上就是在逐个遍历哈希桶,而当前桶走完的时候,需要进入下一个桶,那么如何判断当前桶的位置,以及如何找到下一个桶,就需要把这个数组或者整个哈希表传过来,这里我们做的是把整个哈希表传过来

注意:其实将数组传过来会更简单些,传哈希表会有一些问题

  1. 我们将哈希表传过来,是可能要访问哈希表内的私有变量来获得下一个桶,而直接在_HTIterator这个类内使用哈希表内的私有变量是不可取的,所以需要在哈希表内声明友元
  2. 此处还涉及编译问题,由于编译器是从上往下编译代码,我们将迭代器写在哈希表代码的上面,而迭代器中有哈希表,这里编译器并不认识哈希表,因为哈希表的定义还未出现,所以还需要哈希表对应的类的声明
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>friend struct _HTIterator;
	template<class K, class T, class KeyOfT, class Hash>class HashTable;

至于class KeyOfT 与 class Hash这两个类模板的作用,则在下文中解答

HashTable.h

namespace hash_bucket
{template<class T>struct HashNode{HashNode(const T& data):_data(data),_next(nullptr){}T _data;HashNode* _next;};template<class K, class T, class KeyOfT, class Hash>class HashTable;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>struct _HTIterator{typedef HashNode<T> Node;typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;_HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}_HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht):_node(node), _pht(pht){}_HTIterator(const iterator& x):_node(x._node), _pht(x._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self operator++(){Hash hf;KeyOfT kot;size_t hashi = hf(kot(_node->_data)) % _pht->_t.size();if (_node->_next != nullptr){_node = _node->_next;}else{hashi++;while (hashi < _pht->_t.size()){if (_pht->_t[hashi]){_node = _pht->_t[hashi];break;}hashi++;}}if (hashi == _pht->_t.size()){_node = nullptr;}return *this;}bool operator==(const Self& it){return _node == it._node;}bool operator!=(const Self& it){return _node != it._node;}const HashTable<K, T, KeyOfT, Hash>* _pht;Node* _node;};template<class K, class T, class KeyOfT, class Hash>class HashTable{typedef HashNode<T> Node;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>friend struct _HTIterator;public:HashTable(size_t n = 10){_t.resize(n);}typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef _HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){size_t hashi = 0;while (hashi < _t.size()){if (_t[hashi]){break;}hashi++;}if (hashi == _t.size()){return iterator(nullptr, this);}return iterator(_t[hashi], this);}iterator end(){return iterator(nullptr, this);}const_iterator begin()const{size_t hashi = 0;while (hashi < _t.size()){if (_t[hashi]){break;}hashi++;}if (hashi == _t.size()){return iterator(nullptr, this);}return iterator(_t[hashi], this);}const_iterator end()const{return iterator(nullptr, this);}pair<iterator,bool> Insert(const T& data){KeyOfT kot;Hash hf;iterator ret = Find(kot(data));if (ret != end()){return make_pair(ret, false);}//扩容if (_n / _t.size() == 1){size_t newsize = _t.size() * 2;HashTable newtable(newsize);for (int i = 0; i < _n; i++){Node* cur = _t[i];while (cur){Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newsize;cur->_next = newtable._t[hashi];newtable._t[hashi] = cur;cur = next;}_t[i] = nullptr;}swap(_t, newtable._t);}size_t  hashi = hf(kot(data)) % _t.size();Node* newnode = new Node(data);newnode->_next = _t[hashi];_t[hashi] = newnode;_n++;return make_pair(iterator(newnode, this), true);}iterator Find(const K& key){Hash hf;KeyOfT kot;size_t hashi = hf(key) % _t.size();Node* cur = _t[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return iterator(nullptr, this);}bool Erase(const K& key){Hash hf;KeyOfT kot;size_t hashi = hf(key) % _t.size();Node* cur = _t[hashi];Node* prev = nullptr;if (kot(cur->_data) == key){_t[hashi] = cur->_next;delete cur;return true;}while (cur){if (kot(cur->_data) == key){prev->_next = cur->_next;delete cur;_n--;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<HashNode<T>*> _t;size_t _n = 0;};
}

二、问题二&问题三

1.封装时如何取出key

首先解释一下问题,我们的目的是将unordered_map与unordered_set用哈希表封装实现,map中存的是pair,set中存的是key,而如何用一份哈希表适配两种结构呢

在封装的时候解决这个问题,在unordered_map与unordered_set中写一个内部类,这个类之中实现了一个仿函数,用来返回key,并且将其传给哈希表内

	template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct KeyOfT{const K& operator()(const pair<K, V>& kv){return  kv.first;}};private:hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;};
template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct KeyOfT{const K& operator()(const K& key){return  key;}};private:hash_bucket::HashTable<K, K, KeyOfT, Hash> _ht;};

2.不同类型key如何建立对应关系

本文的拉链法中,使用的哈希函数是除留余数法,如果key是int类型的话那正好,可以直接用key除,但如果key是string或者自定义类型,那么就不能够直接除了,则需要将其转换成int类型,另外一个模板参数Hash,则是将其转换方式传给哈希表

如果是内置类型的float,double之类的,我们可以直接强转成size_t返回
如果是string类型,由于string比较常用,我们可以为string搞个特化,默认支持string

上面两个都是默认支持的,用默认缺省值就行,不需要手动传Hash

而如果是自定义类型,则需要使用者通过接口手动传Hash,因为默认的缺省值用不了

template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};template<>
struct HashFunc<string>
{size_t operator()(const string& key){size_t hash = 0;for (auto e : key){hash *= 13;hash += e;}return hash;}
};

三、问题四&问题五

问题四与问题五与set与map用红黑树封装的问题相同

问题四

set的iterator和const_iterator都是红黑树的const_iterator复用而来
map中的iterator是红黑树的iterator复用而来,const_iterator是红黑树的const_iterator复用而来
既然set中的迭代器都是const_iterator所以key自然不能被修改

	typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator const_iterator;

map解决key不能被修改,value能被修改的原理也很简单,就是在实例化的时候,声明第二个模板参数——在map中也就是pair,pair的first是const类型

	private:hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;

问题五

unordered_map与unordered_set与map和set的红黑树封装相同,insert的返回值都是一个键值对
在这里插入图片描述

first是一个迭代器,second是一个bool类型

基于此性质,引出了map的计数功能,可以通过insert返回的迭代器查看是否有key值,如果不存在则插入,将value值赋值为1,如果key已经存在,则通过insert返回的迭代器将value++,以此实现计数功能,所以map实现了operator[],用来计数

	V& operator[](const K& key){return _ht.Insert(make_pair(key, V())).first->second;}

四、实现代码

MyUnorderedSet.h

#include "HashTable.h"namespace Tlzns
{template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct KeyOfT{const K& operator()(const K& key){return  key;}};typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator const_iterator;iterator begin()const{return _ht.begin();}iterator end()const{return _ht.end();}pair<iterator, bool> Insert(const K& key){auto ret = _ht.Insert(key);return pair<iterator, bool>(iterator(ret.first._node, ret.first._pht), ret.second);}bool Erase(const K& key){return _ht.Erase(key);}iterator Find(const K& key){return _ht.Find(key);}private:hash_bucket::HashTable<K, K, KeyOfT, Hash> _ht;};void test_set(){unordered_set<int> us;us.Insert(5);us.Insert(15);us.Insert(52);us.Insert(3);unordered_set<int>::iterator it = us.begin();while (it != us.end()){//*it += 5;cout << *it << " ";++it;}cout << endl;for (auto e : us){cout << e << " ";}cout << endl;}
}

MyUnorderedMap.h

#include "HashTable.h"namespace Tlzns
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct KeyOfT{const K& operator()(const pair<K, V>& kv){return  kv.first;}};typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}pair<iterator, bool> Insert(const pair<K, V>& kv){return _ht.Insert(kv);}bool Erase(const K& key){return _ht.Erase(key);}iterator Find(const K& key){return _ht.Find(key);}V& operator[](const K& key){return _ht.Insert(make_pair(key, V())).first->second;}private:hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;};void test_map(){unordered_map<string, string> dict;dict.Insert(make_pair("sort", ""));dict.Insert(make_pair("string", ""));dict.Insert(make_pair("insert", ""));unordered_map<string, string>::const_iterator it = dict.begin();for (auto& kv : dict){//kv.first += 'x';kv.second += 'x';cout << kv.first << ":" << kv.second << endl;}cout << endl;string arr[] = { "香蕉", "甜瓜","苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };unordered_map<string, int> count_map;for (auto& e : arr){count_map[e]++;}for (auto& kv : count_map){cout << kv.first << ":" << kv.second << endl;}cout << endl;}
}

HashTable.h

#include <iostream>
#include <vector>using namespace std;template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};template<>
struct HashFunc<string>
{size_t operator()(const string& key){size_t hash = 0;for (auto e : key){hash *= 13;hash += e;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{HashNode(const T& data):_data(data),_next(nullptr){}T _data;HashNode* _next;};template<class K, class T, class KeyOfT, class Hash>class HashTable;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>struct _HTIterator{typedef HashNode<T> Node;typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;_HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}_HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht):_node(node), _pht(pht){}_HTIterator(const iterator& x):_node(x._node), _pht(x._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self operator++(){Hash hf;KeyOfT kot;size_t hashi = hf(kot(_node->_data)) % _pht->_t.size();if (_node->_next != nullptr){_node = _node->_next;}else{hashi++;while (hashi < _pht->_t.size()){if (_pht->_t[hashi]){_node = _pht->_t[hashi];break;}hashi++;}}if (hashi == _pht->_t.size()){_node = nullptr;}return *this;}bool operator==(const Self& it){return _node == it._node;}bool operator!=(const Self& it){return _node != it._node;}const HashTable<K, T, KeyOfT, Hash>* _pht;Node* _node;};template<class K, class T, class KeyOfT, class Hash>class HashTable{typedef HashNode<T> Node;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>friend struct _HTIterator;public:HashTable(size_t n = 10){_t.resize(n);}typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef _HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){size_t hashi = 0;while (hashi < _t.size()){if (_t[hashi]){break;}hashi++;}if (hashi == _t.size()){return iterator(nullptr, this);}return iterator(_t[hashi], this);}iterator end(){return iterator(nullptr, this);}const_iterator begin()const{size_t hashi = 0;while (hashi < _t.size()){if (_t[hashi]){break;}hashi++;}if (hashi == _t.size()){return iterator(nullptr, this);}return iterator(_t[hashi], this);}const_iterator end()const{return iterator(nullptr, this);}pair<iterator,bool> Insert(const T& data){KeyOfT kot;Hash hf;iterator ret = Find(kot(data));if (ret != end()){return make_pair(ret, false);}//扩容if (_n / _t.size() == 1){size_t newsize = _t.size() * 2;HashTable newtable(newsize);for (int i = 0; i < _n; i++){Node* cur = _t[i];while (cur){Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newsize;cur->_next = newtable._t[hashi];newtable._t[hashi] = cur;cur = next;}_t[i] = nullptr;}swap(_t, newtable._t);}size_t  hashi = hf(kot(data)) % _t.size();Node* newnode = new Node(data);newnode->_next = _t[hashi];_t[hashi] = newnode;_n++;return make_pair(iterator(newnode, this), true);}iterator Find(const K& key){Hash hf;KeyOfT kot;size_t hashi = hf(key) % _t.size();Node* cur = _t[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return iterator(nullptr, this);}bool Erase(const K& key){Hash hf;KeyOfT kot;size_t hashi = hf(key) % _t.size();Node* cur = _t[hashi];Node* prev = nullptr;if (kot(cur->_data) == key){_t[hashi] = cur->_next;delete cur;return true;}while (cur){if (kot(cur->_data) == key){prev->_next = cur->_next;delete cur;_n--;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<HashNode<T>*> _t;size_t _n = 0;};
}

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

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

相关文章

WebGL笔记:矩阵缩放的数学原理和实现

矩阵缩放的数学原理 和平移一样&#xff0c;以同样的原理&#xff0c;也可以理解缩放矩阵让向量OA基于原点进行缩放 x方向上缩放&#xff1a;sxy方向上缩放&#xff1a;syz方向上缩放&#xff1a;sz 最终得到向量OB 矩阵缩放的应用 比如我要让顶点在x轴向缩放2&#xff0c;y轴…

python爬虫AES魔改案例:某音乐素材下载网

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行 atob(‘aHR0cHM6Ly93d3cuYWlnZWkuY29tL3NvdW5kL2NsYXNzLw’) 拿到网址&#xff0c;F12打开调…

html动漫网页设计分享 紫罗兰永恒花园网页作业成品带视频,注册登录,表格,表单

html5静态网页设计要是用HTML DIVCSS JS等来完成页面的排版设计,一般的网页作业需要融入以下知识点&#xff1a;div布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点&#xff0c;学生网页作业源码可以…

数据挖掘实战-基于word2vec的短文本情感分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

鸿蒙基础入门与高频知识点梳理

介绍鸿蒙高频知识点&#xff0c;持续更新中 一、鸿蒙代码结构 ├──entry/src/main/ets // 代码区 │ ├──common │ │ └──Constant.ets // 常量类 │ ├──entryability │ │ └──EntryAbility.ts // 程序入口类 │ ├──p…

【算法】单调队列 滑动窗口最大值

文章目录 例题——239. 滑动窗口最大值相关练习1438. 绝对差不超过限制的最长连续子数组解法1——两个单调队列分别维护最大值和最小值解法2——有序集合TreeMap 2398. 预算内的最多机器人数目解法1——二分答案 单调队列解法2——双指针 单调队列 &#xff08;不固定大小的滑…

并发编程1:线程的基本概念

一、进程、线程、用户线程&原生线程、优先级、守护线程 什么是进程 是程序一次执行的过程&#xff0c;是系统运行程序的基本单位。系统运行一次程序&#xff0c;就是一个进程从创建到关闭的过程。Java 项目从 main 方法启动&#xff0c;就是启动了一个 JVM 进程&#xff…

C++面试宝典第1题:爬楼梯

题目 小乐爬楼梯&#xff0c;一次只能上1级或者2级台阶。楼梯一共有n级台阶&#xff0c;请问总共有多少种方法可以爬上楼&#xff1f; 解析 这道题虽然是一道编程题&#xff0c;但实际上更是一道数学题&#xff0c;着重考察应聘者的逻辑思维能力和分析解决问题的能力。 当楼梯只…

理解Zookeper

一、什么是Zookeper Zookeper是一个可以作为注册中心、配置中心、分布式锁的分布式解决方案 二、数据一致性 一致性分为强一致性、弱一致性、最终一致性 Zookeper可以保持数据的强一致性&#xff08;CP&#xff09; 主要是解决写、集群节点的同步、读之间的关系 强一致性&a…

nodejs微信小程序+python+PHP问卷调查系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

This详细用法

this的指向 this有5种指向&#xff0c;在普通函数中&#xff0c;this指向window&#xff1b;在构造函数中&#xff0c;this指向创建的对象&#xff1b;在方法声明中&#xff0c;this指向调用者&#xff1b;在定时器中&#xff0c; this指向window&#xff1b;在事件中&#xff…

超大规模集成电路设计----MOS器件原理(二)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----MOS器件原理&#xff08;二&#xff09; 半导体物理知识补充介绍1. 半导体材料2. 固体类型…

RK3568平台开发系列讲解(Linux系统篇)通过OF函数获取属性

🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过OF函数获取属性。 一、获取获取属性 ① of_find_property 函数 of_find_property 函数用于在设备树中查找节点 下具有指定名称的属性。 如果找到了该属性, 可以通过返回的属性结构体…

Shell循环:expect(二)

expect实战&#xff1a;公钥推送 一、准备工作&#xff1a;安装expect&#xff0c;装备公钥 二、通过shell循环判断主机在线 #!/bin/bash #脚本编写 #创建一个IP地址文件 >ip.txt #使用for循环ping测试主机是否在线 for i in {3..254} do{ip192.168.151.$iping -c1 -W…

【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

目录 一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1 访问限定符4.2 封装 五、类的作用域六、类的实例化七、类的对象大小的计算7.1 类对象的存储方式猜测7.2 如何计算类对象的大小 八、类成员函数的this指针8.1 this指针的引出8.2 this指…

【算法】单调栈题单(矩阵系列、字典序最小、贡献法)⭐

文章目录 题单来源经典题单496. 下一个更大元素 I&#xff08;单调栈模板题&#xff09;503. 下一个更大元素 II&#xff08;单调栈循环数组&#xff09;2454. 下一个更大元素 IV&#xff08;第二个更大的元素&#xff1a;两个单调栈&#xff09;456. 132 模式&#xff08;单调…

操作系统||经典进程同步问题之读者-写者问题

一个数据文件或记录可被多个进程共享 【问题描述】 有读者和写者两组并发进程&#xff0c;共享一个文件&#xff0c;当两个或两个以上的读进程同时访问共享数据时不会产生副作用&#xff0c;但若有某个写进程和其他进程&#xff08;读进程或写进程&#xff09;同时访问共享数…

Autosar COM通信PDU

文章目录 Autosar 中各个PDU所在示意图PDU的分类PDU 和 SDU 的关系I-PDUN-PDUL-PDU相关协议其他参考 Autosar 中各个PDU所在示意图 PDU的分类 在Autosar 中&#xff0c;主要有 I-PDU、N-PDU和 L-PDU 三种。 L-PDU&#xff1a;Data Link Layer PDU&#xff0c;数据链路层PDUN-…

[C/C++]数据结构 关于二叉树的OJ题(利用分治思想解决难题)

题目一: 单值二叉树 &#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; &#x1f6a9;题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 ⛲题目描述: 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。…

制作飞腾(arm)芯片架构的nexus镜像

nexus官方没有arm架构的镜像&#xff0c;下面介绍一种自己制作镜像的方式 1、事先准备 在一个arm架构机器上安装docker下载nexus的linux版(https://www.sonatype.com/download-oss-sonatype)下载centos的arm架构镜像(docker pull centos-centos8.4.2105)下载arm版本的java8(ht…