当前位置: 首页 > news >正文

[C++] 容器适配器:深入理解Stack与Queue的底层原理

Kevin的技术博客.png

文章目录

  • 容器适配器简介
    • `deque`的缺陷
    • 为什么使用`deque`作为`stack`和`queue`的底层默认容器
  • `stack`和`queue`的简单讲解
    • Stack(栈)
      • 栈的操作图示
      • 栈的相关接口
    • Queue(队列)
  • `Stack`和`Queue`的模拟实现
    • Stack(栈)
      • 作为容器适配器的特性
      • 模拟实现
    • Queue(队列)
      • 作为容器适配器的特性
      • 模拟实现
  • `PriorityQueue`(优先级队列)
    • 优先级队列的特性
    • `priority_queue`的使用
      • 常用接口
      • 传入自定义类型的注意事项
      • 使用自定义类型传入应用示例
    • `priority_queue`的模拟实现
    • `deque`的实际应用
  • 仿函数(Functor)
    • 什么是仿函数?
    • 仿函数的定义
    • 仿函数的特性
      • 状态保存
      • 参数化
      • 灵活性
    • 仿函数的使用场景


容器适配器简介

适配器(Adapter)是一种设计模式,其主要作用是将一个类的接口转换为另一个客户希望的接口。在STL(Standard Template Library)中,适配器用来封装底层容器,提供特定的接口和行为。这种封装可以使得不同的底层容器在接口上保持一致,从而简化代码的使用和维护。

本文所涉及的stackqueuepriority_queue都是容器适配器,在底层都可以通过在接口传入的容器类型来进行底层的容器实现。
image.png
image.png
image.png

以上官方接口图示中,Container就是适配器初始化时容器类型的指定,Compare是仿函数,也可以实现相关的适配。

deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

为什么使用deque作为stackqueue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以;queue是先进先出的特殊线性数据结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. stack中元素增长时,dequevector的效率高(扩容时不需要搬移大量数据);queue中的
    元素增长时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷。

关于deque的详细讲解:
[C++] vector对比list & deque的引出-CSDN博客

stackqueue的简单讲解

Stack(栈)

栈的操作图示

栈的相关接口

栈是一种后进先出(LIFO, Last In First Out)的数据结构,通常用于存储临时数据或实现递归。其基本操作包括:

接口函数说明
push(x)将元素x压入栈顶
pop()移除并返回栈顶元素
top()返回栈顶元素
empty()判断栈是否为空
size()返回栈中元素个数

Queue(队列)

Queue-Data-Structures.png
队列是一种先进先出(FIFO, First In First Out)的数据结构,适用于需要顺序处理数据的场景。其基本操作包括:

接口函数说明
push(x)将元素x加入队尾
pop()移除并返回队头元素
front()返回队头元素
back()返回队尾元素
empty()判断队列是否为空
size()返回队列中元素个数

StackQueue的模拟实现

Stack(栈)

作为容器适配器的特性

  1. 后进先出(LIFO):栈是一种遵循 LIFO 原则的数据结构,这意味着最后被添加到栈中的元素将是第一个被移除的元素。
  2. 受限的接口:与完整的容器不同,栈的接口限制了用户只能通过栈顶进行操作,不允许直接访问栈中的其他元素。
  3. 主要操作
    • push:向栈顶添加一个元素。
    • pop:移除栈顶的元素。
    • top:访问栈顶的元素(不移除它)。
  4. 空栈检查:可以检查栈是否为空,以便在尝试访问或移除元素之前确保栈不为空。
  5. 大小限制:可以查询栈中元素的数量,但不允许直接通过索引访问元素。
  6. 迭代器:虽然栈的迭代器功能有限,但栈仍然提供了迭代器,允许遍历栈中的元素,尽管只能从栈顶开始。
  7. 异常中立性:栈的操作(如 pushpop)保证不抛出异常,除非是底层容器的操作抛出异常。
  8. 底层容器:栈通常使用 dequevector 作为底层容器来存储元素。选择哪种容器取决于具体的实现和性能要求。
  9. 模板类:栈是一个模板类,可以存储任意类型的元素。
  10. 不提供排序:栈不提供元素排序功能,它只提供了基本的 LIFO 操作。
  11. 不提供元素删除:除了 pop 操作外,栈不提供从栈中删除任意位置元素的功能。
  12. 不提供直接访问:不能直接访问或修改栈中的元素,除了栈顶元素。

