【STL】list
l i s t list list 是 C C C++ 标准模板库( S T L STL STL)中的一个序列容器( S e q u e n c e C o n t a i n e r Sequence\ Container Sequence Container),它允许在容器的任意位置快速插入和删除元素,是一个能够存放任意类型的双向带头循环链表。与数组或向量( v e c t o r vector vector)不同, l i s t list list 不需要在创建时指定大小,并且在任何位置添加或删除元素的效率为 O ( 1 ) O(1) O(1),而不需要重新分配内存。
文章目录
- 前言 —— 迭代器
- 1. 迭代器的分类
- 2. 不同性质迭代器的区别
- 一、list 的介绍
- 1. 结点(node)
- 2. 迭代器
- 3. 数据结构
- 二、list 的使用(常用接口)
- 1. 常见构造
- 2. iterator 的使用
- 3. capacity
- 4. element access
- 5. modifiers
- 6. operations
- 三、list 的迭代器失效问题
- 四、list 与 vector 的对比
- 五、list 的模拟实现
- 1. 模拟实现 list
- 2. list 的反向迭代器(拓展)
- 总结
前言 —— 迭代器
1. 迭代器的分类
迭代器根据各个容器底层结构(决定可以使用哪些算法)不同,虽然所实现的功能相同,但是性质不同:迭代器只能够进行 ++ 操作,像
++begin()/begin()++
的就是单向迭代器;迭代器只能进行 ++ / / / - - 操作,像++begin()/--begin()
的是双向迭代器;而迭代器能够随机访问(底层是连续空间)操作的,像++begin()/--begin()/begin()+i/end()-i
,是随机迭代器。
从 C C C++ 官方文档中,我们看到 l i s t list list 的迭代器是一个双向的迭代器( b i d i r e c t i o n a l i t e r a t o r bidirectional\ iterator bidirectional iterator):
其他的像 v e c t o r vector vector 是一个随机访问迭代器( r a n d o m a c c e s s i t e r a t o r random\ access\ iterator random access iterator):
u n o r d e r e d _ m a p unordered\_map unordered_map 是一个单向迭代器( f o r w a r d _ i t e r a t o r forward\_iterator forward_iterator):
2. 不同性质迭代器的区别
容器的底层结构决定了迭代器的性质,也决定了这些容器能使用哪些算法。
比如说排序算法 s o r t sort sort:
可以看出,看似 s o r t sort sort 是一个函数模板支持所有类型,实际上其只支持随机访问迭代器的容器使用。
再比如逆置算法 r e v e r s e reverse reverse:
可以看出,由于其使用到了 - - 迭代器的操作,因此 r e v e r s e reverse reverse 只支持双向迭代器的容器使用。
有些算法像 f i n d find find,还会出现 I n p u t I t e r a t o r InputIterator InputIterator 的概念:
这其实就代表着 f i n d find find 函数支持所有类型的迭代器。
实际上,单向迭代器、双向迭代器和随机访问迭代器属于继承关系,即包含关系:
总结:
也就是说,单向( F o r w a r d Forward Forward)迭代能支持的算法,双向( B i d i r e c t i o n a l Bidirectional Bidirectional)迭代器也支持;双向( B i d i r e c t i o n a l Bidirectional Bidirectional)迭代器支持的算法,随机访问( R a n d o m A c c e s s Random\ Access Random Access)迭代器也支持;如果是支持 I n p u t I t e r a t o r InputIterator InputIterator 的算法,那么所有迭代器(容器)都支持。
一、list 的介绍
相较于 v e c t o r vector vector 的 连续线性空间, l i s t list list 就显得复杂很多,它的好处是:每次插入或删除一个元素,就配置或释放一个元素空间。而且,对于任何位置的元素插入或删除都是常数时间( O ( 1 ) O(1) O(1))。
因此, l i s t list list 对于空间的运用有绝对的精准,一点也不会浪费。
1. 结点(node)
每一个设计过 l i s t list list 的人都知道, l i s t list list 本身和 l i s t list list 的结点是不同的结构,需要分开设计。以下是 S T L STL STL l i s t list list 结点( n o d e node node)结构:
template<class T>
struct __list_node
{typedef void* void_pointer;void_pointer prev; // 型别为 void*。其实可设为 __list_node<T>*void_pointer next;T data;
};
显然这是一个双向链表。
2. 迭代器
l i s t list list 不再能够像 v e c t o r vector vector 一样以普通指针作为迭代器,因为其结点不保证在存储空间中连续存在。 l i s t list list 迭代器必须有能力指向 l i s t list list 的结点,并有能力进行正确的递增、递减、取值、成员存取等操作。
由于 S T L STL STL l i s t list list 是一个双向链表( d o u b l e l i n k e d − l i s t double\ linked-list double linked−list),迭代器必须具备前移、后移的能力,所以 l i s t list list 提供的是 B i d i r e c t i o n a l i t e r a t o r s Bidirectional\ iterators Bidirectional iterators。
以下是 l i s t list list 迭代器的设计:
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, Ref, Ptr> self;typedef bidrectional_iterator_tag iterator_category;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __list_node<T>* link_type;typedef size_t size_type;typedef ptrdiff_t difference_type;link_type node; // 迭代器内部当然要有一个普通指针,指向 list 的结点// constructor__list_iterator(link_type x) : node(x) {}__list_iterator() {}__list_iterator(const iterator& x) : node(x.node) {}bool operator == (const self& x) const { return node == x.node; }bool operator != (const self& x) const ( return node != x.node; )// 以下对迭代器取值(dereference),取的是结点的数据值reference operator* () const { return (*node).data; }// 以下是迭代器成员存取(member access)运算子的标准做法pointer operator -> const { return &(operator * ()); }// 对迭代器累加 1,就是前进一个结点self& operator ++ (){node = (link_type)((*node).next);return *this;}self operator ++ (int){self tmp = *this;++*this;return tmp;}// 对迭代器递减 1,就是后退一个结点self& operator -- (){node = (link_type)((*node).prev);return *this;}self operator -- (int){self tmp = *this;--*this;return tmp;}
};
3. 数据结构
S G I SGI SGI l i s t list list 不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,便可完整表现整个链表:
template<class T, class Alloc = alloc> // 缺省使用 alloc 为配置器
class list
{
protected:typedef __list_node<T> list_node;
public:typedef list_node* link_type;protected:link_type node; // 只要一个指针,便可表示整个环状双向链表
...
};
如果让指针 n o d e node node 指向刻意置于尾端的一个空白节点, n o d e node node 便能符合 S T L STL STL 对于 “ 前闭后开 ” 区间的要求,成为 l a s t last last 迭代器,如下图所示。这么一来,以下几个函数便都可以轻易完成:
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }bool empty() const { return node->next == node; }
size_type size() const
{size_type result = 0;distance(begin(), end(), result); // 全局函数return result;
}// 取头结点的内容(元素值)
reference front() { return *begin(); }
// 取尾结点的内容(元素值)
reference back() { return *(--end()); }
关于更多 l i s t list list 的介绍,还是要多查官方文档: l i s t list list的文档介绍。
二、list 的使用(常用接口)
下面只列举了 l i s t list list 最常用的几个接口,更多详细信息可以自行查官方文档: l i s t list list。
l i s t list list 也采用了模板: t e m p l a t e < c l a s s T , c l a s s A l l o c = a l l o c a t o r < T > > c l a s s l i s t ; template < class\ T,\ class\ Alloc = allocator<T> > class\ list; template<class T, class Alloc=allocator<T>>class list;
1. 常见构造
构造函数( c o n s t r u c t o r constructor constructor) | 接口说明 |
---|---|
l i s t ( s i z e _ t y p e n , c o n s t v a l u e _ t y p e & v a l = v a l u e _ t y p e ( ) ) list(size\_type\ n,\ const\ value\_type\&\ val = value\_type()) list(size_type n, const value_type& val=value_type()) | 构造的 l i s t list list 中包含 n n n 个值为 v a l val val 的元素 |
l i s t ( ) list() list() | 构造空的 l i s t list list |
l i s t ( c o n s t l i s t & x ) list(const\ list\&\ x) list(const list& x) | 拷贝构造函数 |
l i s t ( I n p u t I t e r a t o r f i r s t , I n p u t I t e r a t o r l a s t ) list(InputIterator\ first, InputIterator\ last) list(InputIterator first,InputIterator last) | 用 [ f i r s t , l a s t ) [first, last) [first,last) 区间中的元素构造 l i s t list list |
2. iterator 的使用
此处,可以暂时将迭代器理解成一个指针,该指针指向 l i s t list list 中的某个结点。
函数声明 | 接口说明 |
---|---|
b e g i n begin begin + + + e n d end end | 返回第一个元素的迭代器 + + + 返回最后一个元素下一个位置的迭代器 |
r b e g i n rbegin rbegin + + + r e n d rend rend | 返回第一个元素的反向迭代器,即 e n d end end 位置,返回最后一个元素下一个位置的反向迭代器,即 b e g i n begin begin 位置 |
【注意】
-
b e g i n begin begin 与 e n d end end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动。
-
r b e g i n ( e n d ) rbegin(end) rbegin(end) 与 r e n d ( b e g i n ) rend(begin) rend(begin) 为反向迭代器,对迭代器执行 ++ 操作,迭代器向前移动。
3. capacity
函数声明 | 接口说明 |
---|---|
e m p t y empty empty | 检测 l i s t list list 是否为空,是返回 t r u e true true,否则返回 f a l s e false false |
s i z e size size | 返回 l i s t list list 中有效节点的个数 |
4. element access
函数声明 | 接口说明 |
---|---|
f r o n t front front | 返回 l i s t list list 的第一个节点中值的引用 |
b a c k back back | 返回 l i s t list list 的最后一个节点中值的引用 |
5. modifiers
函数声明 | 接口说明 |
---|---|
p u s h _ f r o n t push\_front push_front | 在 l i s t list list 首元素前插入值为 v a l val val 的元素 |
p o p _ f r o n t pop\_front pop_front | 删除 l i s t list list 中第一个元素 |
p u s h _ b a c k push\_back push_back | 在 l i s t list list 尾部插入值为 v a l val val 的元素 |
p o p _ b a c k pop\_back pop_back | 删除 l i s t list list 中最后一个元素 |
i n s e r t insert insert | 在 l i s t p o s i t i o n list\ position list position 位置中插入值为 v a l val val 的元素 |
e r a s e erase erase | 删除 l i s t p o s i t i o n list\ position list position 位置的元素 |
s w a p swap swap | 交换两个 l i s t list list 中的元素 |
c l e a r clear clear | 清空 l i s t list list 中的有效元素 |
6. operations
函数声明 | 接口说明 |
---|---|
s p l i c e splice splice | 将元素从 x x x( l i s t list list) 转移到容器中,并将它们插入到 p o s pos pos 位置(剪切) |
r e m o v e remove remove | 删除 l i s t list list 中值为 v a l val val 的元素 |
u n i q u e unique unique | 删除 l i s t list list 中重复的元素(先排序再去重) |
m e r g e merge merge | 合并有序的多个 l i s t list list |
s o r t sort sort | 对容器中的元素进行排序 |
r e v e r s e reverse reverse | 反转元素的顺序 |
三、list 的迭代器失效问题
之前把迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为 l i s t list list 的底层结构为带头结点的双向循环链表,因此在 l i s t list list 中进行插入时是不会导致 l i s t list list 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
1. 插入时不失效(不挪动数据):
void test1()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);auto it = l.begin();l.insert(it, 10);*it = 99;for (const auto& i : l) cout << i << " "; cout << endl;
}
2. 删除时失效:
void test2()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);//删除所有偶数auto it = l.begin();while(it != l.end()){if (*it % 2 == 0){l.erase(it);}++it;}for (const auto& i : l) cout << i << " "; cout << endl;
}
3. 解决方案 —— —— —— 接收 e r a s e erase erase 返回值,返回的是下一个结点的迭代器:
void test3()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);//删除所有偶数auto it = l.begin();while(it != l.end()){if (*it % 2 == 0){//需要更新一下itit = l.erase(it);}else{++it;}}for (const auto& i : l) cout << i << " "; cout << endl;
}
四、list 与 vector 的对比
v e c t o r vector vector 与 l i s t list list 都是 S T L STL STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:
v e c t o r vector vector | l i s t list list | |
---|---|---|
底层结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
随机访问 | 支持随机访问,访问某个元素效率 O ( 1 ) O(1) O(1) | 不支持随机访问,访问某个元素效率 O ( N ) O(N) O(N) |
插入和删除 | 任意位置插入和删除效率低,需要搬移元素,实现复杂度为 O ( N ) O(N) O(N),插入时有可能需要扩容:开辟新空间,拷贝元素,释放就空间,导致效率更低 | 任意位置插入删除效率高,不需要搬移元素,时间复杂度为 O ( 1 ) O(1) O(1) |
空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 | 底层结点动态开辟,小结点容易造成内存碎片,空间利用率低,缓存利用率低 |
迭代器 | 原生态指针 | 对原生态指针(结点指针)进行封装 |
迭代器失效 | 在插入元素时,要给所有迭代器重新赋值,因为插入元素有可能会导致重新扩容,导致原来的迭代器失效;删除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效;删除元素时,只会导致当前迭代器失效,其他迭代器不受影响 |
使用场景 | 需要高效存储,支持随机访问,不关系插入删除效率 | 大量插入和删除操作,不关心随机访问 |
五、list 的模拟实现
1. 模拟实现 list
根据之前的经验,由于 l i s t list list 也属于 C C C++ S T L STL STL 容器(模板类),因此, l i s t list list 声明和定义都要写到一个文件中。这里不同的是: l i s t list list 的迭代器要自己封装实现(不能用原生态指针了),而且每个结点也是一个独立的结构要单独封装,因此,类外还要定义两个结构体:一个用来封装结点,另一个用来封装迭代器。
因此,总共分为两个文件: l i s t . h list.h list.h 用来存放 l i s t list list 的定义和实现; t e s t . c p p test.cpp test.cpp 用来测试。
注意:这里两个文件中的 l i s t list list 都是自己定义的,为了和 s t d : : l i s t std::list std::list 作区分,我们要单独创建一个命名空间,这里我用的是 n a m e s p a c e y b c namespace\ ybc namespace ybc 。
- l i s t . h list.h list.h
#pragma once
#include<iostream>
#include<list>
#include<assert.h> using namespace std;namespace ybc
{template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& data = T()) //缺省参数要给匿名对象:_data(data), _prev(nullptr), _next(nullptr){}};template<class T, class Ref, class Ptr>struct list_iterator{typedef list_node<T> node;typedef list_iterator<T, Ref, Ptr> self;node* _node;list_iterator(node* node):_node(node){}Ref operator * (){return _node->_data;}Ptr 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& it) const{return _node == it._node;}bool operator != (const self& it) const{return _node != it._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;iterator begin(){/*iterator it(_head->_next);return it;*//*return iterator(_head->_next); //返回匿名对象*/return _head->_next; //类型转换}iterator end(){/*iterator it(_head);return it;*//*return iterator(_head); //返回匿名对象*/return _head; //类型转换}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}void empty_init(){_head = new node;_head->_prev = _head;_head->_next = _head;_size = 0;}list(){empty_init();}list(const list<T>& l){empty_init();for (auto& i : l){push_back(i);}}list<T>& operator = (list<T> l){swap(l);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){auto it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& l){std::swap(_head, l._head);std::swap(_size, l._size);}iterator insert(iterator pos, const T& x){node* cur = pos._node->_prev;node* newnode = new node(x);cur->_next->_prev = newnode;newnode->_prev = cur;newnode->_next = cur->_next;cur->_next = newnode;++_size;return newnode;}void push_front(const T& x){insert(begin(), x);}void push_back(const T& x){insert(end(), x);}iterator erase(iterator pos){assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;--_size;return next;}void pop_front(){erase(begin());}void pop_back(){erase(--end());}size_t size() const{return _size;}bool empty() const{return _size == 0;}T& front(){return _head->_next->_data;}const T& front() const{return _head->_next->_data;}T& back(){return _head->_prev->_data;}const T& back() const{return _head->_prev->_data;}private:node* _head;size_t _size = 0;};
}
- t e s t . c p p test.cpp test.cpp
#include"list.h"namespace ybc
{template<class Container>void print(const Container& l){for (const auto& i : l){cout << i << " ";}cout << endl;}void test1(){list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);l.push_back(6);l.push_front(0);l.push_front(6);l.push_front(5);l.push_front(4);l.push_front(3);l.push_front(2);l.push_front(1);print(l);}void test2(){list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);l.push_back(6);print(l);l.pop_back();print(l);l.pop_front();print(l);}struct AA{int a1 = 1;int a2 = 2;};void test3(){list<AA> l;l.push_back(AA());l.push_back(AA());l.push_back(AA());l.push_back(AA());l.push_back(AA());l.push_back(AA());for (auto it = l.begin(); it != l.end(); ++it){cout << it->a1 << " " << it->a2 << endl;//cout << it->->a1 << " " << it->->a2 << endl;//特殊处理:本来应该是两个->才合理,为了可读性,省略了一个->//cout << it.operator->()->a1 << " " << it.operator->()->a2 << endl;}cout << endl;}void test4(){list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);auto iti = l.begin();l.insert(iti, 10);*iti *= 99;print(l);//删除所有偶数auto it = l.begin();while (it != l.end()){if (*it % 2 == 0){l.erase(it);}++it;}print(l);}void test5(){list<int> l1;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);l1.push_back(5);cout << "l1:"; print(l1);list<int> l2 = l1;cout << "l2:"; print(l2);list<int> l3;l3.push_back(10);l3.push_back(20);l3.push_back(30);l3.push_back(40);l3.push_back(50);l3.push_back(60);cout << "l3:"; print(l3);l1 = l3;cout << "l1:"; print(l3);}void test6(){list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);l.push_back(5);l.push_back(6);l.push_back(7);print(l);cout << l.front() << endl;cout << l.back() << endl;}
}int main()
{//ybc::test1();//ybc::test2();//ybc::test3();//ybc::test4();//ybc::test5();ybc::test6();return 0;
}
2. list 的反向迭代器(拓展)
通过前面例子知道,反向迭代器的 ++ 就是正向迭代器的 - -,反向迭代器的 - - 就是正向迭代器的 ++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。
template<class Iterator>
class ReverseListIterator
{// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量// 因为静态成员变量也是按照 类名::静态成员变量名的方式访问的public:typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef ReverseListIterator<Iterator> Self;public:// 构造ReverseListIterator(Iterator it): _it(it){}// 具有指针类似行为Ref operator*(){Iterator temp(_it);--temp;return *temp;}Ptr operator->(){ return &(operator*());}// 迭代器支持移动Self& operator++(){--_it;return *this;}Self operator++(int){Self temp(*this);--_it;return temp;}Self& operator--(){++_it;return *this;}Self operator--(int){Self temp(*this);++_it;return temp;}// 迭代器支持比较bool operator!=(const Self& l)const{ return _it != l._it;}bool operator==(const Self& l)const{ return _it != l._it;}Iterator _it;
};
总结
l i s t list list 是 S T L STL STL 封装好的一个双向带头循环链表的模板类。相较于 v e c t o r vector vector 的连续线性空间, l i s t list list 就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,且效率为 O ( 1 ) O(1) O(1)。因此, l i s t list list 对于空间的运用有绝对的精准,一点也不浪费。 v e c t o r vector vector 和 l i s t list list 作为两个最常被使用的容器,在上文一个表格中做了对比,两个容器各有优劣,根据各自的应用场景使用最合适的容器,就能够发挥出最好的效果。
相关文章:
【STL】list
l i s t list list 是 C C C 标准模板库( S T L STL STL)中的一个序列容器( S e q u e n c e C o n t a i n e r Sequence\ Container Sequence Container),它允许在容器的任意位置快速插入和删除元素,是一…...
证券公司主要业务分析及当前佣金最低免五情况探讨
我是StockMasterX,今日想分析证券公司主要业务,并探讨当前佣金最低且免五的证券公司情况,此议题具有一定研究价值,我从事股票交易多年,与证券公司互动频繁,前日晚间饮茶之际,浏览手机时对此深思…...
C++ 变量的声明与定义分离式编译与静态类型(十六)
1. 声明与定义的区别 声明(declaration):向编译器表明某个变量(或其他实体)的类型与名字,使它在后续的编译过程中可见或可用。定义(definition):除了声明变量的名字和类…...
黑盒测试的等价类划分法(输入数据划分为有效的等价类和无效的等价类)
重点: 有效等价和单个无效等价各取1个即可 1、正向用例:一条尽可能覆盖多条2、逆向用例:每一条数据,都是一条单独用例。 步骤: 1、明确需求 2、确定有效和无效等价 3、根据有效和无效造数据编写用例 3、适用场景 针对:需要有大量数据测试输入, …...
通过Appium理解MCP架构
MCP即Model Context Protocol(模型上下文协议),是由Anthropic公司于2024年11月26日推出的开放标准框架,旨在为大型语言模型与外部数据源、工具及系统建立标准化交互协议,以打破AI与数据之间的连接壁垒。 MCP架构与Appi…...
uWebSockets开发入门
一、常用C++ WebSocket开源库 一些常用的 C++ WebSocket 开源库,它们支持 WebSocket 协议的实现,适用于客户端或服务器端开发。 1. Boost.Beast (推荐) 特点:基于 Boost.Asio 的高性能库,支持 HTTP/WebSocket,属于 Boost 官方库的一部分,稳定且跨平台。 适用场景:需要高…...
Python自动化模块:开启高效编程新时代
一、写在前面 在数字化时代,自动化技术已成为提高效率、降低成本的关键手段。Python 作为一种简洁、高效且功能强大的编程语言,凭借其丰富的库和框架,在自动化领域占据了举足轻重的地位,成为众多开发者的首选工具之一。从简单的文…...
Android7 Input(二)Linux 驱动层输入事件管理
概述 在Linux系统中,将键盘,鼠标,触摸屏等这类交互设备交由Linux Input子系统进行管理,Linux Input驱动子系统由于具有良好的和用户空间交互的接口。因此Linux Input驱动子系统,不止于只管理输入类型的设备。也可以将其…...
前端给后端发送数据时都需要包含哪些内容?(HTTP请求的基本组成部分)
1 [TOC](1)一、**必须传递的内容**1. **URL(请求地址)** 二、**可选内容**1. **请求方法(HTTP Method)**2. **请求头(Headers)**3. **请求体(Body)**4. **其他配置** 技术无关 一、必…...
记录vite引入sass预编译报错error during build: [vite:css] [sass] Undefined variable.问题
vite.config.ts resolve: {alias: {: path.resolve(__dirname, src),},},css: {// css预处理器preprocessorOptions: {scss: {additionalData: use "/assets/styles/block.scss" as *;,}}},block.scss $colorGreen: #00ff00;index.vue :v-deep .font-size-14{colo…...
智慧运维平台:赋能未来,开启高效运维新时代
在当今数字化浪潮下,企业IT基础设施、工业设备及智慧城市系统的复杂度与日俱增,传统人工运维方式已难以满足高效、精准、智能的管理需求。停机故障、低效响应、数据孤岛等问题直接影响企业运营效率和成本控制。大型智慧运维平台(AIOps, Smart…...
【log4j】配置Slf4j
配置Slf4j 引入lombok包 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.36</version><scope>provided</scope> </dependency>引入log4j相关api <dependency…...
静态网页应用开发环境搭建实战教程
1. 前言 静态网页开发是前端工程师的基础技能之一,无论是个人博客、企业官网还是简单的Web应用,都离不开HTML、CSS和JavaScript。搭建一个高效的开发环境,能够极大提升开发效率,减少重复工作,并优化调试体验。 本教程…...
AP 场景架构设计(一) :OceanBase 读写分离策略解析
说明:本文内容对应的是 OceanBase 社区版,架构部分不涉及企业版的仲裁副本功能。OceanBase社区版和企业版的能力区别详见: 官网链接。 概述 当两种类型的业务共同运行在同一个数据库集群上时,这对数据库的配置等条件提出了较高…...
未来村庄智慧灯杆:点亮乡村智慧生活
在乡村振兴与数字乡村建设的时代进程中,未来村庄智慧灯杆凭借其多功能集成与智能化特性,已成为乡村基础设施建设领域的崭新焦点,为乡村生活带来了前所未有的便利,推动着乡村生活模式的深刻变革。 多功能集成:一杆多能…...
MySQL基础语法DDLDML
目录 #1.创建和删除数据库 #2.如果有lyt就删除,没有则创建一个新的lyt #3.切换到lyt数据库下 #4.创建数据表并设置列及其属性,name是关键词要用name包围 编辑 #5.删除数据表 #5.查看创建的student表 #6.向student表中添加数据,数据要与列名一一对应 #7.查询studen…...
利用 VSCode 配置提升 vibe coding 开发效率
利用 VSCode 配置提升 vibe coding 开发效率 Vibe Coding(氛围编程)是一种基于AI的编程方法,其核心在于通过自然语言描述软件需求,再由大规模语言模型(LLM)自动生成代码,从而实现对传统手写编程…...
使用 Chromedp 监听网页请求和响应
使用 Chromedp 监听网页请求和响应 在进行网络爬虫的时候,有很多网站都有反爬机制,比如你想抓点数据,结果发现每次请求都带一堆奇奇怪怪的参数 —— 什么 timestamp 签名、AES 加密的字段,还有各种 Token 令牌,跟密码…...
AB包介绍及导出工具实现+AB包资源简单加载
Resource原理 项目中建立Resources目录,资源导入内部 生成项目包 资源文件存储路径 结论:存储在Resources下的资源,最终会存储在游戏的主体包中,发送给用户,手机系统上,如果需要做资源的更新,是…...
TCP/IP协议簇
文章目录 应用层http/httpsDNS补充 传输层TCP1. 序列号与确认机制2. 超时重传3. 流量控制(滑动窗口机制)4. 拥塞控制5. 错误检测与校验6. 连接管理总结 网络层ARP**ARP 的核心功能**ARP 的工作流程1. ARP 请求(Broadcast)2. ARP 缓…...
vector的模拟实现01
文章目录 vector的模拟实现构造函数析构函数迭代器容量sizecapacityreverse 遍历下标[] 修改push_backpop_backinsert 结语 我们大家有又见面了,给生活加点</font color red>impetus!!开启今天的编程之路 今天我们来学习vector。了解一…...
信息学奥赛一本通 1609:【例 4】Cats Transport | 洛谷 CF311B Cats Transport
【题目链接】 ybt 1609:【例 4】Cats Transport 洛谷 CF311B Cats Transport 【题目考点】 1. 动态规划:斜率优化动规 【解题思路】 解法1:设a点的前缀和 输入的 d d d序列是从 d 2 d_2 d2到 d n d_n dn,共n-1个数字。人…...
shared_ptr和 weak_ptr的详细介绍
关于 shared_ptr 和 weak_ptr 的详细介绍及使用示例: 1. shared_ptr(共享所有权智能指针) 核心特性 引用计数:记录当前有多少个 shared_ptr 共享同一个对象。自动释放:当引用计数归零时,自动释放对象内存…...
electron打包vue2项目流程
1,安装一个node vue2 的项目 2,安装electron: npm install electron -g//如果安装还是 特比慢 或 不想安装cnpn 淘宝镜像查看是否安装成功:electron -v 3,进入到项目目录:cd electron-demo 进入项目目录…...
Baklib驱动企业知识管理数字化转型
Baklib驱动知识资产激活 在信息碎片化与数据爆炸的产业环境下,企业知识中台正成为重构组织智慧的核心枢纽。Baklib通过构建全生命周期知识管理模型,将分散于邮件、文档及协作系统的非结构化数据转化为可检索、可分析的标准化资产。其内置的智能分类引擎…...
Elasticsearch 高级
Elasticsearch 高级 建议阅读顺序: Elasticsearch 入门Elasticsearch 搜索Elasticsearch 搜索高级Elasticsearch高级(本文) 1. nested 类型 1.1 介绍 Elasticsearch 中的 nested 类型允许你在文档内存储复杂的数据结构,比如一个…...
1--当「穷举」成为艺术:CTF暴力破解漏洞技术从入门到入刑指南(知识点讲解版)
当「穷举」成为艺术:CTF暴力破解漏洞技术从入门到入刑指南 引言:论暴力破解的哲学意义 “世界上本没有漏洞,密码设得简单了,便成了漏洞。” —— 鲁迅(并没有说过) 想象你是个不会撬锁的小偷,面…...
jdk 支持路线图
https://www.oracle.com/java/technologies/java-se-support-roadmap.html 按照路线图得知,在2025.09 发布openjdk 25,是一个LTS版本。...
VsCode启用右括号自动跳过(自动重写) - 自录制gif演示
VsCode启用右括号自动跳过(自动重写) - 自录制gif演示 前言 不知道大家在编程时候的按键习惯是怎样的。输入完左括号后编辑器一般会自动补全右括号,输入完左括号的内容后,是按→跳过右括号还是按)跳过右括号呢? for (int i 0; i < a.s…...
Android设计模式之模板方法模式
一、定义: 定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 二、结构: AbstractClass抽象类:定义算法的骨架,包含模板方法和若干…...
纯个人整理,蓝桥杯使用的算法模板day1(dfs、bfs)
算法索引 dfs(深度优先搜索)bfs(广度优先搜索)迷宫树结构 dfs(深度优先搜索) 功能: 适合搜索所有的解 代码模板: class Solution{public void dfs(int[][] graph, int i, int j){i…...
【第34节】windows原理:PE文件的导出表和导入表
目录 一、导出表 1.1 导出表概述 1.2 说明与使用 二、导入表 2.1 导入表概述 2.2 说明与使用 一、导出表 1.1 导出表概述 (1)导出行为和导出表用途:PE文件能把自身的函数、变量或者类,提供给其他PE文件使用,这…...
Spring Boot事务管理详解(附银行转账案例)
一、事务基础概念 事务的ACID特性: 原子性(Atomicity):操作要么全部成功,要么全部失败一致性(Consistency):数据在事务前后保持合法状态隔离性(Isolation)&…...
(头歌作业—python)3.2 个人所得税计算器(project)
第1关:个人所得税计算器 任务描述 本关任务:编写一个个人所得税计算器的小程序。 相关知识 个人所得税缴纳标准 2018 年 10 月 1 日以前,个税免征额为 3500 元/月,调整后,个税免征额为 5000 元/月, 7 级超…...
在一个scss文件中定义变量,在另一个scss文件中使用
_variables.scss文件 : $line-gradient-init-color: linear-gradient(90deg, #8057ff 0%, #936bff 50%, #b892ff 100%); $line-gradient-hover-color: linear-gradient(90deg, #936bff 0%, #b892ff 50%, #f781ce 100%); $line-gradient-active-color: linear-gradient(90deg, …...
【计网】网络交换技术之电路交换(复习自用)
复习自用的,处理得比较草率,复习的同学或者想看基础的同学可以看看,大佬的话可以不用浪费时间在我的水文上了 1.电路交换定义 电路交换是一种通信方法,在通信开始之前,源和目的地之间建立一条专用的物理路径…...
MacOS 安装open webui
open-webui 不是一个 Python 包,所以 pip install open-webui 会失败。它是一个独立的 Web UI 应用,通常通过 Docker 或 手动构建 来运行。 如何正确安装 Open WebUI? 你可以选择 Docker 方式(推荐)或 手动安装。 方法…...
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例9,TableView16_09 嵌套表格拖拽排序
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例9,TableView16_09 嵌…...
开启ipv6与关闭的区别
在运行P2P CDN时,开启IPv6与关闭IPv6存在以下核心区别,需从技术、合规、运营等维度综合评估: 一、性能与效率 开启IPv6的优势 更大地址空间:IPv6支持海量设备接入,解决IPv4地址枯竭问题,便于P2P CDN节点扩…...
Redis + Caffeine多级缓存电商场景深度解析
Redis Caffeine多级缓存 Redis Caffeine多级缓存电商场景深度解析一、实施目的二、具体实施2.1 架构设计2.2 组件配置2.3 核心代码实现 三、实施效果3.1 性能指标对比3.2 业务指标改善3.3 系统稳定性 四、关键策略4.1 缓存预热4.2 一致性保障4.3 监控配置Prometheus监控指标 …...
Leecode Hot50
文章目录 矩阵Solution73. 矩阵置零Solution54. 螺旋矩阵Solution48. 旋转图像Solution240. 搜索二维矩阵 II二叉树二叉树的四种遍历结果Solution94. 二叉树的中序遍历Solution104. 二叉树的最大深度Solution226. 翻转二叉树Solution101. 对称二叉树Solution543. 二叉树的直径S…...
解决 Gradle 构建错误:Could not get unknown property ‘withoutJclOverSlf4J’
解决 Gradle 构建错误:Could not get unknown property ‘withoutJclOverSlf4J’ 在构建 Spring 源码或其他基于 Gradle 的项目时,可能会遇到如下错误: Could not get unknown property withoutJclOverSlf4J for object of type org.gradle…...
C++ 初阶总复习 (16~30)
C 初阶总复习 (16~30) 目的16. 2009. volatile关键字的作用17. 2010.什么是多态 简单介绍下C的多态18. 2011. 什么是虚函数 介绍下C中虚函数的原理19. 2012 构造函数可以是虚函数嘛20. 2013.析构函数一定要是虚函数嘛?21. 2015. 什么是C中的虚…...
TDengine 中的异常恢复
简介 本章主要介绍在 TDengine 执行命令过程中发生异常,如何手工终于执行的任务。可以终止连接,线上查询及终止事务。 如果一个事务 在一个复杂的应用场景中,连接和查询任务等有可能进入一种错误状态或者耗时过长迟迟无法结束,…...
二层框架组合实验
实验要求: 1,内网IP地址使用172.16.0.0/16分配 2,SW1和sw2之间互为备份 3,VRRP/STP/VLAN/Eth-trunk均使用 4,所有PC均通过DHCP获取IP地址 5,ISP只能配置IP地址 6,所有电脑可以正常访问ISP路由器环回 实验思路顺序: 创建vlan eth-trunk 划分v…...
IP综合实验
1.配置eth-trunk进行绑定 [LSW1]interface Eth-Trunk 0 [LSW1-Eth-Trunk0]q [LSW1]interface g0/0/2 [LSW1-GigabitEthernet0/0/2]eth-trunk 0 [LSW1-GigabitEthernet0/0/2]int g0/0/3 [LSW1-GigabitEthernet0/0/3]eth-trunk 0 [LSW1-GigabitEthernet0/0/3]display et…...
2025年信息系统与未来教育国际学术会议(ISFE 2025)
基本信息 官网:www.icedcs.net 时间:2025年4月18-20日 地点:中国-深圳 简介 2025年信息系统与未来教育国际学术会议(ISFE 2025)作为第二届粤港澳大湾区教育数字化与计算机科学国际学术会议(EDCS 2025&…...
nacos 外置mysql数据库操作(docker 环境)
目录 一、外置mysql数据库原因: 二、数据库准备工作 三、构建nacos容器 四、效果展示 一、外置mysql数据库原因: 想知道nacos如何外置mysql数据库之前,我们首先要知道为什么要外置mysql数据库,或者说这样做有什么优点和好处&am…...
Windows 10 ARM64平台MFC串口程序开发
Windows 10 IoT ARM64平台除了支持新的UWP框架,也兼容支持老框架MFC。使得用户在Windows 10 IoT下可以对原MFC工程进行功能升级,不用在新框架下重写整个工程。熟悉MFC开发的工程师也可以在Windows 10 IoT平台下继续使用MFC进行开发。 本文展示MFC串口程序…...
怎么使用pm2启动和暂停后端程序(后端架构nodejs+koa)
首先查看自己的pm2进程 pm2 list 或者 pm2 status 如果什么进程都没有,但是你确实有后端程序运行在服务器上,使用以下查看pm2程序的启动用户 ps aux | grep pm2就可以看到具体的用户和进行的信息 接着转换到你要操作的pm2的进程用户下 sudo su - …...