【C++】list 相关接口的模拟实现
list 模拟实现
- 回顾
- 准备
- 构造析构函数的构造
- 构造方法
- 析构方法
- 赋值运算符重载
- 容量相关接口
- 元素获取
- 元素修改相关接口
- push 、pop
- insert
- erase
- 清空
- 交换
- 迭代器 **(重点)
- 迭代器基本概念
- 迭代器模拟实现
回顾
在上一篇博客中我们大致了解了 list 相关接口的使用方法并进行了一系列的测试练习,那么这一小节就来模拟实现一些接口吧~
list 是一个带头双向循环的链表:
在模拟实现这些接口之前我们需要先进行节点信息的创建
template<class T> //采用模板------可以定义不同数据类型的 list 链表struct ListNode {ListNode(const T&value = T()) //构造方法:prev(nullptr), next(nullptr), val(value){}ListNode* prev; //前驱节点ListNode* next; //后继节点T val; //值域};
准备
为了与类中 list 进行区分,我们可以自定义一个命名空间,在该命名空间内部来进行接口的模拟实现:
namespace xx {template<class T>struct ListNode //创建节点信息{ListNode(const T& value = T()):prev(nullptr), next(nullptr), val(value){}ListNode* prev;ListNode* next;T val;};
}
为了便于测试,我们在自己的命名空间中定义一个打印信息的函数:
template<class T>void PrintList(const list<T>& L){for (auto e : L)cout << e << " ";cout << endl;}
定义链表信息:
//定义链表信息template<class T>class List {typedef ListNode<T> Node; //取别名public://构造方法//析构方法//接口模拟实现private:Node* _head; //头节点};
构造析构函数的构造
构造方法
由于 list 链表是一个带头的双向循环链表,并且已知 _head 头节点,因此在创建链表时候要注意 prev 与 next 的指向
创建任何类型的构造方法之前,我们首先要定义出一个带头结点的空链表:
void CreateList(){//创建头节点_head = new Node();_head->next = _head->prev = _head; //构造循环}
(1)构造空链表
创建一个空链表也就是创建一个只有头节点的链表,因此我们可以直接在构造方法内部调用头节点创建的函数即可:
List() {CreateList();}
(2)构造具有 n 个值为 val 的链表
构造一个具有 n 个相同节点的链表,我们可以复用尾插(或头插都可以)来进行
List(int n, const T& val = T()){CreateList(); //首先创建头节点信息//构造 n 个值为 val 的节点,我们可以采用 n 次尾插来进行for (int i = 0; i < n; ++i) {push_back(val);}}
注意:
(3)区间构造
template<class Iterator>List(Iterator first, Iterator last){CreateList(); //创建头节点auto it = first;while (it != last) {push_back(*it); //同样采用多次尾插方法来构造,*it 代表获取节点值域++it; //迭代器 it 的自增------表示获取下一个节点的位置}}
注意:
在进行参数构造时候,我们参数类型定义均为模板类型 Iterator ,因此对于上述构造 n 个值相同的节点信息的链表,倘若我们使用 size_t 类型来传入 n 参数,会使得 编译器在进行类型推演时候默认为两个变量类型不一致导致编译器调用错误,这也就解释了为什么将 n 变量类型定义为 int 的原因。
(4)拷贝构造
用一个已有的链表来创建新的链表信息:
List(const List<T>& L){CreateList(); //创建头节点for (auto e : L) {push_back(e); //遍历 L 链表同时将遍历到的节点值插入到 新构造的链表中}}
析构方法
~List(){clear(); //清空所有节点信息delete _head; //删除头节点_head = nullptr;}
赋值运算符重载
我们采用现代版的写法,传递的参数为值类型的参数(会调用一次拷贝构造函数来构造出该参数),然后将该参数与 this 指针指向的内容进行交换即可(可以参考深浅拷贝 : 添加链接描述):
List<T>& operator=(const List<T> L) //以值的方式传参会调用一次拷贝构造方法来创建参数(具有自己独立的地址空间),并在函数调用结束自动销毁临时空间{this->swap(L); //交换之后,L 中空间改变为原来的 this 空间,并在函数调用结束自动进行了析构销毁return *this; }
容量相关接口
由于 list 链表结构为带头双向循环链表,我们只知道 头节点 _head 的信息,因此在进行容量判断时候需要对整个链表进行遍历------注意遍历条件!!!
(1)size 接口
size_t size()const{//统计节点个数int count = 0;Node* cur = _head->next;while (cur != _head) { //循环链表的 next 指针域一定是不为空的,因此遍历条件应该是判断是否回到头节点位置++count;cur = cur->next;}return count;}
(2)判空
循环链表为空时,也就是只有一个头节点存在,故判断条件应为:
bool empty()const{return _head == _head->next;}
(3)resize 修改有效节点的个数
当缩小有效节点个数为 newsize 时,我们需要将 newsize 之后的节点进行删除;
当扩大有效节点个数为 newsize 时,我们需要在原有的节点尾部插入 newsize-oldsize 个新的节点,且新节点的值为 val ;
void resize(size_t newsize,const T& val=T()){size_t oldsize = size(); //统计现有的节点个数//当缩小节点if (newsize < oldsize) {for (int i = newsize; i < oldsize; ++i)pop_back(); //进行尾删}else {//增大节点个数for (int i = oldsize; i < newsize; ++i)push_back(val); //进行尾插操作}}
元素获取
(1)获取首节点信息
//非 const 类型表示可以对节点信息进行修改操作T& front(){//获取首节点值return _head->next->val; //_head 为头节点,它所存储的数据不是有效数据}const T& front()const //只读{//获取首节点值return _head->next->val; //_head 为头节点,它所存储的数据不是有效数据}
(2)获取尾节点信息
T& back(){return _head->prev->val; //_head 为头节点,它的前驱节点为尾节点}const T& back()const{return _head->prev->val; //_head 为头节点,它的前驱节点为尾节点}
元素修改相关接口
push 、pop
由于头部或尾部插入新元素都可以直接复用 insert 任意位置插入方法,因此我们这里直接调用 insert 接口来实现:
void push_front(const T& val){insert(begin(), val); //begin() 指向首节点,因此进行头插 ,直接在第一个节点前插入新节点 }void push_back(const T& val){insert(end(), val); //end() 指向头节点,因此进行尾插,直接在 end() 之前插入新节点}
由于头部或尾部删除之间可以调用 erase 任意位置删除,所有这里也直接调用erase 接口:
void pop_front(){if (empty())return; //链表为空,不能进行删除//头删------删除首节点erase(begin()); //删除 begin() 位置节点}
void pop_back(){if (empty())return; //链表为空,不能进行删除//尾删------删除最后一个节点erase(end()); }
注意看这两段代码是否有问题?
能这么问,当然是有问题啦~
具体什么问题我们来看看:
仔细观察 begin() 与 end() 的位置,我们发现 begin() 指向的就是第一个节点的位置,因此进行头删时候直接可以进行删除,并且删除之后并不会影响之后元素的访问
而 end() 指向的是头节点的位置,而我们要删除的是最后一个有效节点,也就是 end() 的前一个节点位置,因此此处的尾删函数的实现是不对的,具体修改如下:
void pop_back(){if (empty())return; //链表为空,不能进行删除//尾删------删除最后一个节点auto pos = end();--pos; //迭代器向前移动到尾节点位置erase(pos); //删除 end() 前一个位置节点}
list 接口测试中,我们谈到 在插入节点时不会导致迭代器的失效,而在删除元素时候会引发迭代器失效,但是不会导致迭代器位置之后的元素的访问,因此一般在删除节点操作之后我们会接收返回值的信息来防止迭代器失效。
(list 接口使用中我们已经进行了测试,忘记的宝子参考:添加链接描述)
insert
在任意位置进行元素的插入,首先我们需要创建出一个新节点信息,然后对节点的指向进行修改即可:
Iterator insert(Iterator pos, const T& val){//在给定的节点位置 pos 之前进行新节点的插入Node* newnode = new Node(); //创建新节点newnode->val = val;newnode->next = pos;newnode->prev = pos->prev;pos->prev->next = newnode;pos->prev = newnode;return newnode; //返回新插入的节点位置}
画个图来理解一些吧~
erase
Iterator erase(Iterator pos){//删除给定的 pos 位置的节点if (pos == _head)return _head; //头节点不能进行删除Node* cur = pos->next; //记录下一个节点位置//修改指向cur->prev = pos->prev;pos->prev->next = cur;delete pos;return cur; //返回删除节点的位置-------此时迭代器 pos 已经失效了}
删除节点与插入节点过程很类似,注意修改指向的顺序,读者可以自己画画图来理解
清空
clear 是将 list 链表中所有节点进行删除,我们可以采用头删的方法来进行:
void clear(){Node* cur = _head->next; //进行头删while (cur != _head) {cur->next->prev = _head;_head->next = cur->next;delete cur;cur = _head->next; //修改要删除的节点位置}_head->next = _head->prev = _head; //最后删除头节点}
交换
void swap(List<T>& L) {std::swap(_head, L._head); //之间采用全局 swap 函数,交换头节点位置即可}
迭代器 **(重点)
迭代器基本概念
string 、vector 还有现在学习的 list 当中,我们都有使用到迭代器,那么迭代器到底是什么呢?
在前边的学习中我们提到 ,迭代器可以看作是原生态的指针类型,在模拟接口中我们发现,定义的迭代器变量我们可以对其进行以下操作:
1)解引用 *
2)自增自减
3)迭代器的比较
例如在遍历时我们使用到的迭代器(vector 容器下的迭代器):
因此,我们在模拟实现迭代器时候也要能够进行这三种基本操作。
接下来,我们来模拟实现一些迭代器吧~
迭代器模拟实现
在前边模拟实现 string 以及 vector 时,我们将迭代器处理为原生态的指针类型,发现在整个模拟接口测试过程中是没有任何问题的,说明在之前的模拟实现中 ,迭代器就是被当作指针来进行处理的,那么在 list 中我们是否也可以这么处理?
同样将迭代器看作是原生态的指针类型:
typedef Node* Iterator;
当我们进行迭代器的解引用以及自增自减:
auto it = begin();while (it!=end()){cout << *it << " ";++it;}cout << endl;
那么为什么在 string 和 vector 当中,迭代器可以被处理成为 原生态的指针并且可以正常使用,在这里就不行了呢?(大家可以思考思考)
解答
回顾我们在学习 string 和 vector 容器中,使用的是顺序结构,也就是说所采用的容器空间是连续的,因此进行自增自减可以直接获取到它相邻的前后元素位置
而在 list 中,我们知道 list 是多个节点构成的链表,而每一个节点都是在使用时才创建(new)出来的,然后将创建的新节点链接到我们的链表当中,由此可见 list 当中的结构并不一定连续的,故不能直接进行迭代器的自增自减操作;
其次,假如将迭代器定义为原生态的指针类型 Node* ,在进行解引用操作时候取到的类型是 Node 而并非是当前迭代器指向位置的值域(val)信息,因此迭代器不能被简单的处理为 原生态的指针;
再者,我们提到迭代器可以进行比较,而原生态指针类型定义出来的迭代器类型都是一致的,类型间如何进行比较?
由此可见,在 list 中模拟实现迭代器时,我们首先需要对迭代器进行封装操作:
正向迭代器的封装
//封装迭代器类-------------要求能够使用指针解引用,并能够进行自增自减操作,并能够进行迭代器的比较template<class T,class Ptr,class Ref>class ListIterator {typedef ListNode<T> Node;public:typedef Ptr Ptr; //类型重命名,指明当前 Ptr 是类型而非变量typedef Ref Ref;typedef ListIterator<T, Ptr, Ref> Self; //因为节点当中包含值域和指针类型,因此我们设计迭代器时需要能够返回不同的类型的数据信息public:ListIterator(Node* pNode = nullptr) :_pNode(pNode){}// 解引用Ref operator*() //返回值信息应该是节点当中的数据类型{return _pNode->val; //解引用也就是获取当前节点中的值域信息}Ptr operator->() //返回值信息为当前节点数据域的地址{return &(_pNode->val); //在自定义类型中体现很明显}/// 自增自减Self& operator++() //返回值为迭代器自身类型{_pNode = _pNode->next; //自增操作也就是将迭代器的位置向后移动---------获取下一个位置的节点return *this;}Self operator++(int) //后置++{Self tmp(*this); //后置++:先使用,后对其进行修改_pNode = _pNode->next;return tmp;}Self& operator--(){_pNode = _pNode->prev; //自减操作也就是将迭代器向前移动--------获取前一个位置的节点return *this; }Self operator--(int) //后置--{Self tmp(*this);_pNode = _pNode->prev;return tmp;}迭代器能够比较bool operator!=(const Self& s)const{return _pNode != s._pNode;}bool operator==(const Self& s)const{return _pNode == s._pNode;}Node* _pNode;};
反向迭代器的封装
在前边我们介绍到,正向迭代器 begin() 的位置与反向迭代器 rend() 位置相同,正向迭代器 end() 位置与反向迭代器 rbegin() 位置相同,两种迭代器正好相反,因此进行反向迭代器封装我们可以复用正向迭代器的方法:
template<class Iterator>struct ListReverseIterator {//typename 是为了说明 Ref Ptr 是属于正向迭代器 Iterator 中的类型而不是静态成员变量typename typedef Iterator::Ref Ref;typename typedef Iterator::Ptr Ptr;typedef ListReverseIterator<Iterator> Self;public:ListReverseIterator(Iterator it) :_it(it){}Ref operator*(){Iterator tmp = _it; //rbegin() 指向 _head 头节点,不需要进行打印,因此 解引用 获取到的是第一个节点的值域,即需要将 rbegin 向后(++操作)移动-----------即需要将正向迭代器的 end() 向前移动(--操作)--tmp;return *tmp; //返回值是节点数据域中的数据类型}Ptr operator->() //获取当前节点数据域的地址信息{return &(_it->pNode->val);}Self& operator++() {--_it; //反向迭代器的前置++ 等价于 正向迭代器的前置--return *this;}Self operator++(int){_it--; //反向迭代器后置++ 等价于 正向迭代器的后置--return *this; }Self& operator--() {++_it; //反向迭代器的前置-- 等价于 正向迭代器的前置++return *this;}Self operator--(int){_it++; //反向迭代器后置-- 等价于 正向迭代器的后置++return *this;}///bool operator!=(const Self& s)const{return _it != s._it;}bool operator==(const Self& s)const{return _it == s._it;}Iterator _it;};
封装之后我们需要在我们自己定义的 List 类中进行声明:
typedef ListIterator<T,T*,T&> Iterator; //迭代器封装之后要能够返回不同的数据类型(节点值域,节点的地址等)typedef ListIterator<T, const T*, const T&> const_Iterator; //const 迭代器-----只读typedef ListReverseIterator<Iterator> reverse_iterator;typedef ListReverseIterator<const_Iterator> const_reverse_iterator; //const 迭代器-----只读
则迭代器的模拟接口实现如下:
Iterator begin() //begin() 获取首节点的位置{return Iterator(_head->next); //返回值类型的临时对象}Iterator end(){return Iterator(_head);}//反向迭代器reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}//const 迭代器const_Iterator begin()const{return const_Iterator(_head->next);}const_Iterator end()const{return const_Iterator(_head);}const_reverse_iterator rbegin()const{return const_reverse_iterator(end());}const_reverse_iterator rend()const{return const_reverse_iterator(begin());}
由于我们对迭代器进行了封装而并非原始方式定义为原生态的指针类型,因此采用迭代器来进行插入删除元素的函数也要进行相应的修改操作:
Iterator insert(Iterator Itpos, const T& val){//在 pos 位置之前进行插入Node* pos = Itpos._pNode; //此时的迭代器 Itpos 所指向的并不是当前节点的位置信息(而是包含三种数据结构的信息),因此需要进行取节点操作//后续的代码与前边是相同的Node* newnode = new Node();newnode->val = val;newnode->prev = pos->prev;newnode->next = pos;pos->prev->next = newnode;pos->prev = newnode;return newnode;}Iterator erase(Iterator Itpos){//删除 pos 位置的元素Node* pos = Itpos._pNode;//此时的迭代器 Itpos 所指向的并不是当前节点的位置信息,因此需要进行取节点操作//后续的代码与前边是相同if (pos == _head)return pos;Node* cur = pos->next;pos->prev->next = cur;cur->prev = pos->prev;delete pos;return cur;}
好了,今天的学习就到这里啦
对于迭代器部分的理解可能比较困难,读者可以自己在代码当中多调试调试看看
关于本节具体的代码实现请参考(mylist 文件):添加链接描述
有任何问题欢迎评论留言哦!
相关文章:
【C++】list 相关接口的模拟实现
list 模拟实现回顾准备构造析构函数的构造构造方法析构方法赋值运算符重载容量相关接口元素获取元素修改相关接口push 、popinserterase清空交换迭代器 **(重点)迭代器基本概念迭代器模拟实现回顾 在上一篇博客中我们大致了解了 list 相关接口的使用方法…...
快速找到外贸客户的9种方法(建议收藏)
所有外贸企业想要做好外贸出口的头等大事,就是要快速的找到优质的外贸客户和订单,没有订单的达成,所有的努力都是图劳,还有可能会陷入一种虚假的繁荣,每天都很忙,但是没有结果。今天,小编就来分…...
TCP状态转换
欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 TCP状态转换专栏:《Linux从小白到大神》《网络编程》 TCP状态转换示意图如下 针对上面的示…...
3500年里,印度被11个文明征服
转自:3500年里,印度被11个文明征服,如今看似统一,实际上却是缝合怪 (qq.com)今天的印度是亚洲第二大国,南亚第一大国,世界第二人口大国。如果我们将时间线拉长,纵观历史的长河,就会惊…...
Java编程问题top100---基础语法系列(一)
Java编程问题top100---基础语法系列一一、Java 操作符实质二、将InputStream转换为String使用IOUtils自己写轮子三、将数组转换为List四、如何遍历map对象使用For-Each迭代entries(方法一)使用For-Each迭代keys和values(方法二)使…...
【C#基础】C# 异常处理操作
序号系列文章6【C#基础】C# 常用语句讲解7【C#基础】C# 常用数据结构8【C#基础】C# 面向对象编程文章目录前言1,异常的概念2,处理异常3,自定义异常4,编译器异常结语前言 🌷大家好,我是writer桑,…...
系统分析师---操作系统思维导图
进程管理(5星) 进程与线程:共享:内存地址空间、代码、数据、文件等不能共享:独立的cpu运行上下文和栈指针、寄存器 信号量与PV操作:信号量,一种特殊的变量分为:信号量可以表示资源数…...
Linux | Ubuntu20.04系统使用命令从移动硬盘/U盘拷贝文件到服务器上
*确认自己移动硬盘、U盘的格式,本文为exfat格式STEP1:把移动硬盘插到Ubuntu系统的主机上查看disk默认位置#查看移动硬盘/U盘在哪个位置命令 fdisk -l #查询后出现了包含电脑系统的所有硬盘查看最后的位置,我的显示为Device, 位置为 /dev/sdb1…...
【经验总结】10年的嵌入式开发老手,到底是如何快速学习和使用RT-Thread的?
【经验总结】一位近10年的嵌入式开发老手,到底是如何快速学习和使用RT-Thread的? RT-Thread绝对可以称得上国内优秀且排名靠前的操作系统,在嵌入式IoT领域一直享有盛名。近些年,物联网产业的大热,更是直接将RT-Thread这…...
一起Talk Android吧(第五百零九回:约束布局中的组功能一)
文章目录功能介绍使用方法GroupLayer对比总结各位看官们大家好,上一回中咱们说的例子是"多层布局功能",这一回中咱们说的例子是"约束布局中的组功能"。闲话休提,言归正转, 让我们一起Talk Android吧! 功能介…...
2023安徽省“中银杯”职业技能大赛“网络安全” 项目比赛任务书
2023安徽省“中银杯”职业技能大赛“网络安全” 项目比赛任务书2023安徽省“中银杯”职业技能大赛“网络安全” 项目比赛任务书A模块基础设施设置/安全加固(200分)A-1:登录安全加固(Windows, Linux)A-2:Ngi…...
观测云产品更新|新增用户访问监测自动化追踪;新增 CDN 质量分析;新增自定义查看器导航菜单等
观测云更新 用户访问监测优化 新增用户访问监测自动化追踪 用户访问监测新增自动化追踪,通过“浏览器插件”的实现方式,使用浏览器记录用户访问行为,创建无代码的端到端测试。更多详情可参考文档【 自动化追踪 】https://docs.guance.com/…...
大数据技术生态全景一览
大数据技术生态全景一览大数据平台ETL数据接入大数据平台海量数据存储大数据平台通用计算大数据平台各场景的分析运算分布式协调服务任务流调度引擎大数据平台ETL数据接入 大数据有很多的产品,琳琅满目。从架构图上就能看出产品很多。这些产品它们各自的功能是什么…...
CI/CD | 深入研究Jenkins后,我挖掘出了找到了摆脱低效率低下的方法
在本系列的第一篇文章中,您已经了解了一些关于如何管理Jenkins的内容,主要是为无序的人带来秩序。在这篇文章中,我将更深入地探讨我效率低下的问题,提出我们工作流中一些安全性、治理和合规性的挑战。这不仅仅是你在网站上或展览横…...
刷LeetCode
文章目录滑动窗口算法1 涉及知识点 :unordered_set 容器2 参数详情3 例题滑动窗口算法 滑动的窗口,每次记录下窗口的状态,再找出符合条件的窗口使用滑动窗口减少时间复杂度 1 涉及知识点 :unordered_set 容器 说明:…...
Spring 大白话系列:工厂
Spring 大白话系列:工厂 “工厂模式,大家都很熟悉了。说到底,就是解除创建对象和使用对象之间的耦合。这东西没啥啊。” 教室里,老师傅听到小明在嘀嘀咕咕的。老师走过去问: “有什么问题呢小明同学?” 小…...
喜讯!华秋电子荣获第六届“蓝点奖”十佳分销商奖
2 月 25 日,由深圳市电子商会主办的2023 中国电子信息产业创新发展交流大会暨第六届蓝点奖颁奖盛典在深圳隆重举行。 图:华秋商城渠道总监杨阳(右三) 深圳市电子商会连续六年举办“蓝点奖”评选活动,旨在表彰对电子信…...
Linux概述
1:Linux概述1.1:操作系统常见操作系统有:Windows、MacOS、Linux。名称描述Windows微软公司研发的收费操作系统。分为两类:用户操作系统、Server操作系统。用户操作系统:win 95、win 98、win NT、win Me、win xp、vista…...
中级嵌入式系统设计师2015下半年上午试题及答案解析
中级嵌入式系统设计师2015下半年上午试题 单项选择题 1、CPU是在______结束时响应DMA请求的。 A.一条指令执行 B.一段程序 C.一个时钟周期 D.一个总线周期 2、虚拟存储体系由______两级存储器构成。 A.主存-辅存 B.寄存器-Cache C.寄存器-主存...
华为OD机试模拟题 用 C++ 实现 - 删除指定目录(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明删除指定目录题目输入输出示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为…...
【正点原子FPGA连载】第二十章AXI4接口之DDR读写实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第二十章AXI4接口…...
超出认知的数据压缩 用1-bit数据来表示32-bit的梯度 语音识别分布式机器学习 梯度压缩 论文精读
说明 介绍1−bit1-bit1−bit论文内容。 原文链接:1-bit stochastic gradient descent and its application to data-parallel distributed training of speech DNNs | Semantic Scholar ABS 实验证明在分布式机器学习的过程中能够通过将同步所传递的梯度进行量化…...
深度剖析指针(上)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是指针噢,在学习C语言的过程中,指针算是一个比较重要的内容,当然,难度也是比较大的,那么现在就让小雅兰来带大家进入指针的世界吧 字符指针 数组指针…...
学习 Python 之 Pygame 开发魂斗罗(六)
学习 Python 之 Pygame 开发魂斗罗(六)继续编写魂斗罗1. 创建碰撞类2. 给地图添加碰撞体3. 让人物可以掉下去4. 实现人物向下跳跃5. 完整的代码继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗(五)中,我…...
LeetCode题解:1238. 循环码排列,归纳法,详细注释
原题链接: https://leetcode.cn/problems/circular-permutation-in-binary-representation/ 前置条件: 在解题之前,请先一定要阅读89.格雷编码的题解格雷编码可以满足题目的条件“p[i] 和 p[i1] 的二进制表示形式只有一位不同”,…...
全新后门文件Nev-3.exe分析
一、 样本发现: 蜜罐 二、 内容简介: 通过公司的蜜罐告警发现一个Nev-3.exe可执行文件文件,对该样本文件进行分析发现,该可执行程序执行后会从远程服务器http://194.146.84.2:4395/下载一个名为“3”的压缩包,解压后…...
线性回归系数解释
线性回归系数解释线性回归系数1、R2R^2R2(R方,R-Square)2、Adj−R2Adj-R^2Adj−R2(调整后的 R 方)3、标准误差4、FFF 值5、FFF 显著度6、置信区间7、PPP 值线性回归系数 回归模型得到后会有多个系数,这些系…...
22.2.27打卡 Codeforces Round #852 (Div. 2) A~D
A Yet Another Promotion 题面翻译 题目描述 共 ttt 组数据,每组数据中,你需要买 nnn 公斤苹果,第一天单价为 aaa ,但每买 mmm 公斤赠送一公斤;第二天单价为 bbb 。求最小花费。 输入输出格式 第一行一个正整数 …...
如何查看Spring Boot各版本的变化
目录 1.版本 2.基础特性和使用 3.新增特性和Bug修复 1.版本 打开Spring官网,点进Spring Boot项目我们会发现在不同版本后面会跟着不同的标签: 这些标签对应不同的版本,其意思如下: GA正式版本,通常意味着该版本已…...
程序员是否要加入创业公司?
我从1月份入职到2月份离职,历时一个半月。短暂的体验了一段创业生活,更准确的说是一段“待在”创业团队的生活,因为我发现创业本身跟我关系不大。一个半月的就业经历,对任何人来说都不是一个好选择,当然也不是我所期望…...
苏州网站制作/青岛百度推广优化
玩手机,玩相机,一直是从大学毕业以来每天的必想。我的手机生涯从毕业第三年开始,一晃快十年了,友人网是我常去的地方,当初注册的 “ 常来看看 ” 把后来的未来预测的很准。从玩手机到研究手机,现在集中自有…...
哪里有制作网站/互联网论坛
根据IDC APeJ半年度服务追踪最新预测显示,2121年,不含日本的亚太区(APeJ)IT服务支出预计将达到950亿美元。 APeJ地区包括IT和商业服务在内的整体服务支出预计将从2017年大约1050亿美元增长到2021年的1400亿美元。 IDC亚太区高级市场服务分析师Aubrey Lim…...
网站上的高清动态图怎么做的/百度推广在线客服
网上相当一部分博客如此描述内外部表的区别 创建表时:创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径, 不对数据的位置做任何改变。 删除表时:在删除表的时候ÿ…...
网站排名优化效果/seo的搜索排名影响因素有哪些
1. Peek View 可以在不新建TAB的情况下快速查看、编辑一个函数的代码。 用法:在光标移至某个函数下,按下altF12。 然后在Peek窗口里可以继续按altF12。然后按ctrlalt-,或者ctrlalt就可以前后跳转。按ESC关闭Peek窗体。 这下就不需要来回跳转了…...
甘肃建设体网站首页/网络营销
目前项目中使用的是elasticsearch-1.5.1版本,使用到的插件如下: 1. hq 监控,管理elasticsearch集群以及通过web界面来进行查询操作 项目地址: https://github.com/royrusso/elasticsearch-HQ2. analysis-ik ik分词器,中…...
互联网营销网站建设/seo网站地图
这里记录一个比较方便的方式来解决Textview设置不同颜色的字体的方法。可能第一反应是布局的嵌套,这个方法肯定可以啊,但是肯定不推荐啊,布局要尽量减少布局的嵌套,其次,使用自定义控件,U got it,不过确实有…...