<C++> STL_list
1.list的介绍
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
2.list的使用
在学过vector接口后,list接口的使用也就非常容易了。大同小异!
构造函数
一、默认构造函数:
std::list<int> myList; // 创建一个空的整数链表
二、带有初始元素的构造函数:
std::list<int> myList = {1, 2, 3, 4, 5}; // 创建一个包含初始元素的整数链表
三、拷贝构造函数:
std::list<int> originalList = {1, 2, 3};
std::list<int> copiedList(originalList); // 通过拷贝构造函数创建一个与原链表相同的新链表
四、范围构造函数:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> myList(vec.begin(), vec.end()); // 从一个范围内的元素创建链表
五、构造函数指定元素个数和值:
std::list<int> myList(5, 42); // 创建一个包含5个值为42的元素的链表
六、使用自定义分配器的构造函数:
std::allocator<int> myAllocator;
std::list<int, std::allocator<int>> myList(myAllocator); // 创建一个使用自定义分配器的链表
operator=
list& operator= (const list& x);
#include <iostream>
#include <list>int main() {std::list<int> sourceList = {1, 2, 3, 4, 5};std::list<int> targetList;// 使用赋值运算符将源链表赋值给目标链表targetList = sourceList;// 输出目标链表的内容for (const auto& value : targetList) {std::cout << value << " ";}return 0;
}
迭代器
- 获取迭代器:
begin(): 返回指向链表第一个元素的迭代器。end(): 返回指向链表尾部后一个元素的迭代器(并不指向有效元素)。
- 反向迭代器:
rbegin(): 返回指向链表最后一个元素的反向迭代器。rend(): 返回指向链表头部前一个元素的反向迭代器(并不指向有效元素)。
- 迭代器移动操作:
++iterator: 将迭代器移动到下一个元素。--iterator: 将迭代器移动到前一个元素。
- 解引用迭代器:
*iterator: 获取迭代器指向的元素的值。->: 如果链表元素是对象,可以使用箭头运算符访问对象的成员。
正向迭代器遍历链表:
#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3, 4, 5};// 使用迭代器遍历链表并输出元素for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}// 使用 C++11 范围循环进行遍历(更简洁的方式)for (const int& value : myList) {std::cout << value << " ";}return 0;
}
反向迭代器遍历链表:
#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3, 4, 5};// 使用反向迭代器遍历链表并输出元素for (std::list<int>::reverse_iterator rit = myList.rbegin(); rit != myList.rend(); ++rit) {std::cout << *rit << " ";}return 0;
}
注意: 由于 list 是双向链表,迭代器支持前进和后退操作,但不支持随机访问。
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
capacity
size():返回链表中的元素数量。
std::list<int> myList = {1, 2, 3, 4, 5};
std::cout << "Size of the list: " << myList.size() << std::endl;
empty():检查链表是否为空。
if (myList.empty()) {std::cout << "The list is empty." << std::endl;
} else {std::cout << "The list is not empty." << std::endl;
}
max_size():返回std::list可以容纳的最大元素数量,考虑到系统的限制。
std::cout << "Maximum size of the list: " << myList.max_size() << std::endl;
resize(size_type n)和resize(size_type n, const T& value):改变链表的大小。第一个版本使用默认构造函数添加或移除元素,第二个版本将指定的值用作添加的元素值。
myList.resize(10); // 默认构造函数添加元素
myList.resize(8, 42); // 使用值 42 添加元素
resize的两种情况:
- 当所给值大于当前的size时,将size扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
- 当所给值小于当前的size时,将size缩小到该值。
element access
front():返回链表的第一个元素的引用。
std::list<int> myList = {1, 2, 3, 4, 5};
int firstElement = myList.front(); // 获取第一个元素的值
back():返回链表的最后一个元素的引用。
std::list<int> myList = {1, 2, 3, 4, 5};
int lastElement = myList.back(); // 获取最后一个元素的值
Modifiers
assign(): 用新的元素替换链表中的元素。
std::list<int> myList;
myList.assign({1, 2, 3, 4, 5}); // 用新元素替换现有元素
push_back(): 在链表末尾添加一个元素。
std::list<int> myList = {1, 2, 3};
myList.push_back(4); // 在末尾添加元素4
pop_back(): 移除链表末尾的元素。
std::list<int> myList = {1, 2, 3, 4};
myList.pop_back(); // 移除最后一个元素
push_front(): 在链表开头添加一个元素。
std::list<int> myList = {2, 3, 4};
myList.push_front(1); // 在开头添加元素1
pop_front(): 移除链表开头的元素。
std::list<int> myList = {1, 2, 3, 4};
myList.pop_front(); // 移除第一个元素
insert(): 在指定位置插入一个或多个元素。
std::list<int> myList = {1, 2, 5};
std::list<int>::iterator it = std::next(myList.begin()); // 获取第二个元素的迭代器
myList.insert(it, 3); // 在第二个位置插入元素3
erase(): 移除指定位置的一个或多个元素。
std::list<int> myList = {1, 2, 3, 4, 5};
std::list<int>::iterator it = std::next(myList.begin(), 2); // 获取第三个元素的迭代器
myList.erase(it); // 移除第三个元素
clear(): 移除所有链表中的元素,使其变为空链表。
std::list<int> myList = {1, 2, 3, 4, 5};
myList.clear(); // 清空链表中的所有元素
swap():用于交换两个链表的内容
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
list1.swap(list2); // 交换两个链表的内容
Operations
remove(): 移除链表中等于指定值的所有元素。
std::list<int> myList = {1, 2, 2, 3, 4, 2, 5};
myList.remove(2); // 移除所有值为2的元素
sort(): 对链表中的元素进行排序。
std::list<int> myList = {3, 1, 4, 1, 5, 9, 2, 6};
myList.sort(); // 对元素进行升序排序
reverse(): 反转链表中的元素顺序。
std::list<int> myList = {1, 2, 3, 4, 5};
myList.reverse(); // 反转元素的顺序,变为 {5, 4, 3, 2, 1}
merge(): 合并两个已排序的链表。合并后的list容器仍然有序
std::list<int> list1 = {1, 3, 5};
std::list<int> list2 = {2, 4, 6};
list1.merge(list2); // 合并两个已排序的链表
unique(): 移除链表中的重复元素(连续重复的元素只保留一个)。
std::list<int> myList = {1, 2, 2, 3, 3, 3, 4, 5, 5};
myList.unique(); // 移除连续重复的元素,变为 {1, 2, 3, 4, 5}
splice
splice函数用于两个list容器之间的拼接,其有三种拼接方式:
- 将整个容器拼接到另一个容器的指定迭代器位置。
- 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置。
- 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lt1(4, 2);list<int> lt2(4, 6);lt1.splice(lt1.begin(), lt2);//将容器lt2拼接到容器lt1的开头for (auto e: lt1) {cout << e << " ";}cout << endl;//6 6 6 6 2 2 2 2list<int> lt3(4, 2);list<int> lt4(4, 6);lt3.splice(lt3.begin(), lt4, lt4.begin());//将容器lt4的第一个数据拼接到容器lt3的开头for (auto e: lt3) {cout << e << " ";}cout << endl;//6 2 2 2 2list<int> lt5(4, 2);list<int> lt6(4, 6);lt5.splice(lt5.begin(), lt6, lt6.begin(), lt6.end());//将容器lt6的指定迭代器区间内的数据拼接到容器lt5的开头for (auto e: lt5) {cout << e << " ";}cout << endl;//6 6 6 6 2 2 2 2return 0;
}
注意: 容器当中被拼接到另一个容器的数据在原容器当中就不存在了。(实际上就是将链表当中的指定结点拼接到了另一个容器当中)
remove_if
remove_if函数用于删除容器当中满足条件的元素。
#include <iostream>
#include <list>
using namespace std;bool single_digit(const int &val) {return val < 10;
}int main() {list<int> lt;lt.push_back(10);lt.push_back(4);lt.push_back(7);lt.push_back(18);lt.push_back(2);lt.push_back(5);lt.push_back(9);for (auto e: lt) {cout << e << " ";}cout << endl; //10 4 7 18 2 5 9lt.remove_if(single_digit);//删除容器当中值小于10的元素for (auto e: lt) {cout << e << " ";}cout << endl;//10 18return 0;
}
3.list迭代器失效问题
此处可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
void TestListIterator1() {int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()) {l.erase(it);++it;}
}
erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
改正:
void TestListIterator() {int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()) {l.erase(it++);// 或者it = l.erase(it);}
}
4.list模拟实现

