C++【list容器模拟实现函数解析】
list容器&&模拟实现函数解析
文章目录
- list容器&&模拟实现函数解析
- 一、list容器使用介绍
- 二、list容器模拟实现及函数解析
- 2.1 list结构体创建
- 2.2 迭代器封装
- 2.21 构造函数:
- 2.22 前置++和后置++及- -
- 2.23 解引用
- 2.24 判断相等
- 2.25 箭头重载
- 2.26 第二个和第三个模板参数以应对const情况
- 2.3 在pos位置插入
- 2.4 头插
- 2.5 尾插
- 2.6 删除pos位置
- 2.7 尾删
- 2.8 头删
- 2.9 析构和清理函数
- 3.1 swap交换函数
- 3.2 拷贝构造函数
- 3.3 使用迭代器进行初始化构造
- 3.4 构造的list中包含n个值为val的元素
- 3.5 赋值重载函数
- 三、list迭代器失效
- 四、list模拟实现代码
- (1)simulate_list
- (2)test.cpp
- (3)测试结果
- 五、vector和list容器对比
- 六、总结与补充
一、list容器使用介绍
(1)list介绍:
概念:
list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。list指针使用类来封装的。
对比:
list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能向前迭代,已让其更简单高效。与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第1个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息。
(2)list使用:
1.list的构造
list (size_type n, const value_type& val = value_type()) :构造的list中包含n个值为val的元素。
list() :构造空的list。
list (const list& x) :拷贝构造函数。
list (InputIterator first, InputIterator last) :用[first, last)区间中的元素构造list。
2.list iterator 起始终止使用
begin +end:返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器。
rbegin +rend:返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置。
3.list增删查改
push_front :在list首元素前插入值为val的元素
pop_front: 删除list中第一个元素
push_back :在list尾部插入值为val的元素
pop_back :删除list中最后一个元素
insert :在list position 位置中插入值为val的元素
erase :删除list position位置的元素
swap :交换两个list中的元素
clear :清空list中的有效元素
find :查找(这个是算法库函数里的,不是容器接口)
4.list空间增长和个数接口
size :获取数据个数
capacity :获取容量大小
empty :判断是否为空
resize: 改变vector的size
reserve : 改变vector的capacity
二、list容器模拟实现及函数解析
2.1 list结构体创建
template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x =T()):_next(nullptr), _prev(nullptr), _data(x){}};
因为它是一个带头的双向循环链表,里面啊穿件一个next指针和一个前驱指针,以及数据data变量,并在里面构建默认构造函数,里面提供匿名对象,因为有可能传的是自定义类型。
2.2 迭代器封装
这里的list迭代器本质上是自定义类型对原生指针的封装,模拟指针的行为,因为list物理结构是不连续的啊,迭代器有++或者–这时候需要对它进行重新定义,需要进行运算符重载。为了它行为像指针一样,只能通过一个类来描述它。所以list的迭代器是一个类对象,这里为了能直接访问我们用struct来定义类。
先拿取部分代码如下:
template<class T,class Cst,class P>struct list_iterator{typedef list_node<T> node;typedef list_iterator<T,Cst,P> self;node* _node;list_iterator(node* n):_node(n){}P operator->(){return &(_node->_data);}Cst operator *(){return _node->_data;}self& operator++(){_node = _node->_next;return *this;}self operator++(int){self tmp(*this);_node = _node->next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self operator--(int){self tmp(*this);_node = _node->prev;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};template<class T>class list{typedef list_node<T> node;public:typedef list_iterator<T,T&,T*> iterator;typedef list_iterator<T, const T&,const T*> const_iterator;list(){_head = new node;_head->_next = _head;_head->_prev = _head;}iterator begin(){return iterator(_head->_next);}const_iterator begin()const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end()const{return const_iterator(_head);}}
在代码里面无论是返回类型还是创建的临时变量,都需要写一个很长的一个类型名,为了使用方便,在list中把list中的结点结构体类型list_node<T>
和普通迭代器list_iterator<T,T&,T*>
及const迭代器list_iterator<T, const T&,const T*>
还有迭代器中的list_node<T>
和list_iterator<T,Cst,P>
进行重定义。增加的另外俩个模板参数下面会分析。
2.21 构造函数:
里面必须得有构造函数,因为编译器自动生成的满足不了我们的需求。要把_node初始化为我们想要的。
2.22 前置++和后置++及- -
前置++返回的是加加后的结果,直接先更新_node,再直接返回this指向的内容,因为这里的迭代器不会销毁,用引用返回。返回的还是一个迭代器直接替换为self&。下面也同理。
后置++返回的是加加之前的结果,所以需要一个临时变量,存放++之前的结果,然后再进行加加。因为这里是一个临时对象,出了作用域会销毁,所以这里使用引用返回。
前置- -,和后置- -出了更新不同,它使用的是前驱指针,其他逻辑和++相同。
2.23 解引用
解引用就直接返回它的数据,由于以后会对list中的数据进行访问,所以这要引用返回T&。
2.24 判断相等
直接返回的时候 判断相等,也就是this指向的迭代器和右面传回来的迭代器进行比较。
2.25 箭头重载
普通指针,原生指针,内置类型的指针就用解引用就可以,如果是实例化一个结构体,打印的时候,自定义类型还需要流插入运算符重载,如果是私有必选重载,现在里面是公有不想重载,这就要用到箭头,所以在迭代器里面重载一个operator箭头,打印写的时候只需要写一个箭头,是了为了提高可读性,编译器进行了简化,省略一个箭头。
而箭头访问成员变量左面必须得是指针,也就是结构指针访问成员,而(*).是用于结构体变量访问成员,在迭代器箭头重载里面,通过箭头拿到数据,再引用拿到数据的地址即可,再加上编译器的增加箭头就可访问具体成员。
2.26 第二个和第三个模板参数以应对const情况
第一个模板参数:
使用begin函数时候,如果是const对象的时候会发生什么,比如下面代码:
void input_list(const nza::list<int> & v)
{nza::list<int> ::iterator p = v.begin();while (p != v.end()){(*p) *= 2;cout << *p << " ";++p;}cout << endl;
}
这里编译报错,这里权限放大,对象调用成员函数得传递this指针,这地方的对象是const,取地址是const list int* ,不能传给普通的this。这里要重载两个函数,在begin,end成员函数后面加const。就能编译成功。
iterator begin()const{return iterator(_head->_next);}
为什么const能构造普通的迭代器:按理说这里加了const后,它所指向内容就不能修改,这里变成可读可写的普通迭代器了,但迭代器所因为这里的const修饰的this即this指向的内容,它指向的内容是_head指针,修饰的是_head指针本身,也就是说在const情况下修饰的是_head不能被改变,并不是_head指向的内容不能改变,所以这个结点的指针是它本身不能改变,但是能拷贝给别人。
这里的记结果最终不仅能遍历还能修改它的数据,能修改就是因为构造了普通迭代器,普通迭代器是可读可写的。
解决:
库里面是这样做的,如果是一个const对象的时候,对它进行了限制,const对象调用的是const begin,返回的是一个const迭代器。
const迭代器和普通迭代器的区别是指针指向的内容不可修改,不能用typedef const iterator const_iterator
这就相当于const修饰的是迭代器这个类,而成员变量不可修改,成员变量是指针,普通跌迭代器相当于T,而上面的是相当于T* const`,这个意思是保证迭代器本身不会被修改,内容可修改,我们想要的是指向的内容不可修改。
只需要在解引用前面加上const,控制这个返回值不一样,但是不能在同一个类中增加一个const& 函数,因为返回类型不同不能重载,这时候思路是再创建一个const_iterator迭代器,但是又太冗余。
这时候为了使代码更简洁,,使代码适应性更强,在迭代器的类模板增加一个模板参数Cst,而在list类中,给迭代器类不同类型的模板的参数,如果是普通迭代器传T&,如果是const迭代器就传const&。使用哪种迭代器,就会使用哪种模板参数进行实例化,就是用这个类型的时候,编译器就会把T替换对应的类型。
第三个模板参数:
在箭头运算符重载函数中,返回类型是T*,如果来一个const对象,继续使用不符合const的性质,const T 类型无法构成重载,所以在迭代器模板参数里面再增加一个参数P就能解决,把T换成P,在list把iterator,const_iterator分别增加一个T和一个const T,就能解决,让编译器根据具体情况去实例化
2.3 在pos位置插入
void insert(iterator pos,const T& x){node* cur = pos._node;node* prev = cur->_prev;node* Newnode = new node(x);prev->_next = Newnode;Newnode->_next = cur;Newnode->_prev = prev;cur->_prev = Newnode;}
如果不用struct用class 去加私有封装,在这要么用友元要么getnode,所以迭代器一般用struct,在里面用这个迭代器去取这个指针,这里用pos迭代器取取出当前指针,在pos前插入,再定义一个前驱指针,然后开一个结点,进行链接。后面四部是链接过程。
2.4 头插
void push_front(const T& x){insert(begin(), x);}
直接复用插入函数,就是在第一个结点的前面进行插入,begin位置进行插入。
2.5 尾插
void push_back(const T& x){/*node* newnode = new node(x);node* tail = _head->_prev;tail->_next = newnode;newnode->_prev=tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}
这里可以用传统的方法也是直接复用插入函数,就是在end的前面插入也即end指向前面的下一个。
2.6 删除pos位置
iterator erase(iterator pos){assert(pos!=end());node* net = pos._node->_next;node* prev=pos._node->_prev;prev->_next = net;net->_prev = prev;delete pos._node;return iterator(net);}
先加一个断言,头结点不能删除。定义一个net指针表示当前位置的下一个位置,再定一个前驱指针表示当前位置的前面的,然后直接把它俩进行链接,释放当前结点,因为考虑到迭代器失效,要给一个返回值,也就是删除位置下一个结点,不然无法遍历。
2.7 尾删
void pop_back(){erase(--end());}
直接复用删除函数,因为尾删,删的是头结点上一个,里面返回的是一个迭代器对象直接调用前置- -。
2.8 头删
void pop_front(){erase(begin());}
也是复用删除函数,头删就是删第一个也即是删除begin位置。
2.9 析构和清理函数
~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){erase(it++);}}
在clear函数如果直接erase(it),迭代器会失效,最好返回当前位置的下一个位置,两种方法,一种是是接收一下,二种是在里面it++,因为这是后置++,返回的是临时变量,是it的拷贝,所以这里删除的是it的拷贝。但是clear函数不删除头结点。
析构函数先调用clear函数把head以后的结点删除,再释放头结点,在把头结点置空。
3.1 swap交换函数
void swap(list<T>& tmp){std::swap(_head, tmp._head);}
3.2 拷贝构造函数
list(const T& it){/*init();for (auto e : it){push_back(e);}*/init();list<T>tmp(it.begin(), it, end());swap(tmp);}
拷贝构造使用第一种方法,就可以拷贝嵌套list和string等自定义类型,如果里面是一个list,用init函数解决了外部的拷贝,里面开了一个新的头结点的空间,而在push_back的时候,调用insert里面开除新结点,最重要的点是新结点中的_data,它存的是拷贝的自定义类型,这个新_data变量的地址和旧结点的地址是不一样的,因为开新结点,就意味着T _data,进行实例化,变量是新空间开的是新地址,而在释放结点摧毁里面的变量时或改变里面其中一个内容不会互相影响,或者说就不存在指向同一块的空间的问题即深拷贝问题。如土_data地址不一样已得到验证:
使用第二种方法:先在内部进行创建一个头结点,之后调用迭代器初始化构造tmp对象,里面形成了一个新的初始化的链表,最后用swap函数交换tmp的头指针和刚刚创建的头指针也即是this指向的头指针。
3.3 使用迭代器进行初始化构造
template<class Iterator>list(Iterator first, Iterator last){init();while (first != last){push_back(*first);++first;}}
也就是先初始化,把某种迭代器的区间的内容进行尾插到实例化对象的空间中去。
3.4 构造的list中包含n个值为val的元素
list (size_t n, const T& val = T()){init();for (size_t i = 0; i<n; ++i){push_back(val);}}
先初始化,再用一个for循环,进行尾插n个val。
3.5 赋值重载函数
list<T>& operator=(list<T> it){swap(it);return *this;}
这里是用swap的时候在里面要传值,如果传引用,交换的时候等号右边的内容会发生变化,我们需要传值,就会调用拷贝构造,it就是等号右边的拷贝,拷贝是等号左边想要的不是右边想要的的,不能让右变的发生变化。所以交换后直接返回*this。
三、list迭代器失效
迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
在insert时这里的迭代器pos不会失效,因为这里pos始终指向这个结点,并且这个位置关系不会变,不像vector插入删除数据就算不扩容,也要挪动数据,因为数据相对位置已经发生改变,他已经不是指向之前的位置了。它俩形成鲜明对比。如图:
而在使用**erase()**函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值如图:
四、list模拟实现代码
(1)simulate_list
#pragma once
#include<assert.h>
namespace nza
{template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x =T()):_next(nullptr), _prev(nullptr), _data(x){}};template<class T,class Cst,class P>struct list_iterator{typedef list_node<T> node;typedef list_iterator<T,Cst,P> self;node* _node;list_iterator(node* n):_node(n){}P operator->(){return &(_node->_data);}Cst operator *(){return _node->_data;}self& operator++(){_node = _node->_next;return *this;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self operator--(int){self tmp(*this);_node = _node->prev;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};template<class T>class list{typedef list_node<T> node;public:typedef list_iterator<T,T&,T*> iterator;typedef list_iterator<T, const T&,const T*> const_iterator;void init(){_head = new node;_head->_next = _head;_head->_prev = _head;}list(){init();}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){erase(it++);}}template<class Iterator>list(Iterator first, Iterator last){init();while (first != last){push_back(*first);++first;}}void swap(list<T>& tmp){std::swap(_head, tmp._head);}list(const list<T>& it){init();for (auto e : it){push_back(e);}/*init();list<T> tmp(it.begin(), it. end());swap(tmp);*/}list (size_t n, const T& val = T()){init();for (size_t i = 0; i<n; ++i){push_back(val);}}list<T>& operator=(list<T> it){swap(it);return *this;}iterator begin(){return iterator(_head->_next);}const_iterator begin()const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end()const{return const_iterator(_head);}void push_back(const T& x){/*node* newnode = new node(x);node* tail = _head->_prev;tail->_next = newnode;newnode->_prev=tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void insert(iterator pos,const T& x){node* cur = pos._node;node* prev = cur->_prev;node* Newnode = new node(x);prev->_next = Newnode;Newnode->_next = cur;Newnode->_prev = prev;cur->_prev = Newnode;}iterator erase(iterator pos){assert(pos != end());node* net = pos._node->_next;node* prev=pos._node->_prev;prev->_next = net;net->_prev = prev;delete pos._node;return iterator(net);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}private:node* _head;};
}
(2)test.cpp
#include<iostream>
#include"simulate_list.h"
#include<list>using namespace std;
void test1()
{nza::list<int> v;v.push_back(9);v.push_back(5);v.push_back(3);nza::list<int> ::iterator p = v.begin();for (auto n : v){cout << n << " ";}cout << endl;
}
void input_list(const nza::list<int> & v)
{nza::list<int> ::const_iterator p = v.begin();while (p != v.end()){/* (*p)*= 2;*/cout << *p << " ";++p;}cout << endl;
}
void test2()
{nza::list<int>l;l.push_back(4);l.push_back(6);l.push_back(8);input_list(l);}
struct M{int _a1;int _a2;M(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}};
void test3()
{nza::list<M> q;q.push_back(M(3, 7));q.push_back(M(22, 22));q.push_back(M(44, 66));nza::list<M> ::iterator p = q.begin();while (p != q.end()){cout << p->_a1 << " ";++p;}cout << endl;
}
void test4()
{nza::list<int> l;l.push_back(99);l.push_back(88);l.push_back(77);for (auto e : l){cout << e << " ";}cout << endl;l.push_back(66);l.push_front(55);for (auto e : l){cout << e << " ";}cout << endl;l.pop_back();l.pop_front();for (auto e : l){cout << e << " ";}cout << endl;l.clear();for (auto e : l){cout << e << " ";}cout << endl;
}
void test5()
{nza::list<std::list<int>> v3(3,{1,2,3});for (auto e : v3){/* cout << e << " "*/}//cout << endl;nza::list<std::list<int>> v4(v3);for (auto e : v4){/*cout<<e<<" ";*/}//cout << endl;
}
int main()
{test1();test2();test3();test4();test5();
}
(3)测试结果
五、vector和list容器对比
底层结构:
vector是动态顺序表,一段连续空间,list是带头结点的双向循环链表。
随机访问:
vector支持随机访问,访问某个元素效率O(1),而list不支持随机访问,访问某个元素效率O(N)。
插入与删除:
任意位置插入和删除效率低,需要挪动元素,时间复杂度为O(N),插入时有可能需要扩容,扩容就需要开辟新空间,拷贝元素,释放旧空间,导致效率更低,而list任意位置插入和删除效率高,不需要诺丁元素,时间复杂度为O(1)。
迭代器:
vector是原生态指针,这不能绝对例如VS是封装的,list对原生态指针即节点指针进行封装。
空间利用率:
vector底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高,而list底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低。
使用场景:
vector需要高效存储,支持随机访问,不关心插入删除效率,而list大量插入和删除操作,不关心随机访问。
迭代器失效:
在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效,而list插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
六、总结与补充
总结:
原生指针只是一个偶然,用来类去封装才是常态,底层的本质都可以认为是指针,只是说嵌入了一个自定义类型,去封装它,语法这个行为就把他识别为自定义类型,自定义用这个运算符就是重载运算符,比如解引用,++等到底是怎么样的行为是另回事,因为函数是我们实现的。const在定义对象的那一下是没有const属性的,这是编译器的特殊处理。
补充:
list<int>::iterator p =v.begin()
,这里要调用一个拷贝构造,这里没有写拷贝构造,编译器默认生成了一个拷贝构造,它是一个浅拷贝,begin返回一个临时对象包含了开始结点的指针,把这个结点的位置赋值给p,也是期望这里有个p对象,对象里面有个结点指针也就是指向首位置,不是有结点指针就得深拷贝。
同时指向了一个位置,但并没有报错,因为迭代器没有写析构函数,不需要释放结点,虽然有结点指针,这不属于迭代器,只是给迭代器进行封装,帮助它实现遍历链表++修改链表,不支持释放,释放是链表的事,另外结点也不是它创建的,总来的的说浅拷贝没问题。
相关文章:
C++【list容器模拟实现函数解析】
list容器&&模拟实现函数解析 文章目录list容器&&模拟实现函数解析一、list容器使用介绍二、list容器模拟实现及函数解析2.1 list结构体创建2.2 迭代器封装2.21 构造函数:2.22 前置和后置及- -2.23 解引用2.24 判断相等2.25 箭头重载2.26 第二个和第…...
(Java)试题 算法提高 约数个数
一、题目 (1)资源限制 内存限制:512.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s (2)输入 输入一个正整数N (3)输出 N有几个约数 &a…...
魔法反射--java反射初入门(基础篇)
👳我亲爱的各位大佬们好😘😘😘 ♨️本篇文章记录的为 java反射初入门 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。 ♨️如果文章有…...
概率统计_协方差的传播 Covariance Propagation
1. 方差的传播 误差的传播是指分析在形如的关系中,参量误差(x)对变量误差(y)的影响有多大。误差的传播与函数的微分紧密相关,本质是在利用当Δ x 不大时,。 方差计算公式: X为变量,为总体均值,N为总体例数。求变量X与均值的差的平方再求平均值,即得到方差。方差…...
大学生考研的意义?
当我拿起笔头,准备写这个话题时,心里是非常难受的,因为看到太多的学生在最好的年华,在自由的大学本应该开拓知识,提升认知,动手实践,不断尝试和试错,不断历练自己跳出学生思维圈&…...
【C++笔试强训】第三十一天
🎇C笔试强训 博客主页:一起去看日落吗分享博主的C刷题日常,大家一起学习博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话:夜色难免微凉,前方必有曙光 🌞。 选择题 &#x…...
toString()、equals()是什么,为啥需要重写,多种方法来重写
https://m.runoob.com/java/java-object-class.html toString() 1.为什么会有toString 子类继承父类就可以使用父类所有非私有的属性的方法。 在Java中所有类都直接或者间接继承Object类,可以说只要是Object类里面定义的非私有的属性和方法,任何类都可…...
家装材料清单中会有哪些装饰材料?
在家居装修中,业主可以根据装修公司出具的材料清单去一一采购,这样不至于有遗漏,就算采用全包的方式,通过材料清单也可以大致了解当时房子装修所用的材料,补充自己的装修知识。下面跟随小编一起了解下房子装修材料中所…...
【C++初阶】6. CC++内存管理
1. C/C内存分布 我们先来看下面的一段代码和相关问题 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* …...
【数据结构】万字超详解顺序表(比细狗还细)
我这个人走得很慢,但是我从不后退。 ——亚伯拉罕林肯 目录 一.什么是线性表? 二.什么是顺序表? 三.接口函数的实现 1.创建工程 2.构造顺序表 3.初始化顺序表 3.初始化顺序表 4.顺序表的尾插 5.顺序…...
yolov5 剪枝、蒸馏、压缩、量化
文章大纲 剪枝推理优化YOLOv5 剪枝可能出现的问题参考文献与学习路径考察神经网络时期重要的激活函数sigmoid和tanh,它们有一个特点,即输入值较大或者较小的时候,其导数变得很小,而在训练阶段(详见1.2.3节),需要求取多个导数值,并将每层得到的导数值相乘,这样一旦层数…...
如何用python代码,更改照片尺寸,以及更换照片底色
前言 python浅浅替代ps?如何用代码来p证件照并且更换底色? 唉,有个小姐姐给我扔了张照片,叫我帮忙给她搞成证件照的尺寸还得换底色,她说自己忙的很 可惜电脑上没有ps只有pycharm,没得办法只能来试试看代…...
【pygame游戏】Python实现蔡徐坤大战篮球游戏【附源码】
前言 话说在前面,我不是小黑子~😏 本文章纯属技术交流~娱乐 前几天我获得了一个坤坤打篮球的游戏,也给大家分享一下吧~ 好吧,其实并不是这样的游戏,往下慢慢看吧。 准备工作 开发环境 Python版本:3.7.8 …...
通过指针引用字符串详解,以及字符指针变量和字符数组的比较
在平常的案例中已大量地使用了字符串,如在 printf函数中输出一个字符串。这些字符串都是以直接形式 (字面形式) 给出的,在一对双引号中包含若干个合法的字符。在本节中将介绍使用字符串的更加灵活方便的方法:通过指针引用字符串。 目录 一、…...
Vue基本整合(一)
NPM安装npm是node的包管理工具https://nodejs.org/en/脚手架安装npm i -g vue/clihttps://registry.npmjs.org/vue浏览器插件https://devtools.vuejs.org/guide/installation.html#chromehttps://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhble…...
C++编程之 万能引用
万能引用是一种可以同时接受左值或右值的引用,它的形式是T&&,其中T是一个模板参数。万能引用不是C的一个新特性,而是利用了模板类型推导和引用折叠的规则来实现的功能。 模板类型推导 模板类型推导是指在调用一个模板函数时&#x…...
【JavaScript速成之路】JavaScript内置对象--数组对象
📃个人主页:「小杨」的csdn博客 🔥系列专栏:【JavaScript速成之路】 🐳希望大家多多支持🥰一起进步呀! 文章目录前言数组对象1,数组类型检测2,数组元素增删3,…...
【华为机试真题详解 Python实现】最差产品奖【2023 Q1 | 100分】
文章目录 前言题目描述输入描述输出描述示例 1题目解析参考代码前言 《华为机试真题详解》专栏含牛客网华为专栏、华为面经试题、华为OD机试真题。 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能…...
[算法] 二分查找
package com.guigu.search;import java.util.ArrayList; import java.util.Arrays;/*** author: guorui fu* versiion: 1.0* 二分查找 直接适用于已经排序完成的数组*/ public class BinarySearch {public static void main(String[] args) {int arr[] {1,8,8,89,101,1234};Ar…...
HTML面经
1.src与href的区别 src用于替换当前元素,如script标签,img标签等。当html解析到这些标签时,会暂停解析,将指定的资源下载下来,嵌入到所在位置内。href的话则是一个当前页面与引用资源之间的链接,如link标签…...
我的十年编程路 2021年篇
慢慢地,时光走过了8个年头,来到2021年。 站在2021年,回望8年的过往,没有大的起伏和波澜。或许是上天的眷顾,我的事业发展一直都很顺利。当然,弯路也走过一些,而且工作其实挺辗转的,…...
ElasticSearch 8 学习笔记总结(七)
感觉这些东西没必要认真学,了解一下,工作用到再学。 文章目录一、ES8 EQL 介绍二、ES8 EQL基本操作 与 安全检测三、ES SQL操作四、ES SQL与DSL的关系五、ES 常用的SQL操作六、ES datagrip配置ES七、ES8 自然语言处理 NLP八、ES8 性能优化 之 缓存九、ES…...
【云原生】Docker 网络模式详解、容器间网络通信
当项目大规模使用 Docker 时,容器通信的问题也就产生了。要解决容器通信问题,必须先了解很多关于网络的知识。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,也有着很多不完善的地方,网络方面就是 Docker…...
Java开发 - 布隆过滤器初体验
目录 前言 布隆过滤器 什么是布隆过滤器 布隆过滤器的作用 布隆过滤器原理 怎么设计布隆过滤器 布隆过滤器使用案例 安装布隆过滤器 添加依赖 添加配置 添加工具类 添加测试代码 简单测试 特别提醒 结语 前言 前面三篇,已经把消息队列…...
【计算机组成原理 - 第一章】计算机系统概论(完结)
本章参考王道考研相关课程: 【2021版】1.2.1_计算机硬件的基本组成_哔哩哔哩_bilibili 【2021版】1.2.2_认识各个硬件部件_哔哩哔哩_bilibili 【2021版】1.2.3_计算机系统的层次结构_哔哩哔哩_bilibili 【2021版】1.3_计算机的性能指标_哔哩哔哩_bilibili 目录 一、…...
C++类与对象(下)【详析】
类与对象(下) 目录类与对象(下)一、再谈构造函数1.构造函数体赋值2.初始化列表定义:注意点:总结:3.explicit关键字引入:explicit:二、 static成员回顾:static…...
exe反编译为.py文件
介绍公司以前的一个exe包,我们需要查看里面python源码,但是以前的py源码文件找不到,所以只能反编译,介绍一下反编译的过程。首先准备:pyinstxtractor.py这个文件,网上很多,自己下载准备查看二进…...
38 openEuler搭建FTP服务器-FTP总体介绍
文章目录38 openEuler搭建FTP服务器-FTP总体介绍38.1 FTP简介38.2 FTP使用到的端口38.3 vsftpd简介38 openEuler搭建FTP服务器-FTP总体介绍 38.1 FTP简介 FTP(File Transfer Protocol)即文件传输协议,是互联网最早的传输协议之一࿰…...
三天吃透操作系统面试八股文
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...
vue后台管理系统——添加i18n国际化功能——技能提升
昨天在写后台管理系统时,遇到一个需求就是需要实现国际化功能。 antd和element-ui这两个框架其实都是有国际化的。 具体展示形式就是如下: 点击右上角头部的语言,切换语言,然后整个系统的文字都改变成对应的语言展示。 切换成…...
wordpress 隐藏顶部/武汉seo报价
整个项目包含了:开题报告 开题报告PPT 任务书 中期报告 论文模板 答辩PPT等 项目源码 主要安介绍了系统在开发过程中所应用到的一些关键的技术 主要python技术介绍;框架Django概要;MySQL数据库知识; 以及常规的网页技术HTM…...
专业营销型网站建设公司/优化防控举措
SqlServer中的数据类型UniqueIdentifier 2007-08-30 11:23:00 vipxiaotian 阅读数 36033 文章标签: sqlserverapiinsert服务器go脚本 更多 分类专栏: SQL SqlServer中的数据类型UniqueIdentifier到底是什么东东? 该类型一般用来做为主键使…...
做一门户网站价格/营销宣传方案
一、函数的有用信息在函数内,用 引起来,添加的注释信息可以写函数的功能,以及需要填写和可以扩展的注释词语二、带参数的装饰器及多个装饰器装饰一个函数def timmerout(flag1): # flag1 flagdef timmer(f):def inner(*args,**kwargs):if fla…...
网站关键词优化wang/seo引擎优化是做什么的
为什么80%的码农都做不了架构师?>>> 简单说说吧:我俩当年都是中兴15年那一批次的应届入职毕业生,一起参加入职公司级别的培训,一个班,一个小组。培训长达7天,无脑级别的洗脑(你懂的…...
四川蓉和建设公司网站/长尾关键词
alpha,4α alpha,5Α beta,4β beta,5Β gamma,4γ gamma,5Γ delta,4δ delta,5Δ epsilon,4ε epsilon,5Ε zeta,4ζ zeta,5Ζ eta,4η eta,5Η theta,4θ theta,5Θ iota,4ι iota,5Ι kappa,4κ kappa,5Κ lambda,4λ lambda,5Λ mu,4μ mu,5Μ nu…...
电子政务门户网站建设的教训/怎么注册一个自己的网址
paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境#---混合编程的类型1.代码inline 方式2.使用库/api 解析方式.#----配置resin 支持phpresin4默认自动支持php..也能手动配置了.web.xml加php的servlet解析..参考Quercus让你的PHP开心在Servlet容器奔跑#---…...