模拟实现

template<class T, class Container = std::deque<T>>
class stack {
public:// 向栈顶添加一个元素void push(const T& x) {_con.push_back(x); // 使用底层容器的 push_back 方法}// 移除栈顶元素void pop() {if (empty()) {throw std::out_of_range("Stack<>::pop: empty stack");}_con.pop_back(); // 使用底层容器的 pop_back 方法}// 获取栈顶元素的引用const T& top() const {if (empty()) {throw std::out_of_range("Stack<>::top: empty stack");}return _con.back(); // 使用底层容器的 back 方法}// 获取栈中元素的数量size_t size() const {return _con.size();}// 检查栈是否为空bool empty() const {return _con.empty();}private:Container _con; // 底层容器
};

Queue(队列)

作为容器适配器的特性

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
    1. empty:检测队列是否为空
    2. size:返回队列中有效元素的个数
    3. front:返回队头元素的引用
    4. back:返回队尾元素的引用
    5. push_back:在队列尾部入队列
    6. pop_front:在队列头部出队列
  4. 标准容器类dequelist满足了这些要求。默认情况下,如果没有为queue实例化指定容器
    类,则使用标准容器deque

模拟实现

template<class T, class Container = std::deque<T>>
class queue {
public:// 向队列尾部添加一个元素void push(const T& x) {_con.push_back(x); // 使用底层容器的 push_back 方法}// 移除队列头部的元素void pop() {if (empty()) {throw std::out_of_range("Queue<>::pop: empty queue");}_con.pop_front(); // 使用底层容器的 pop_front 方法}// 获取队列头部元素的引用const T& front() const {if (empty()) {throw std::out_of_range("Queue<>::front: empty queue");}return _con.front(); // 使用底层容器的 front 方法}// 获取队列尾部元素的引用const T& back() const {if (empty()) {throw std::out_of_range("Queue<>::back: empty queue");}return _con.back(); // 使用底层容器的 back 方法}// 获取队列中元素的数量size_t size() const {return _con.size();}// 检查队列是否为空bool empty() const {return _con.empty();}private:Container _con; // 底层容器,默认为 deque
};

PriorityQueue(优先级队列)

优先级队列是一种特殊的队列,元素按照优先级排列。其基本操作类似于堆,主要用于调度算法、路径搜索等需要频繁获取最高优先级元素的场景。

优先级队列的特性

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    1. empty():检测容器是否为空
    2. size():返回容器中有效元素个数
    3. front():返回容器中第一个元素的引用
    4. push_back():在容器尾部插入元素
    5. pop_back():删除容器尾部元素
  5. 标准容器类vectordeque满足这些需求。默认情况下,如果没有为特定的priority_queue
    类实例化指定容器类,则使用vector
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用
    算法函数make_heappush_heappop_heap来自动完成此操作。

priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使priority_queue

注意:默认情况下priority_queue是大堆。

常用接口

接口函数说明
push(x)插入元素x
pop()移除并返回最大(或最小)元素
top()返回最大(或最小)元素但不移除
empty()判断队列是否为空
size()返回队列中元素个数
emplace(x)就地构造元素x并插入队列
swap(q)交换当前优先级队列与q中的元素
std::less<T>默认仿函数,构建最大堆
std::greater<T>自定义仿函数,构建最小堆(需自定义仿函数参数)

传入自定义类型的注意事项

当你使用 std::priority_queue 时,它默认使用 < 运算符来确定元素之间的优先级关系,即默认情况下,较小的元素会被认为是具有较高优先级的。然而,std::priority_queue 也允许用户指定一个自定义的比较函数,这使得你可以定义自己的优先级规则。

所以:如果在priority_queue中放自定义类型的数据,需要在自定义类型中提供>或者< 的重载。