要模拟实现list,必须要熟悉list的底层结构以及其接口的含义,通过上面的学习,这些内容已基本掌握,现 我们来模拟实现list。
代码如下:
#pragma once
#include "iterator.h"
#include <algorithm>
#include <assert.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
// list - 底层是一个双向带头循环链表
template<class T>
struct list_node {list_node *next;list_node *prev;T data;// 模板T类型,适用任何类型// 构造函数初始化列表// T()被用来初始化list_node类的data成员变量,以确保每个新创建的list_node对象都有一个合适的T类型的默认值。list_node(const T &x = T())// T()用来给自定义类型调用默认构造函数来初始化x: next(nullptr), prev(nullptr), data(x) {}
};// list类
template<class T>
class list {
public:typedef list_node<T> node; // 链表typedef list_iterator<T, T &, T *> iterator;// 迭代器// const迭代器 通过const T& 传给Ref ,const T* 传给Ptrtypedef list_iterator<T, const T &, const T *> const_iterator;// const迭代器 - 通过const迭代器访问的数据无法被修改typedef STL_reverse_iterator<iterator, T &, T *> reverse_iterator;// 节点初始化void empty_init() {head = new node;head->next = head;head->prev = head;}// list默认构造函数list() {empty_init();}// 利用迭代器构造函数template<class iterator>list(iterator first, iterator last) {empty_init();while (first != last) {push_back(*first);++first;}}//拷贝构造 lt2(lt1) 老方法/*list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e); //将lt的元素复制到现在的list中}}*/void swap(list<T> &tmp) {std::swap(head, tmp.head);//交换头指针}// 拷贝构造-现代方法list(const list<T> <) {empty_init(); // 必须有,不然)_head就是空指针list<T> tmp(lt.begin(), lt.end());//由lt的迭代器,构造出一个tmpswap(tmp); //交换tmp和this->head的指针}// 赋值 lt1 = lt3 这里lt就是lt3的拷贝,lt1是thislist<T> &operator=(list<T> lt) {swap(lt); // 交换 lt和this交换return *this;// 返回自己就是返回lt,赋值给别的对象}// 迭代器通常建议将迭代器作为值传递,而不是作为引用传递。引用会导致迭代器失效iterator begin() {return iterator(head->next);// 调用默认构造函数给node初始化}const_iterator begin() const// const修饰的函数,无法改变成员变量{return const_iterator(head->next);// 指针不能改变,但可以赋值给别人}reverse_iterator rbegin() {return reverse_iterator(head->prev);//rbegin 是最后一个数}reverse_iterator rend() {return reverse_iterator(head);//rend是头指针}iterator end() {// 双向带头循环判尾是头节点headreturn iterator(head);}const_iterator end() const {return const_iterator(head);}// pos迭代器不会失效,插入后,pos位置永远不会变,地址不变void insert(iterator pos, const T &x) {// pos是一个类node *cur = pos._node; // 先取pos位置的节点地址node *prevnode = cur->prev;// 记录pos位置的前节点node *newnode = new node(x);prevnode->next = newnode;newnode->prev = prevnode;newnode->next = cur;cur->prev = newnode;}iterator erase(iterator pos) {if (pos != end()) {// 先记录前节点 后节点node *prevnode = pos._node->prev;node *nextnode = pos._node->next;prevnode->next = nextnode;nextnode->prev = prevnode;delete pos._node;// 返回下一个地址return iterator(nextnode);} else {perror("erase fail");exit(-1);}}void push_back(const T &x) {insert(end(), x);// 复用}void pop_back() {erase(end()--);// end()是头指针,头指针的prev是尾节点}void push_front(const T &x) {insert(begin(), x);}void pop_front() {erase(begin());}void clear() {// 清理内存 - 不清理头节点iterator it = begin();while (it != end()) {erase(it);it++;}}~list() {clear();delete head;head = nullptr;}private:node *head;// 头节点 - list只有一个数据成员,头节点
};
list的反向迭代器
通过前面例子知道,反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行 包装即可。
iterator.h:
#pragma once
template<class T>
struct list_node;//声明外部类,
// list迭代器
template<class T, class Ref, class Ptr>
struct list_iterator {typedef list_node<T> node; // 链表typedef list_iterator<T, Ref, Ptr> iterator;// 迭代器node *_node; // 迭代器里唯一的成员变量:链表指针// 迭代器默认构造函数,传的是迭代器链表指针list_iterator(node *n): _node(n) {}// 解引用 - 返回的是链表的值 Ref通过传参,T和const T 用来控制const类型和非const类型Ref operator*() {return _node->data;}//-> 返回的是链表data的地址 Ptr通过传参,T和const T 用来控制const类型和非const类型Ptr operator->() {return &_node->data;}// 前置++ 先++,在返回自己iterator &operator++() {_node = _node->next;return *this;}// 后置++ 先返回 在++iterator operator++(int) {iterator tmp = *this;// 注意:临时变量,不能引用返回_node = _node->next;return tmp;// tmp是一个类,不是引用返回,返回的时候会创建一个临时类}// 前置-- 先--,在返回自己iterator &operator--() {_node = _node->prev;return *this;}// 后-- 在返回,在--iterator operator--(int) {iterator tmp = *this;_node = _node->prev;return tmp;}// pos地址++iterator &operator+(int x) {while (x--) {//*this表示迭代器里的指针,++复用前面的重载,表示指针++*this = ++*this;}return *this;}iterator &operator-(int x) {while (x--) {*this = --*this;}return *this;}// this->_node 不等于参数_nodebool operator!=(const iterator &it) {return _node != it._node;}
};//反向迭代器
template<class iterator, class Ref, class Ptr>
struct STL_reverse_iterator {iterator cur;//正向迭代器typedef STL_reverse_iterator<iterator, Ref, Ptr> reverse_iterator;STL_reverse_iterator(iterator it): cur(it) {}Ref operator*() {return *cur;//对正向迭代器解引用,就是返回node->data}reverse_iterator operator++() {--cur;return *this;}reverse_iterator operator--() {++cur;return *this;}bool operator!=(const reverse_iterator &s) {return cur != s.cur;}
};
5.list和vector对比
vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不 同,其主要不同如下:
| vector | list | |
|---|---|---|
| 底层结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 随机访问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
| 插入和删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为 O(1) |
| 空间利 用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 | 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低, 缓存利用率低 |
| 迭代器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
| 迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代器失效,其他迭代器不受影响 |
| 使用场景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |
相关文章:
<C++> STL_list
1.list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。list与…...
聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化
聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化 目录 聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 FCM模糊C均值聚类,聚类结果可视化,MATLAB程序。 FCM(Fuzzy C-Means&a…...
C++笔记之设计模式:setter函数、依赖注入
C笔记之设计模式:setter函数、依赖注入 参考笔记: 1.C笔记之静态成员函数可以在类外部访问私有构造函数吗? 2.C笔记之设计模式:setter函数、依赖注入 3.C笔记之两个类的实例之间传递参数——通过构造函数传递类对象的方法详细探究…...
Spring MVC详解
文章目录 一、SpringMVC1.1 引言1.2 MVC架构1.2.1 概念1.2.2 好处 二、开发流程2.1 导入依赖2.2 配置核心(前端)控制器2.3 后端控制器2.4 配置文件2.5 访问 三、接收请求参数3.1 基本类型参数3.2 实体收参【重点】3.3 数组收参3.4 集合收参 【了解】3.5 路径参数3.6 中文乱码 四…...
谷歌公开.zip域名,应采取哪些措施应对可能的安全风险?
近期,谷歌发布了几个新的顶级域名,这些新域名包括.dad、.esq、.prof、.phd、.nexus、.foo、.mov以及本文我们将要提到的.zip域名。自发布以来,多个安全社区都开始讨论这些顶级域名所带来的影响,主要原因是.zip很容易被误认为是文件…...
css3滤镜属性filter让网页变黑白
今天是特殊的日子,抗击疫情全国哀悼日,向英雄们致敬,一路走好!应该发现了今天很多网站页面都是黑白色的,我的博客今天都是黑白色,用css3滤镜属性filter让网页马上变黑白,一行代码就搞定。 在你…...
C++教程 - How to C++系列专栏第5篇
关于专栏 这个专栏是优质的C教程专栏,如果你还没看过第0篇,点击这里去第0篇 本专栏一致使用操作系统:macOS Ventura,代码编辑器:CLion,C编译器:Clang 感谢一路相伴的朋友们,感谢你…...
Vue2向Vue3过度核心技术插槽
目录 1 插槽-默认插槽1.作用2.需求3.问题4.插槽的基本语法5.代码示例6.总结 2 插槽-后备内容(默认值)1.问题2.插槽的后备内容3.语法4.效果5.代码示例 3 插槽-具名插槽1.需求2.具名插槽语法3.v-slot的简写4.总结 4 作用域插槽1.插槽分类2.作用3.场景4.使用…...
vite配置electron、ElementPlus或者AntDesignVue
这是全部的配置原文: import { defineConfig } from vite; import vue from vitejs/plugin-vue; import electron from "vite-plugin-electron"; import electronRenderer from "vite-plugin-electron-renderer"; import polyfillExports from…...
时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化
时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化 目录 时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SVD分解重构算法,MATLAB程序,奇异值分解 (Singular Value Decompo…...
强化学习系列--时序差分学习方法(SARSA算法)
强化学习系列--时序差分学习方法(SARSA算法) 介绍示例代码 介绍 SARSA(State-Action-Reward-State-Action)是一种强化学习算法,用于解决马尔可夫决策过程(MDP)中的问题。SARSA算法属于基于值的…...
深度学习9:简单理解生成对抗网络原理
目录 生成算法 生成对抗网络(GAN) “生成”部分 “对抗性”部分 GAN如何运作? 培训GAN的技巧? GAN代码示例 如何改善GAN? 结论 生成算法 您可以将生成算法分组到三个桶中的一个: 鉴于标签&#…...
adb shell setprop 、开发者选项
App性能调试详解 Android App性能监控工具 更多系统属性参考 一、开启 GPU Render 的profiling bar: Gpu渲染速度 adb shell setprop debug.hwui.profile true adb shell setprop debug.hwui.profile visual_bars adb shell setprop debug.hwui.profile visual…...
性能测试面试问题,一周拿3个offer不嫌多
性能测试的三个核心原理是什么? 1.基于协议。性能测试的对象是网络分布式架构的软件,而网络分布式架构的核心是网络协议 2.多线程。人的大脑是单线程的,电脑的cpu是多线程的。性能测试就是利用多线程的技术模拟多用户去负载 3.模拟真实场景。…...
Android Bitmap压缩
Android View截屏长图拼接(RecyclerView) 我们在实际使用中,往往图片转化成Bitmap,对Bitmap操作的时候(如:截屏分享等),可能Bitmap会过大,导致无视实现对应功能。那么我们就需要对B…...
不同子网络中的通信过程
从输入www.baidu.com经历了什么 一、DNS(网址->IP) 二、ARP(IP->MAC) A->B:有数据发送,数据封装ip之后发现没有主机B的mac地址。然后ARP在本网段广播:检查目标地址和源地址是否在同一…...
Ubuntu Touch OTA-2 推出,支持 Fairphone 3 和 F(x)tec Pro1 X
导读UBports 基金会近日宣布为基于 Ubuntu 20.04 LTS (Focal Fossa) 的 Ubuntu Touch 移动操作系统发布并全面提供 OTA-2 软件更新。 Ubuntu Touch OTA-2 在首次 OTA 更新整整四个月后发布,支持新设备,包括 Fairphone 3、F(x)tec Pro1 X 和 Vollaphone X…...
【网络】数据链路层——MAC帧协议 | ARP协议
🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言:你只管努力,剩下的交给时间! 来到数据链路层后,完整的数据被叫做数据帧,习惯上称之为MAC帧。 MAC帧协议 | A…...
【Spring Boot】Spring Boot自动加载机制:简化应用程序的启动
在微服务盛行的今天,快速搭建和启动应用程序变得至关重要。Spring Boot作为Java生态系统中主流的框架,其自动加载机制使得开发者能够快速构建和启动应用程序。本文将详细介绍Spring Boot的自动加载机制,并通过代码示例加以说明。 首先&#…...
centos7搭建apache作为文件站后,其他人无法访问解决办法
在公司内网的一个虚拟机上搭建了httpsd服务,准备作为内部小伙伴们的文件站,但是搭建好之后发现别的小伙伴是无法访问我机器的。 于是寻找一下原因,排查步骤如下: 1.netstat -lnp 和 ps aux 先看下端口和 服务情况 发现均正常 2.…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
