实用指南:【C++】list容器的模拟实现
目录
1. 节点(list_node) 的结构
2. 哨兵位头节点
3. list容器的成员变量
4. 插入/删除操作
4.1 插入操作(insert)
4.2 删除操作(erase)
5. 迭代器的实现
6. 不同迭代器和const容器的限制
7. 重载operator->
8. 迭代器失效问题
insert操作
erase操作
9. 析构函数
10. 拷贝构造函数
11. 赋值运算符重载
传统写法
现代写法
12. C++11引入的列表初始化
13. 总结
C++标准库中的list底层是双向循环链表,这是一种与vector(动态数组)完全不同的数据结构,核心特点是节点独立存储,通过指针连接,因此在插入/删除操作上有独特优势。
1. 节点(list_node) 的结构
template
struct list_node
{
T _data; // 存储节点数据
list_node* _prev; // 正确:指向前一个节点
list_node* _next; // 正确:指向后一个节点
// 节点构造函数(初始化数据和指针)
list_node(const T& val = T())
: _data(val), _prev(nullptr), _next(nullptr) {}
};
2. 哨兵位头节点
曾经实现单链表的时候,进行尾插操作,那么我们要判断当前链表是否为空,如果链表为空,直接插入;如果链表不为空,找到尾节点再插入。为了简化边界判断,list中会额外创建一个哨兵位头节点(不存储实际数据),整个链表形成双向循环结构,链表为空时,哨兵位的_prev和_next都指向自己。
3. list容器的成员变量
list类内部只存储两个核心信息:
template
class list
{
private:
list_node* _head; // 指向哨兵位头节点的指针
size_t _size; // 记录有效元素个数(非必需,但方便快速获取大小)
};
4. 插入/删除操作
list的插入/删除操作远高于vector,核心原因是:只需修改指针,无需移动元素。
4.1 插入操作(insert)
//在 pos 迭代器指向的节点前插入val
iterator insert(iterator pos, const T& val)
{
Node* cur = pos._node; //pos 指向的节点
Node* prev = cur->_prev; //pos 前一个节点
Node* newnode = new Node(val);//创建新节点
//调整指针: prev newnode cur
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
++_size; //有效元素+1
return newnode; //返回指向新节点的迭代器
}
4.2 删除操作(erase)
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node; //要删除的节点
Node* prev = cur->_prev; //前一个节点
Node* next = cur->_next; //后一个节点
//调整指针: prev cur next
prev->_next = next;
next->_prev = prev;
delete cur; //释放节点内存
--_size; //有效元素-1
return next; //返回被删除元素的下一个有效迭代器
}
5. 迭代器的实现
list迭代器本质是节点指针的封装,通过重载++/--运算符实现遍历。
//普通迭代器(可修改元素)
template
struct list_iterator
{
typedef list_node Node;
typedef list_iterator Self;
Node* _node;
list_iterator(Node* node) : _node(node) {}
T& operator*() { return _node->_data; } // 返回非const引用,允许修改
T* operator->() { return &_node->_data; } // 返回非const指针
Self& operator++() { _node = _node->_next; return *this; }
Self operator++(int) { Self temp(*this); _node = _node->_next; return temp; }
Self& operator--() { _node = _node->_prev; return *this; }
Self operator--(int) { Self temp(*this); _node = _node->_prev; return temp; }
bool operator!=(const Self& s) const { return _node != s._node; }
bool operator==(const Self& s) const { return _node == s._node; }
};
实现 list 的const迭代器(const_iterator)的核心目标是:允许遍历元素但禁止通过迭代器修改元素的值,它的实现逻辑与普通迭代器(iterator)类似,需要通过修改解引用和箭头运算符的返回类型来限制写操作。
- 普通迭代器(iterator):解引用返回T&,箭头运算符返回T*,允许通过迭代器修改元素(*it = value 或 it->member = value)。
- const迭代器(const_iterator):解引用返回const T&,箭头运算符返回const T*,仅允许读取元素,禁止修改(*it 和 it->member 都是只读的)。
我们有两种方式实现它:
方式1:
直接复制普通迭代器的代码,仅修改operator*和operator->的返回类型,其余操作(++、--、比较等)完全复用,但是这种方式代码冗余,重复代码太多。
//const迭代器(仅可读,不可修改元素) template struct list_const_iterator { typedef list_node Node; typedef list_const_iterator Self; Node* _node; // 仍指向普通节点(节点本身可修改,但通过迭代器访问的元素不可修改) list_const_iterator(Node* node) : _node(node) {} // 核心区别:返回const引用/指针,禁止修改元素 const T& operator*() { return _node->_data; } // 只读 const T* operator->() { return &_node->_data; } // 只读 Self& operator++() { _node = _node->_next; return *this; } Self operator++(int) { Self temp(*this); _node = _node->_next; return temp; } Self& operator--() { _node = _node->_prev; return *this; } Self operator--(int) { Self temp(*this); _node = _node->_prev; return temp; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; } };
方式2:
用模版参数复用代码,将普通迭代器和const迭代器的共性代码合并到一个模版中,仅通过参数控制是否为const。
template //Ref: T& 或 const T&; Ptr: T* 或 const T* struct list_iterator { typedef list_node Node; typedef list_iterator Self; Node* _node; list_iterator(Node* node) : _node(node) {} // 解引用和箭头运算符的返回类型由模板参数控制 Ref operator*() { return _node->_data; } // Ref为const T&时,返回只读引用 Ptr operator->() { return &_node->_data; } // Ptr为const T*时,返回只读指针 // 移动操作完全复用(与是否const无关) Self& operator++() { _node = _node->_next; return *this; } Self operator++(int) { Self temp(*this); _node = _node->_next; return temp; } Self& operator--() { _node = _node->_prev; return *this; } Self operator--(int) { Self temp(*this); _node = _node->_prev; return temp; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; } };
list容器在定义普通迭代器和const迭代器这两种具体类型时,主动明确地把它们的具体值(比如T&或const T&)传给list_iterator模版,从而生成了“能改”和“不能改”两种不同功能的迭代器。在list类提供const版本的begin()和end(),用于const对象的遍历:
template class list { private: typedef list_node Node; Node* _head; // 哨兵节点 size_t _size; public: //1.定义普通迭代器:Ref=T& Ptr=T* typedef list_iterator iterator; //2.定义const迭代器:Ref=const T& Ptr=const T* typedef list_iterator const_iterator; // 普通迭代器接口 iterator begin() { return _head->_next; } //第一个有效节点 iterator end() { return _head; } //哨兵节点(尾后位置) // const迭代器接口(供const对象使用) const_iterator begin() const { return _head->_next; } //第一个有效节点 const_iterator end() const { return _head; } //哨兵节点(尾后位置) // …… 其他成员函数(构造、push_back等,省略) };
遍历打印元素
template void print_container(const Container& con) { typename Container:: const_iterator it = con.begin(); //auto it = con.begin(); while (it != con.end()) { //*it += 10; 编译错误:const_iterator禁止修改元素 cout << *it << " "; //可以读取 ++it; } cout << endl; for (auto e : con) { cout << e << " "; } cout << endl; }
6. 不同迭代器和const容器的限制
void test_list2()
{
list lst = { 1,2,3 };
//1. 普通迭代器
list::iterator it = lst.begin();
*it = 10;//合法:可修改元素
++it;//合法:可移动迭代器
//2. const迭代器(元素不可修改的迭代器)
list ::const_iterator cit = lst.begin();
//*cit = 20; 报错:不能修改元素 (const_iterator特性)
++cit;//合法:可移动迭代器
//3. const修饰迭代器(迭代器变量本身不可修改),【实际这种迭代器几乎不用】
//情况A:const修饰普通迭代器
const list::iterator const_it1 = lst.begin();
*const_it1 = 30;//合法:普通迭代器仍可修改元素 但只能改第一个元素,使用场景极窄!
//++const_it1; //报错:迭代器变量本身不可移动 无法修改第二个、第三个元素
//情况B:const修饰const_iterator(迭代器不可移动,元素也不可修改)
const list::const_iterator const_it2 = lst.begin();
//*const_it2 = 40; //报错:不能修改元素
//++const_it2; //报错:迭代器本身不可移动
cout "容器的状态不可变"->而容器的状态不仅包括内部指针,还包括其管理的元素
const list const_lst = { 4,5,6 };
list::const_iterator clst = const_lst.begin(); //const容器只能返回const迭代器
//*clst = 50; //报错:const容器元素不可修改
++clst; //合法:迭代器本身可移动
//const_lst.push_back(7); //报错:容器对象状态不可改变(包括容器长度、节点数量、节点存储的数据等),
//push_back是非const的成员函数,const容器只能调用const成员函数,添加、删除、清空元素同样都不可以。
}
7. 重载operator->
为什么要重载operator->呢?
假设链表存储自定义对象,相当于链表的每一个节点存储的都是对象。
struct Person
{
string name;
int age;
};
list people; // 存储Person对象的链表
当我们用迭代器it指向链表中的某个Person对象时,需要访问其成员(如name或age),如果没有重载operator->,访问方式会是:
list lst; lst.push_back({ "张三", 20 }); auto it = lst.begin(); //迭代器,指向第一个Person对象 (*it).name; //先解引用迭代器得到Person对象,再用.访问成员
当写*it时,本质是调用 it.operator*(),这个函数会通过迭代器的_node找到对应的链表节点,返回节点中_data的引用即(Person&类型),*it等价于 “迭代器所指向节点中的Person对象。
operator->的重载逻辑
T* operator->() { return &_node->_data; // 返回指向节点中数据的指针 }
而有了operator->重载后,访问方式可以简化为:
list lst; lst.push_back({ "张三", 20 }); auto it = lst.begin(); //迭代器,指向第一个Person对象 it->name; //迭代器用->访问成员(看似一步,实则两步)编译器会自动拆解为(it.operator->())->name
编译器会把 it->name; 这个表达式自动拆解为两步:
- 第一步:显示触发重载操作,执行 it.operator->(),得到 Person* 指针(指向节点中存储的Person对象的指针),取名叫 p。
- 第二步:再对 p 执行 “原生 -> 操作”:p->name(这一步是隐藏的,编译器自动补全)。
总共 2 次-> 相关操作,其中第 2 次是编译器按标准自动隐藏执行的,目的是让迭代器用起来和原生指针一样简单。
不管是标准库还是自定义的迭代器,只要正确重载了operator->,编译器就会自动补充第二次->,这是C++标准规定的行为,目的是让类类型的对象可以模拟指针的->操作。
这种设计的目的是让迭代器的用法和原生指针完全一致,降低使用成本,如果编译器不自动补充第二次->,用户就必须写成( it.operator->( ) ) -> name,不仅麻烦,还会让迭代器和原生指针的用法脱节,违背了迭代器“模拟指针”的设计初衷。
8. 迭代器失效问题
在C++中,list的迭代器失效问题和vector 等连续内存容器有显著区别,这源于list当节点式存储结构(非连续内存)。
insert操作
插入元素时,只会在目标位置创建新节点,并调整相邻节点的指针,不会改变原有任何节点的内存地址,因此,所有已存在的迭代器(包括插入位置的迭代器)都不会失效。
标准库实现insert,返回值为指向新插入元素的迭代器,插入后可直接通过返回值操作新元素。【4.1插入操作】
list lst = {1, 3, 4}; auto it = lst.begin(); // 指向1的迭代器 lst.insert(++it, 2); // 在3前插入2,lst变为{1,2,3,4} // 原it仍指向3(有效),新节点2的迭代器需通过insert返回值获取
erase操作
删除元素时,被删除的节点会被销毁,指向该节点的迭代器会失效;但其它节点的内存地址没变,因此除了被删除节点的迭代器外,其他所有迭代器仍然有效。
erase返回指向被删除元素的下一个元素的迭代器,避免使用已失效的迭代器。【4.2删除操作】
//删除偶数 std::list lst = {1, 2, 3, 4}; auto it = lst.begin(); while (it != lst.end()) { if (*it % 2 == 0) { //lst.erase(it);it已失效,不能再使用,下一次判断会导致未定义行为 it = lst.erase(it); //用返回值更新it(指向被删元素的下一个) } else { ++it; // 奇数不删除则正常移动迭代器 } } // 最终lst为{1,3}
9. 析构函数
第一种实现:
~list()
{
Node* current = _head->_next; //从第一个有效节点开始遍历
while (current != _head)
{
Node* nextNode = current->_next; //先保存下一个节点
delete current; //销毁当前节点
current = nextNode; //移动到下一个节点
}
//销毁哨兵节点
delete _head;
_head = nullptr;
//重置大小
_size = 0;
cout << "链表已销毁" << endl;
}
第二种实现:复用clear() 和 erase()
~list()
{
clear();
delete _head; //释放哨兵节点
_head = nullptr;
_size = 0;
cout << "链表已销毁" << endl;
}
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it); //复用erase逻辑删除单个节点
}
}
10. 拷贝构造函数
在链表中,必须手动实现拷贝构造函数,不能依赖编译器默认生成的默认拷贝构造函数,核心原因是:编译器默认的拷贝构造函数是浅拷贝,仅复制指针值,导致多个链表共享节点内存,引发双重释放、野指针等问题(原链表和拷贝出的新链表会共享同一份节点内存,当其中一个链表析构时,导致另一个链表的指针变成野指针,指向已释放的内存,若对其中一个链表修改,会直接影响另一个链表,两个链表析构时,会双重释放导致程序崩溃)。
手动实现拷贝构造函数需要完成深拷贝:为新链表创建独立的节点,确保每个链表拥有自己的资源。
//空初始化 (创建独立的哨兵节点_head,形成自循环,_size为0) void empty_init() { _head = new Node(); _head->_next = _head; _head->_prev = _head; _size = 0; } //拷贝构造函数 lt2(lt1) list(const list& lt) { empty_init(); //初始化新链表的基础结构 for (auto& e : lt) { push_back(e); } }
11. 赋值运算符重载
传统写法
//赋值运算符重载(传统写法)
list& operator=(const list& lt)
{
//处理自赋值(避免释放自身资源后无法拷贝)
if (this != <)
{
//释放当前链表所有节点
clear();
//从lt复制元素到当前链表
for (auto& e : lt)
{
push_back(e);
}
}
return *this;//返回自身引用(支持连续赋值如a=b=c)
}
现代写法
//交换两个链表的成员
void swap(list& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//赋值运算符重载(现代写法)
//利用拷贝构造函数创建临时副本,再交换成员变量 lt1 = lt3
list& operator=(list lt) //形参lt是按值传递,调用拷贝构造函数创建lt3的临时副本lt
{
swap(lt); //交换当前对象与临时副本的资源
return *this; //临时副本离开作用域自动析构
}
//等价写法
//list& operator=(const list& lt)
//{
// list tmp(lt); //显式调用拷贝构造函数创建lt的临时副本
// swap(tmp);
// return *this;
//}
12. C++11引入的列表初始化
C++及以后标准中引入了列表初始化,使用方式:
std::list lt{ 1,2,3,4 }; //等价于std::list lt = { 1,2,3,4 }; std::list lt2({1,2,3,4});//显示传参,语法较传统
上面代码执行过程:
- 当编译器看到{1,2,3,4}这个花括号初始化列表时,会自动生成一个std::initializer_list<int>类型的临时对象,并让它“包裹”花括号里面所有的元素。(具体操作:编译器会在栈上创建一个临时的int数组,存储1,2,3,4。)
- 调用std::list里面接收initializer_list<int>参数的构造函数,将步骤1创建的临时对象作为实参传递给这个构造函数。
- std::list构造函数内部会遍历这个临时对象,创建链表节点。
- 当lt构造完成后,临时对象和它指向的临时数组自动销毁。
像std::list、std::vector等标准容器都专门提供了接收initializer_list<T>参数的构造函数,对于自定义实现list,我们也想用这种方式初始化,就需要添加这个构造函数,例如:
template class list { public: //接收initializer_list的构造函数 list(initializer_list il) { empty_init(); for (auto& e : il) { push_back(e); } } …… };
13. 总结
namespace cat
{
//定义节点的结构
template
struct list_node
{
T _data;
list_node* _next;
list_node* _prev;
list_node(const T& data = T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{}
};
//实现迭代器来模拟指针
template
struct list_iterator
{
typedef list_node Node;
typedef list_iterator Self;
Node* _node; //成员变量 _node指针变量专门用于指向链表中的某个节点
list_iterator(Node* node)
:_node(node)
{}
Ref operator*() //返回引用 *it = 100;
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()//前置++ 指针++返回指针本身,迭代器++返回迭代器本身
{
_node = _node->_next;
return *this;
}
Self operator++(int) //后置++(有int占位参数):先保存当前状态,再移动,再返回原状态
{ //后置++不能返回引用,tmp是局部临时对象,出了作用域会销毁,如果返回引用,会导致悬垂引用问题(引用的对象已不存在)
Self temp(*this); //保存当前迭代器 调用拷贝构造函数构造temp
_node = _node->next;
return temp; //返回原状态
}
Self& operator--()
{
_node = _node->prev;
return *this;
}
Self operator--(int)
{
Self temp(*this);
_node = _node->_prev;
return temp;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
//定义链表
template
class list
{
private:
typedef list_node Node;
Node* _head; //指向哨兵节点
size_t _size;
public:
typedef list_iterator iterator;
typedef list_iterator const_iterator;
iterator begin()
{
/*iterator it(_head->_next);
return it;*/
return _head->_next;//返回指向第一个元素节点的迭代器,函数返回的类型(Node*)和函数声明的返回类型(iterator对象)不匹配
//iterator类有单参数构造函数,支持隐式类型转换,自动调用构造函数将_head->next作为参数,创建一个临时的iterator对象
//等价于return iteartor(_head->next); (显示调用构造函数)
}
iterator end()
{
return _head;
}
const_iterator begin()const
{
return _head->_next;
}
const_iterator end()const
{
return _head;
}
//空初始化
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//无参构造函数
list()
{
empty_init();
}
list(initializer_list il) //接收initializer_list参数的构造函数
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
}
//拷贝构造函数 lt2(lt1)
list(const list& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
//赋值运算符重载(现代写法) lt1 = lt3
list& operator=(list lt)
{
swap(lt);
return *this;
}
//析构函数
~list()
{
clear();
delete _head;
_head = nullptr;
cout & lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//尾插
void push_back(const T& x)
{
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
//插入数据
iterator insert(iterator pos, const T& val)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(val);
//prev newnode cur
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
++_size;
return newnode;//返回指向新节点的迭代器
}
//删除数据
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
//prev cur next
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next; //返回被删除元素的下一个有效迭代器
}
//尾删
void pop_back()
{
erase(--end());
}
//头删
void pop_front()
{
erase(begin());
}
size_t size() const
{
return _size;
}
//判空
bool empty() const
{
return _size == 0;
}
};
template
void print_container(const Container& con)
{
typename Container:: const_iterator it = con.begin(); //auto it = con.begin();
while (it != con.end())
{
//*it += 10;// error!
cout lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
print_container(lt);
list lt2 = { 1,2,3,4 };//调用接收initializer_list参数的构造函数
list lt3({ 1,2,3,4 }); //同上
const list& lt4{ 1,2,3 }; //lt4是临时对象的引用
print_container(lt4);
}
相关文章:
实用指南:【C++】list容器的模拟实现
实用指南:【C++】list容器的模拟实现pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important…...
windows系统缺失DLL库文件下载方法
https://cn.dll-files.com/ windows系统缺失DLL文件下载方法_dll文件下载官网-CSDN博客...
更为通用的决策单调性
学习自 在线决策单调性地皮还能单老哥分治做? - 洛谷专栏 决策单调性最为常用的为分治和二分队列,前者要求离线,后者必须快速处理两个位置的转移,都有一定的局限性,其他算法大部分码量较长,很难应用。 但我们还有一种好写且能维护复杂转移的写法, 简易版 LARSCH 算法,一…...
一文读懂 PHP PSR 接口 PSR-3、PSR-7、PSR-11、PSR-15 完整指南
一文读懂 PHP PSR 接口 PSR-3、PSR-7、PSR-11、PSR-15 完整指南 现代 PHP 的选择很多。这本来是好事,但一到升级框架、替换 Logger,或在团队间统一服务时,你会发现:看不见的耦合(类型、方法签名、约定)会把小改动变成大手术。 本文用通俗的话讲清四个关键标准——PSR-3(…...
2025模拟赛Round9
T1 T2 T3 T4 T5 T6\({\color{#F39C11} 普及− }\) \({\color{#FFC116} 普及/提高− }\) \({\color{#3498DB} 提高+/省选− }\) \({\color{#3498DB} 提高+/省选− }\) \({\color{#3498DB} 提高+/省选− }\) \({\color{#3498DB} 提高+/省选− }\)参赛网址:https://boyacoding.cn…...
NOIP2025模拟赛19
T1 T2 T3 T4\({\color{#3498DB} 提高+/省选− }\) \({\color{#3498DB} 提高+/省选− }\) \({\color{#9D3DCF} 省选/NOI− }\) \({\color{#3498DB} 提高+/省选− }\)参赛网址:https://oj.33dai.cn/d/TYOI/contest/68919c89c5d9c2f14c1a537f T2,T4搭建未完成 T1 人才计数【NOIP2…...
Qt/C++开发监控GB28181系统/公网对讲/代码实现28181语音对讲/采集本地麦克风数据/支持udp和tcp模式
一、前言说明 按照国标的文档,语音对讲这块,并没有强制要求支持公网对讲,所以当初设备厂家做的都是支持的udp对讲,而且按照国标协议要求,对讲是先发一个语音对讲广播到设备,设备收到后,主动发起对讲到服务端,和视频点播是反着来的,这样的话数据的传输模式只能设备控制…...
P3195 [HNOI2008] 玩具装箱 (斜率优化)
题目描述 一道不限段数的分段问题,要求给出 \(n\) 个元素,求出分任意组所产生的最小代价。 思路 我们可以分为两步来求解这个问题,暴力转移与优化。 The First Step 暴力转移 考虑暴力DP,根据题目描述,每个容器之中玩具的编号都是连续的,并且不限容器数量,状态就很好定义…...
DBeaver使用指南
概述 由于众所周知的原因,公司不容许使用非授权正版软件。datagrip本身挺不错的,但是要倒腾激活码之类的,比较麻烦。只能从开源社区寻找好用的DBMS GUI工具。 发现DBeaver挺不错,功能很全,但是默认的配置不够好用、字体、样式等都不友好,需要打磨一番才行。 下文重点关注…...
sh-2025模拟赛
CSP-J 模拟(九)题目解答 一、单项选择题(每题2分,共30分) 1. 进制转换计算 答案:C 解析:先将八进制数\((2025)_8\)转换为十进制: \(2\times8^3 + 0\times8^2 + 2\times8^1 + 5\times8^0 = 2\times512 + 0 + 16 + 5 = 1024 + 21 = 1045\)。 将十六进制数\((2025)_{16}\)…...
C++ day7 - 指南
C++ day7 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size: 14px !i…...
读人形机器人11娱乐领域
读人形机器人11娱乐领域1. 机器人表演者与艺术家 1.1. 在人类历史的宏大画卷中,艺术与娱乐一直是我们内心欲望、恐惧和抱负的映照 1.2. 音乐、舞蹈和戏剧表演中的机器人不再是科幻的虚构,而是正在重塑艺术表现形式的崭新现实 1.3. 机器人表演者和艺术家正在通过引入新的表现形…...
Java 注解机制全解析:原理、用途与框架中的实战
注解(Annotation)作为 Java 语言自 JDK5 引入以来的一项重要元编程特性,已经成为现代 Java 开发不可或缺的一部分。无论是 Spring 框架中的依赖注入、事务控制,还是 Hibernate 的对象关系映射,抑或是 Lombok 对代码生成的辅助,注解几乎贯穿于 Java 的开发全过程。 本文将…...
模板集
考虑到作为一名 Oier 有很多需要掌握的模板,所以整合了一下以前的专栏,就变成现在这样了! 有问题请加 qq 3848603482。可以帮你讲解。 给萌新的代码建议 尽量不要写全局变量,容易弄混不方便调试,要用了再创建。没有必要手写栈之类的,STL要了解多一点。不要写#define int …...
暑假
P2569 https://www.luogu.com.cn/problem/P2569 参考这篇。 /*单调队列优化dp买入股票的转移方程j是顺序枚举的,因为是买入股票,手中的股票应该是越来越多的, 当前的决策有可能在后面(j更大)的时候用到,所以你需要先求出来, 同理,卖出股票时,你手中的股票是越来越少的…...
做题记录
P1248 加工生产调度 比较妙的一道题,这里我们可以直接把 A 与 B 的最小值碾一遍,注意 B 操作时间要与 A 操作时间取最大值。...
课程助教工作总结
在上学期,我有幸担任了《数字电路与逻辑设计》课程的助教。这是一次极其宝贵和充实的经历,不仅巩固和深化了我的专业知识,更锻炼了我的沟通、组织和解决问题的能力。现将本学期的工作情况总结如下: 一、 助教工作的具体职责和任务 我的工作核心是成为连接主讲教师与学生的桥…...
6G 驱动的智慧城市新格局
引言随着科技的不断进步,6G 驱动的智慧城市新格局 正逐渐走向应用前沿。它不仅推动了相关产业的发展,也在改变人类社会的运作方式。本文将从背景、核心技术、应用案例、挑战与趋势、总结几个方面对其进行系统分析。 背景 📊6G 驱动的智慧城市新格局 的出现,是社会需求、政…...
SHA-1 证书淘汰警告:网站管理员需紧急验证TLS安全性
微软宣布自2017年5月9日起,Edge和IE11浏览器将逐步阻止SHA-1签名的TLS服务器证书。本文详细解析三阶段淘汰计划、受影响证书类型验证方法及企业应对方案,涉及证书链验证和弱签名日志收集技术。こんにちは、村木ゆりかです。 以前よりマイクロソフト セキュリティ アドバイザリ…...
数字孪生在制造业中的应用
引言在当前快速发展的科技环境中,数字孪生在制造业中的应用 已经成为学术界与产业界广泛关注的主题。本文将通过背景、核心技术、应用案例、挑战与趋势、总结六个部分进行系统性分析。 背景 📊数字孪生在制造业中的应用 的兴起与社会发展需求密切相关。随着数字化转型的加速…...
device第一周个人作业
一. 自我介绍 回顾我的学习经历,虽然没有什么惊天动地的大成就,但也没有一些值得骄傲的小闪光点。兴趣爱好是科幻和军事读物,以前喜欢跟朋友一起打比赛,现在发现朋友在打假赛…… 二.现状、经验与计划 (1)当前技能树与技术偏好 当前技能树: 我可以使用基础的C语言和java…...
Java 在移动开发与跨平台应用中的应用
随着智能手机的普及,移动应用已经成为数字化转型的重要载体。移动端承载着用户的核心交互,而跨平台开发趋势也不断兴起, 旨在减少多端重复开发成本。在这一过程中,Java 一直是移动开发的核心语言, 尤其在 Android 生态中占据主导地位,并通过跨平台技术、移动后端服务、混…...
5G 技术与远程教育
引言在当前快速发展的科技环境中,5G 技术与远程教育 已经成为学术界与产业界广泛关注的主题。本文将通过背景、核心技术、应用案例、挑战与趋势、总结六个部分进行系统性分析。 背景 📊5G 技术与远程教育 的兴起与社会发展需求密切相关。随着数字化转型的加速,全球化的扩展…...
5G 技术在工业互联网的应用
引言在当前社会与科技的双重推动下,5G 技术在工业互联网的应用 已成为一个举世瞩目的议题。它不仅仅是技术层面的创新,更代表着社会运行模式的转型。本文将从背景、核心技术、应用案例、挑战与趋势、总结几个方面进行深入探讨。 背景 📊5G 技术在工业互联网的应用 的发展源…...
一键部署ftp脚本
在 Ubuntu(root) 上搭建标准 FTP(vsftpd)服务器,带被动模式端口、用户创建、目录与权限配置,可直接落地执行。一、部署脚本(保存为 setup_ftp.sh,root 执行) #!/usr/bin/env bash set -euo pipefail### ====== 可按需修改的变量 ====== FTP_USER="ftpuser" …...
PySimpleGUI安装4.60.5老版本安装教程!
PySimpleGUI安装4.60.5老版本安装教程! ━━━━━━━━━━━━━━━━━━━━━━ https://download.csdn.net/download/mzl_18353516147/89506033?utm_medium=distribute.pc_relevant_download.none-task-download-2defaultBlogCommendFromBaiduRate-2-89506033-downlo…...
PySimpleGUI-免注册版本
PySimpleGUI 开始注册了,怎样能免注册使用早期版本? 若需免注册使用 PySimpleGUI 早期版本,可通过以下两种方法实现,具体操作及原理如下: 方法一:通过 pip 指定版本安装(推荐) 操作步骤:卸载现有版本(若已安装新版):pip uninstall PySimpleGUI安装指定旧版本(如 4…...
高三闲话 #1
APJ 三个银牌高考 699,我一个银牌考个 233 不过分吧高三生活还是如期而至了,写点高三闲话感觉太有道理了。不过我得先说明这篇文章的目的不是传播负能量,事实上我最近还是蛮积极的,只不过我无法回避这些我内心里的真实想法,而且感觉写出来很有意思,所以写一写。如果让我用…...
三大免费CDN推荐:安全防护强、稳定性卓越、加载飞速,长期使用超安心
三大免费CDN推荐:安全防护强、稳定性卓越、加载飞速,长期使用超安心1. 将盾CDN:防御与速度双卓越,建站首选利器 将盾CDN是一款专注于安全与性能的CDN服务,其免费版本在防御强度、国内访问速度及功能实用性上表现亮眼,无论是企业官网还是个人站点,都能提供可靠保障,是当…...
PySimpleGUI 开始注册了,怎样能免注册使用早期版本?
若需免注册使用 PySimpleGUI 早期版本,可通过以下两种方法实现,具体操作及原理如下: 方法一:通过 pip 指定版本安装(推荐) 操作步骤:卸载现有版本(若已安装新版):pip uninstall PySimpleGUI安装指定旧版本(如 4.60.5):pip install PySimpleGUI==4.60.5该版本在官方…...
全屏与退出全屏功能
全屏与退出全屏功能 📄 Fullscreen API 使用说明 通过浏览器提供的 Fullscreen API,你可以实现在任意 HTML 元素上的全屏展示与退出全屏效果,适用于视频播放器、看图模式、报表展示等场景。🧩 功能说明支持进入全屏 支持退出全屏 支持检测当前是否处于全屏状态 兼容多浏览…...
二十多年.NET老兵重返技术博客
缘起:博客园,我们的技术家园 自2005年入驻博客园,2006年开始撰写技术博客,直至2013年,我将博客园视为.NET技术交流的圣地。这里纯净、专注的技术氛围,以及众多技术精英的真诚分享,使其成为我们共同的网上家园。然而,由于工作性质的变化,我在2013年后逐渐淡出了技术博客…...
5月杂题
一点骨灰?臭不要脸的回到了机房,感觉大家越来越强了,膜拜。 想到什么写什么。多校清北营训练「清华场」B. 绕口令/gym100162B题意:定义一个字符串为好的当且仅当其中没有两个相邻字符相同,问你对 \(k\in[1,n]\),能否删去原字符串中连续 \(k\) 个字符得到一个好的字符串。…...
uv,下一代Python包管理工具
uv,下一代Python包管理工具 https://segmentfault.com/a/1190000047202911 什么是uv uv(Universal Virtual)是由Astral团队(知名Python工具Ruff的开发者)推出的下一代Python包管理工具,使用Rust编写。它集成了包管理、虚拟环境、依赖解析、Python版本控制等功能,它聚焦于…...
阅读 |《虚空》观后感以及一些想法——万物简史
保持学习,保持记录,保持思考—————— 啊啊啊,真的有好多想要去做的事情,好多想要体验的事情,真的没法同时去学习所有的事物,那是多么的令人感到幸福 这篇文主要就记下看完《虚空》之后的想法以及之后和AI的讨论 首先呢我初读这本书感觉很枯燥,但一整篇看下来之后还是…...
Python进阶必看:深入解析yield的强大功能
https://segmentfault.com/a/1190000045317342?utm_source=sf-similar-article Python进阶必看:深入解析yield的强大功能 头像 涛哥聊Python 2024-10-22 四川 阅读 3 分钟 头图 大家好,我是涛哥,本文内容来自 涛哥聊Python ,转载请标原创。 更多Python学习内容:http://ip…...
polyglot,一个有趣的 Python 库!
https://segmentfault.com/a/1190000045317129 polyglot,一个有趣的 Python 库! 头像 涛哥聊Python 2024-10-02 四川 阅读 4 分钟 头图 大家好,我是涛哥,本文内容来自 涛哥聊Python ,转载请标原创。 更多Python学习内容:http://ipengtao.com 今天为大家分享一个有趣的 Py…...
个人介绍与博客初建
大家好!我是 李祺,很高兴能通过博客园这个平台和大家交流。我是一名计算机相关专业的学生,在平时生活里,音乐、旅途和美食是三大支柱。耳机里永远循环着不同语言的旋律:华语流行的细腻歌词、K-pop 的强劲节奏、英文歌的叙事感,都能让我在忙碌时快速切换状态。 旅游是我探…...
PySimpleGUI,一个神奇的 Python 库!
https://segmentfault.com/a/1190000045317132?utm_source=sf-similar-article#item-3-3 大家好,我是涛哥,本文内容来自 涛哥聊Python ,转载请标原创。 更多Python学习内容:http://ipengtao.com 今天为大家分享一个神奇的 Python 库 - PySimpleGUI。 Github地址:https://…...
c++ 的拷贝构造函数
当类成员变量有指针类型的时候,要自定义拷贝函数,不要使用默认的拷贝构造函数.因为默认的拷贝构造构造函数是浅拷贝是位拷贝的,只拷贝指针指向的空间而没有把空间里的内容一起拷贝过来,这样会导致改变其中的一个,另一个也会跟着一起改变(老婆也都一样的了)class Teacher{ publi…...
变异
n可以理解成特征的对比 是的,完全可以这么理解!✅ 一句话总结:n 就是“你用多少种不同的视角/特征去衡量这两段区间的关系”。✅ 举个例子: 你把两条 150 kb 区间(X 和 Y)拿来比较,可以问:视角(特征) 具体问法 对应通道覆盖度视角 “X 和 Y 哪边 reads 更多?” read…...
【笔记】类欧几里得算法
类欧几里得算法(之后或许会加上万欧)直接用例子开始吧: \[给定 a,b,c,n,求解\sum_{i = 0}^n\left\lfloor\frac{ai+b}{c}\right\rfloor \]我们记 \(f(a,b,c,n) = \sum_{i = 0}^n\left\lfloor\frac{ai+b}{c}\right\rfloor\) 首先考虑 \(a \ge c\) 或 \(b \ge c\) 的情况: 先…...
AQS的一些思考
AQS-AI问答AQS是怎么实现CountDown AQS 为 CountDownLatch 提供了核心的 状态管理(state 作为计数器) 和 线程排队 / 唤醒机制(CLH 队列):通过 tryAcquireShared 检查计数器是否为 0,决定线程是否需要等待; 通过 tryReleaseShared 原子递减计数器,当计数器归零时唤醒所…...
DearPyGui-最强大的一款Python GUI工具
https://zhuanlan.zhihu.com/p/200754892 其实,在Python中不乏知名的UI构建工具包,例如,Tkinter,PyQT / PySide,wxPython,Kivy,PySimpleGui。这些工具包都很强大,但是,也非常繁琐。 开发一个框架要付出的精力和代码量几乎和核心逻辑相差无几,这与Python崇尚的简单是相…...
2 模型评估与选择
目录P18P23P25 P18 可以好好想一下ROC曲线是如何形成的:我们设置不同的二分类的阈值,就会有不同的(TPR,FPR)对;如果我们将所有数据按照其置信度从大到小排序,然后让阈值逐渐减小,并在ROC曲线上进行描点,那么我们可以发现,如果遇到一个正例,那么当前点会竖直向上走\(\fr…...
TY-290ES计算器屏幕逆向
刷拼多多时看到个便宜的TY-290ES计算器,到手价只需要人民币11块多,还是个点阵屏,真是难以想象国产计算器能做这么便宜,买一个来研究下。廉价小商品大本营(浙江)发的货,,,先点亮看看,功能很少,比82ES还差点意思。屏幕点阵粗略估了下,应该是96x31像素的,顶部有一行图标…...
CF1559E
时间限制 \(2\,\text{sec}\) 空间限制 \(256\,\text{MB}\) 题目来源 CodeForces。 题目大意 给定 \(n\) 个区间 \([l_i,\, r_i]\) 和一个常数 \(m\) ,在每个区间内选出一个正整数 \(a_i\) 构成一个数列 ,满足\(\displaystyle\sum^{n}_{i=1}a_i \leq m\) , \(\gcd \{a_i \} …...
笔记 哈希
A - Description 给定字符串 \(S\) 和 \(T\),问你在 \(S\) 中 \(T\) 出现了几次。 \(1\le |S|,|T|\le 2\times 10^6\) A - Solution 首先暴力地来想。在 \(S\) 中找出所有长度为 \(|T|\) 的子串,然后暴力逐字符匹配,复杂度显然是 \(O(n^2)\) 的,考虑优化。(此处假设 \(|T|…...
题解:CF566A Matching Names
题意 有 \(n\) 名同学,每位同学有一个真名与一个笔名,都是一个由小写英文字母组成的字符串。每位同学可以选择一位同学,使自己的真名与那位同学的笔名相匹配,产生的贡献为其最长公共前缀,每位同学的笔名只能与一人匹配,求贡献总和的最大值。 思路 把笔名插入字典树,并在…...
Tarjan 求连通性相关
复健发现自己对于 Tarjan 的一些东西都有些搞不清了啊。整理一下。 如有错误/不足,欢迎指出。 先给一些定义:\(u\) 表示某个子树的根节点。 \(v\) 表示与 \(u\) 相连的点。如果是无向图那么不包含父亲。 \(fa\) 表示 \(u\) 在 DFS 生成树上的父亲节点。 \(dfn_u\),表示 \(u\…...