如果你要将自定义类型的对象放入 std::priority_queue 中,并且希望使用不同于默认的优先级规则(例如,你可能希望较大的元素具有较高的优先级),你需要提供一个自定义的比较函数。这个比较函数可以是:

  • 一个函数对象(functor)。
  • 一个普通的函数。
  • 一个 lambda 表达式。

这就是仿函数的基本用法。当使用自定义类型时,传入std::greater<T>std::less<T>会自动调用自定义类型重载的<>来构建优先级队列。

使用自定义类型传入应用示例

class Date {
public:// 构造函数,初始化日期Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day) {}// 重载小于运算符,用于比较两个日期bool operator<(const Date& d) const {return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}// 重载大于运算符,用于比较两个日期bool operator>(const Date& d) const {return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}// 友元函数,重载输出流运算符,用于输出日期friend std::ostream& operator<<(std::ostream& os, const Date& d) {os << d._year << "-" << d._month << "-" << d._day;return os;}private:int _year;int _month;int _day;
};void TestPriorityQueue() {// 使用默认的 priority_queue 创建最大堆std::priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));std::cout << "Max heap top: " << q1.top() << std::endl;// 使用自定义比较对象 greater<Date> 创建最小堆std::priority_queue<Date, std::vector<Date>, std::greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));std::cout << "Min heap top: " << q2.top() << std::endl;
}int main() {TestPriorityQueue();return 0;
}

TestPriorityQueue 函数展示了如何使用 std::priority_queue 来创建最大堆和最小堆。最大堆 q1 使用 Date 类的 < 运算符来确定元素的优先级,而最小堆 q2 使用 std::greater<Date> 来实现,它将 Date 类型的 > 运算符作为比较函数。函数最后输出了两个堆的顶部元素。

priority_queue的模拟实现

template<class T>
class Less {
public:bool operator()(const T& x, const T& y) const { return x < y;}
};template<class T>
class Greater {
public:bool operator()(const T& x, const T& y) const { return x > y;}
};namespace bee {template<class T, class Container = std::vector<T>, class Compare = Less<T>>class priority_queue {public:void adjustUp(int child) {while (child > 0) {int parent = (child - 1) / 2;if (_compare(_con[child], _con[parent])) {std::swap(_con[child], _con[parent]);child = parent;} else {break;}}}void push(const T& x) {_con.push_back(x);adjustUp(static_cast<int>(_con.size()) - 1);}void adjustDown(int parent) {int child = parent * 2 + 1;while (child < _con.size()) {int right_child = child + 1;if (right_child < _con.size() && _compare(_con[child], _con[right_child])) {child = right_child;}if (_compare(_con[child], _con[parent])) {std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;} else {break;}}}void pop() {std::swap(_con[0], _con.back());_con.pop_back();adjustDown(0);}T top() const { // 添加 const 并移除 return 关键字return _con[0];}size_t size() const {return _con.size();}bool empty() const {return _con.empty();}private:Container _con;Compare _compare; // 存储比较函数对象};
}

在底层使用堆进行维护,符合了deque的逻辑。

deque的实际应用

struct Task {int priority;std::string name;// 重载运算符,用于比较任务的优先级(priority),越小优先级越高bool operator<(const Task& other) const {return priority > other.priority; // priority值越小优先级越高}
};int main() {std::priority_queue<Task> taskQueue;// 添加任务到队列taskQueue.push(Task{3, "Task A"});taskQueue.push(Task{1, "Task B"});taskQueue.push(Task{2, "Task C"});// 处理任务while (!taskQueue.empty()) {Task currentTask = taskQueue.top();std::cout << "Processing " << currentTask.name << " with priority " << currentTask.priority << std::endl;taskQueue.pop();}

在这个例子中,我们定义了一个Task结构体,每个任务有一个优先级和名称。我们使用std::priority_queue来管理这些任务,并通过重载operator<来定义任务的优先级比较规则。优先级最高的任务(priority值最小)会首先被处理。

仿函数(Functor)

什么是仿函数?

仿函数(Functor)是指实现了operator()的对象。在C++中,仿函数是一种能够像普通函数一样被调用的对象。它们通过重载函数调用运算符operator()来实现这一点,因此可以像函数一样使用。
通过重载operator(),仿函数可以模拟函数的行为,使得对象不仅可以保存状态,还可以执行操作。这种机制在C++中非常有用,特别是在STL(标准模板库)中,它允许用户自定义排序准则、筛选条件等。

仿函数的定义

仿函数是一个类或者结构体,通过重载operator()来实现。基本形式如下:

class Functor {
public:void operator()(/* 参数列表 */) {// 函数体}
};

仿函数的特性

状态保存

仿函数可以有成员变量,这允许它们在调用时保存状态。这是与普通函数的一个重要区别,因为普通函数没有状态。仿函数可以应用在需要保留上下文信息的场景。例如,计数器仿函数可以记录被调用的次数:

class Counter {
public:Counter() : count(0) {}void operator()() {++count;std::cout << "Called " << count << " times" << std::endl;}private:int count;
};int main() {Counter countCalls;countCalls(); // 输出 "Called 1 times"countCalls(); // 输出 "Called 2 times"return 0;
}

在这个例子中,Counter仿函数保存了调用次数,并在每次调用时输出当前的调用次数。

参数化

仿函数可以通过构造函数参数传递数据,使得调用operator()时可以使用这些数据进行操作,也就是在上文适配器中关于仿函数的使用方式。

灵活性

仿函数可以重载operator()来实现不同的功能,比如比较、操作等,提供了很大的灵活性。结合灵活性与参数化,可以灵活的控制相关容器的底层存储。

template<class T, class Container = std::vector<T>, class Compare = Less<T>>

通过传递仿函数,用户可以自定义优先级队列的元素排列规则

例如在上文实现优先级队列的模拟实现代码中,就使用的仿函数作为模板参数:

priority_queue中,仿函数Compare决定了元素的优先级顺序。默认情况下,Less<T>会将较小的元素放在堆顶,形成最小堆。如果使用Greater<T>,则会形成最大堆。仿函数的灵活性允许用户根据需要自定义优先级队列的行为。仿函数的使用使得priority_queue能够支持多种排列规则,而不需要修改底层容器的实现。

仿函数的使用场景

  • 排序:在STL算法(如std::sort)中,可以使用仿函数自定义排序准则。
  • 筛选:在STL算法(如std::remove_if)中,可以使用仿函数定义筛选条件。
  • 优先级队列:在std::priority_queue中,仿函数用于定义元素的优先级排序。
  • 延迟计算:通过在仿函数中保存状态,用户可以实现延迟计算的逻辑。

具体的应用请通过上文优先级队列理解。


image.png

相关文章:

[C++] 容器适配器:深入理解Stack与Queue的底层原理

文章目录 容器适配器简介deque的缺陷为什么使用deque作为stack和queue的底层默认容器 stack和queue的简单讲解Stack&#xff08;栈&#xff09;栈的操作图示栈的相关接口 Queue&#xff08;队列&#xff09; Stack和Queue的模拟实现Stack&#xff08;栈&#xff09;作为容器适配…...

Eclipse maven 的坑

在使用 eclipse 时&#xff0c; eclipse 的右下角 一直在提示 “JPA java change event handler” &#xff0c;eclipse使用起来很卡&#xff0c;解决办法 问题描述&#xff1a; 在使用 eclipse时&#xff0c; eclipse 的右下角 一直在提示 “JPA java change event handler”…...

多模态视觉大语言模型——LLaVA

论文题目:Visual Instruction Tuning 论文地址:https://arxiv.org/abs/2304.08485 github: https://github.com/haotian-liu/LLaVA 1. Abstract 本文首次尝试使用GPT-4生成多模态指令数据,并基于这些数据训练了LLaVA(Large Language and Vision Assistant)模型,这是一种结…...

服务注册到nacos上,不能点击下线的问题处理

nacos不能下线&#xff1a; 修改 /usr/local/mid/nacos/data 文件夹下 protocol 文件重命名为 protocol_bak&#xff0c;然后再重启nacos nacos单机启动命令&#xff1a;cd sh startup.sh -m standalone nginx启动命令&#xff1a;cd /usr/local/mid/nginx/sbin ./…...

未来3-5年,哪些工作会被AI取代

一篇由高盛经济学家约瑟夫布里格斯 &#xff08;Joseph Briggs&#xff09;和德维西科德纳尼 &#xff08;Devesh Kodnani&#xff09;撰写的报告指出&#xff0c;全球预计将有3亿个工作岗位被生成式AI取代。 报告称&#xff1a;“最近出现的生成式人工智能将降低劳动力成本和…...

鸿蒙系统开发【网络管理】

网络管理 介绍 此Demo展示如何查询网络详情、域名解析、网络状态监听等功能。 效果预览&#xff1a; 使用说明&#xff1a; 1.启动应用&#xff0c;在点击检查网络、网络详情、网络连接信息后&#xff0c;展示对应的信息&#xff1b; 2.在域名解析的模块下&#xff0c;输入…...

nginx如何处理请求

nginx如何处理请求 注&#xff1a;内容翻译自Nginx官网文档 How nginx processes a request。 基于名称的虚拟服务器 nginx首先要决定哪个服务器应该处理请求。让我们从一个简单的配置开始&#xff0c;三个虚拟服务器都监听在端口*:80: server {listen 80;server_name e…...

换地不换IP?揭秘微博IP地址的奥秘

在这个信息飞速传递的时代&#xff0c;社交媒体成为我们生活中不可或缺的一部分。微博&#xff0c;作为其中的佼佼者&#xff0c;不仅是我们获取资讯的重要渠道&#xff0c;也是展现自我、分享生活的重要平台。然而&#xff0c;你有没有遇到过这样的情况&#xff1a;明明已经换…...

数据库事务处理技术——故障恢复

1. 数据故障恢复的宏观思路 我们知道DBMS是利用内存&#xff08;主存&#xff09;和外存&#xff08;辅存&#xff09;这样的存储体系进行数据库的管理&#xff0c;其中内存也就是我们常说的缓存是易失的。而事务时DBMS对数据库进行控制的基本单元&#xff0c;宏观上是由程序设…...

Java零基础之多线程篇:性能考虑篇

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…...

CSP 初赛复习 :计算机网络基础

计算机网络的基础和网络的拓扑结构是计算机网络设计和实施的关键要素。‌ 计算机网络的基础涉及多个方面&#xff0c;‌包括网络层协议&#xff08;‌如IP、‌ICMP、‌IGMP等&#xff09;‌、‌传输层协议&#xff08;‌TCP、‌UDP等&#xff09;‌以及应用层协议&#xff08;‌…...

【Docker应用】快速搭建Plik服务结合内网穿透无公网IP远程访问传输文件

文章目录 前言1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 前言 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设…...

记录使用FlinkSql进行实时工作流开发

使用FlinkSql进行实时工作流开发 引言Flink SQL实战常用的Connector1. MySQL-CDC 连接器配置2. Kafka 连接器配置3. JDBC 连接器配置4. RabbitMQ 连接器配置5. REST Lookup 连接器配置6. HDFS 连接器配置 FlinkSql数据类型1. 基本数据类型2. 字符串数据类型3. 日期和时间数据类…...

韶音开放式耳机怎么样?韶音、西圣、QCY热门款实测横评

开放式耳机是目前最火爆的的耳机市场细分赛道&#xff0c;开放式耳机的优点包括健康卫生&#xff0c;佩戴舒适性高&#xff0c;方便我们接收外部环境音等等&#xff0c;以上这些优势使得开放式耳机特别适配户外运动场景&#xff0c;在工作、日常生活等场景下使用也是绰绰有余。…...

求值(河南萌新2024)

我真的服了&#xff0c;注意数据范围&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#…...

【Linux】文件描述符 fd

目录 一、C语言文件操作 1.1 fopen和fclose 1.2 fwrite和fread 1.3 C语言中的输入输出流 二、Linux的文件系统调用 2.1 open和文件描述符 2.2 close 2.3 read 2.4 write 三、Linux内核数据结构与文件描述符 一、C语言文件操作 在C语言中我们想要打开一个文件并对其进…...

带通采样定理

一、采样定理 1.1 低通采样定理(奈奎斯特采样) 低通采样定理&#xff08;奈奎斯特采样&#xff09;是要求大于信号的最高上限频率的两倍 1.2 带通采样定理 带通信号的采样频率在某个时间小于采样频率也能无失真恢复原信号 二、频谱混叠 对一个连续时域信号&#xff0c;采…...

运维工作中的事件、故障排查处理思路

一、运维工作中的事件 https://www.51cto.com/article/687753.html 二、运维故障排查 一&#xff09;故障排查步骤 1、明确故障 故障现象的直接表现故障发生的时间、频率故障发生影响哪些系统故障发生是否有明确的触发条件   故障举例&#xff1a;无法通过ssh登录系统 影响…...

深入源码P3C-PMD:使用流程(1)

PMD开源组件启动流程介绍 在软件开发领域&#xff0c;代码质量是项目成功的关键因素之一。为了提升代码质量&#xff0c;开发者们常常借助各种工具进行代码分析和检查。PMD作为一款开源的静态代码分析工具&#xff0c;在Java、JavaScript、PLSQL等语言项目中得到了广泛应用。本…...

java~反射

反射 使用的前提条件&#xff1a;必须先得到代表的字节码的Class&#xff0c;Class类用于表示.class文件&#xff08;字节码&#xff09; 原理图 加载完类后&#xff0c;在堆中就产生了一个Class类型的对象&#xff08;一个类只有一个Class对象&#xff09;&#xff0c;这个对…...

【Linux】(26) 详解磁盘与文件系统:从物理结构到inode机制

目录 1.认识磁盘、 1.1 理论 1.2 磁盘的物理结构 CHS 寻址 1.3 磁盘的逻辑抽象结构 2. inode 结构 1.Boot Block 启动块 2.Super Block&#xff08;超级块&#xff09; 3.Group Descriptor Block&#xff08;块组描述符&#xff09; 4.Data Blocks (数据块) 5.Inode…...

8.1 字符串中等 43 Multiply Strings 38 Count and Say

43 Multiply Strings【默写】 那个难点我就没想先解决&#xff0c;原本想法是先想其他思路&#xff0c;但也没想出。本来只想chat一下使用longlong数据类型直接stoi()得不得行&#xff0c;然后就看到了答案&#xff0c;直接一个默写的大动作。但这道题确实考察的是还原乘法&…...

upload-labs靶场:1—10通关教程

目录 Pass-01&#xff08;JS 验证&#xff09; Pass-02&#xff08;MIME&#xff09; Pass-03&#xff08;黑名单绕过&#xff09; Pass-04&#xff08;.htaccess 绕过&#xff09; Pass-05&#xff08;大小写绕过&#xff09; Pass-06&#xff08;空格绕过&#xff09; …...

Hive3:一键启动、停止、查看Hive的metastore和hiveserver2两个服务的脚本(好用)

脚本内容 #!/bin/bash # 一键启动、停止、查看Hive的metastore和hiveserver2两个服务的脚本 function start_metastore {# 启动Hive metastore服务hive --service metastore >/dev/null 2>&1 &for i in {1..30}; doif is_metastore_running; thenecho "Hiv…...

遗传算法与深度学习实战——生命模拟及其应用

遗传算法与深度学习实战——生命模拟及其应用 0. 前言1. 康威生命游戏1.1 康威生命游戏的规则1.2 实现康威生命游戏1.3 空间生命和智能体模拟 2. 实现生命模拟3. 生命模拟应用小结系列链接 0. 前言 生命模拟是进化计算的一个特定子集&#xff0c;模拟了自然界中所观察到的自然…...

大数据|使用Apache Spark 删除指定表中的指定分区数据

文章目录 概述方法 1: 使用 Spark SQL 语句方法 2: 使用 DataFrame API方法 3: 使用 Hadoop 文件系统 API方法 4: 使用 Delta Lake使用注意事项常见相关问题及处理结论 概述 Apache Spark 是一个强大的分布式数据处理引擎&#xff0c;支持多种数据处理模式。在处理大型数据集时…...

OSPF动态路由协议实验

首先地址划分 一个骨干网段分成三个&#xff0c;r1&#xff0c;r2&#xff0c;r5三个环回网段 &#xff0c;总共要四个网段 192.168.1.0/24 192.168.1.0/26---骨干网段 192.168.1.0/28 192.168.1.16/28 192.168.1.32/28 备用 192.168.1.64/28 192.168.1.64/26---r1环回 192.1…...

tcp中accept()的理解

源码 参数理解 NAMEaccept, accept4 - accept a connection on a socketSYNOPSIS#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);#define _GNU_SOURCE …...

让我们逐行重现 GPT-2:第 1 部分

欢迎来到雲闪世界。Andrej Karpathy 是人工智能 (AI) 领域的顶尖研究人员之一。他是 OpenAI 的创始成员之一&#xff0c;曾领导特斯拉的 AI 部门&#xff0c;目前仍处于 AI 社区的前沿。 在第一部分中&#xff0c;我们重点介绍如何实现 GPT-2 的架构。虽然 GPT-2 于 2018 年由 …...

第十九天内容

上午 1、构建vue发行版本 2、java环境配置 jdk软件包路径&#xff1a; https://download.oracle.com/java/22/latest/jdk-22_linux-x64_bin.tar.gz 下午 1、安装tomcat软件 tomcat软件包路径&#xff1a; https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.26/bin/apache-to…...

燕郊做网站找谁/指数查询

视频效果 炫酷按钮思路&#xff1a; 大致实录就是将盒子里边的before倾斜变形&#xff0c;使其变成一个平行四边形的效果&#xff0c;然后将其缩小隐藏&#xff0c;在放大&#xff0c;添加过渡时间就会出现如图所示的效果 代码&#xff1a; <!DOCTYPE html> <html l…...

国内包装设计网站/百度浏览器打开

第一次, 写点啥呢? 分享一下订阅的资源吧! 1 <?xml version"1.0" encoding"UTF-8"?>2 3 <opml version"1.0">4 <head>5 <title>建勇 subscriptions in feedly Cloud</title>6 </head>7…...

卡盟网站怎么做图片素材/班级优化大师怎么加入班级

vantui是一个基于Vue.js的组件库&#xff0c;它提供了文件上传组件&#xff0c;可以方便地在Vue.js应用中实现文件上传功能。使用vantui文件上传组件&#xff0c;你可以在网页中选择文件并将其上传到服务器。它支持多文件上传、文件限制、文件类型限制等功能。使用vantui文件上…...

天长做网站的/2345网址导航删除办法

「2019 Python开发者日」全日程揭晓&#xff0c;请扫码咨询 ↑↑↑作者 | 伊凡伊德里斯&#xff08;Ivan Idris&#xff09;&#xff0c;曾是Java和数据库应用开发者&#xff0c;后专注于Python和数据分析领域&#xff0c;致力于编写干净、可测试的代码。他还是《Python Machin…...

南昌网站快速排名提升/网络平台建站

PasswordStrength是AtlasControlToolkit在最新版本里面提供的一个检测密码强度的Extender&#xff0c;它支持两种文本提示和进度条提示两种方式。主要内容1&#xff0e;PasswordStrength介绍2&#xff0e;完整示例一&#xff0e;PasswordStrength介绍PasswordStrength是AtlasCo…...

建设银行网站登录首页/给网站做seo的价格

客制化键盘的那些事儿——GH60篇&#xff0c;入坑的朋友们一定要注意的事是由B2B101网站(www.b2b101.com)为您收集修改整理而来&#xff0c;更多相关内容请关注B2B101网站其他品牌栏目。客制化键盘中的gh60&#xff0c;是国外一个geek论坛的一群大神们无聊搞出来的一个东西&…...