【C/C++】深入解析 Stack 与 Queue 数据结构(详解):实现原理、应用场景与性能优化

文章目录

    • 引言
    • 栈(Stack)数据结构详解
      • 1. 栈的基本概念
      • 2. 栈的实现原理
      • 3. C++中的栈实现
      • 4. 栈的应用场景
      • 5. 栈的性能分析
      • 6. 实战示例:括号匹配
    • 队列(Queue)数据结构详解
      • 1. 队列的基本概念
      • 2. 队列的实现原理
      • 3. C++中的队列实现
      • 4. 队列的应用场景
      • 5. 队列的性能分析
      • 6. 实战示例:任务调度
    • 栈与队列的比较与选择
    • 性能优化技巧
      • 1. 选择合适的底层容器
      • 2. 避免不必要的内存分配
      • 3. 使用移动语义
      • 4. 并行处理
      • 5. 编译器优化
    • 更多文章
    • 结论

引言

在软件开发中,数据结构是组织和存储数据的核心方式。合理选择和使用数据结构不仅能提高程序的效率,还能简化代码逻辑,增强可维护性。其中,**栈(Stack)队列(Queue)**作为最基础的线性数据结构,广泛应用于各种算法和系统设计中。然而,掌握它们的底层实现、应用场景及性能优化策略,是每位C++开发者必备的技能。

【体验最新的GPT系列模型!支持Open API调用、自定义助手、文件上传等强大功能,助您提升工作效率!点击链接体验:CodeMoss & ChatGPT-AI中文版】

在这里插入图片描述

栈(Stack)数据结构详解

1. 栈的基本概念

是一种**后进先出(LIFO, Last In First Out)**的数据结构。这意味着最后被压入栈中的元素将最先被弹出。栈常用于需要逆序处理数据的场景,如函数调用管理、表达式解析等。

基本操作

  • Push:向栈顶添加一个元素。
  • Pop:移除栈顶的元素。
  • Top/Peek:查看栈顶的元素而不移除它。
  • IsEmpty:检查栈是否为空。
    在这里插入图片描述

2. 栈的实现原理

栈的实现可以基于数组或链表。基于数组的实现通常具有较低的内存占用和更快的访问速度,但需要预先设置栈的大小。基于链表的实现则更为灵活,能够动态调整大小,但每个元素需要额外的指针存储空间。

基于数组的实现

class Stack {
private:int* arr;int top;int capacity;
public:Stack(int size = 100);~Stack();void push(int x);int pop();int peek();bool isEmpty();
};Stack::Stack(int size) {arr = new int[size];capacity = size;top = -1;
}Stack::~Stack() {delete[] arr;
}void Stack::push(int x) {if(top == capacity -1) {throw std::overflow_error("Stack Overflow");}arr[++top] = x;
}int Stack::pop() {if(isEmpty()) {throw std::underflow_error("Stack Underflow");}return arr[top--];
}int Stack::peek() {if(!isEmpty()) {return arr[top];}throw std::underflow_error("Stack is empty");
}bool Stack::isEmpty() {return top == -1;
}

基于链表的实现

struct Node {int data;Node* next;
};class Stack {
private:Node* head;
public:Stack() : head(nullptr) {}~Stack();void push(int x);int pop();int peek();bool isEmpty();
};Stack::~Stack() {while(!isEmpty()) {pop();}
}void Stack::push(int x) {Node* temp = new Node();temp->data = x;temp->next = head;head = temp;
}int Stack::pop() {if(isEmpty()) {throw std::underflow_error("Stack Underflow");}Node* temp = head;head = head->next;int popped = temp->data;delete temp;return popped;
}int Stack::peek() {if(!isEmpty()) {return head->data;}throw std::underflow_error("Stack is empty");
}bool Stack::isEmpty() {return head == nullptr;
}

3. C++中的栈实现

C++标准库提供了std::stack,这是一个模板类,基于其他容器(如vectordequelist)实现。默认情况下,std::stack使用std::deque作为底层容器,但也可以选择其他容器。

使用std::stack的示例

#include <iostream>
#include <stack>int main() {std::stack<int> s;// Push elementss.push(10);s.push(20);s.push(30);// Display and pop elementswhile(!s.empty()) {std::cout << ' ' << s.top();s.pop();}return 0;
}

输出:

 30 20 10

std::stack提供了以下关键成员函数:

  • push(const T& value):压入元素。
  • pop():弹出栈顶元素。
  • top():访问栈顶元素。
  • empty():检查栈是否为空。
  • size():返回栈的大小。

4. 栈的应用场景

栈在计算机科学中有着广泛的应用,主要包括:

  • 函数调用管理:程序调用函数时,会将函数的返回地址、参数等信息压入调用栈,函数执行完毕后,从栈中弹出这些信息。
  • 表达式解析:如中缀表达式转换为后缀表达式、括号匹配等。
  • 深度优先搜索(DFS):在图或树的遍历中,DFS通常使用栈来记录访问路径。
  • 撤销操作:在编辑器等应用中,实现撤销(Undo)操作时,会用到栈来记录历史操作。

5. 栈的性能分析

栈的主要操作(Push、Pop、Top)通常具有常数时间复杂度(O(1)),无论是基于数组还是链表的实现。这使得栈在需要频繁进行这些操作的场景下,表现出色。

基于数组的栈

  • 时间复杂度
    • Push: O(1) 平均,O(n) 在需要扩展数组时
    • Pop: O(1)
    • Top: O(1)
  • 空间复杂度:预先分配的空间固定,但可以通过动态数组实现动态扩展。

基于链表的栈

  • 时间复杂度
    • Push: O(1)
    • Pop: O(1)
    • Top: O(1)
  • 空间复杂度:每个元素需要额外的指针存储空间,但无需预先分配固定大小的空间。

6. 实战示例:括号匹配

括号匹配是栈的经典应用之一。通过栈,我们可以有效地检查表达式中的括号是否配对正确。

问题描述:给定一个包含括号的字符串,检查括号是否正确匹配。支持的括号类型包括(){}[]

解决思路

  1. 遍历字符串的每一个字符。
  2. 如果遇到左括号,将其压入栈中。
  3. 如果遇到右括号,检查栈顶是否有对应的左括号:
    • 如果有,则弹出栈顶。
    • 如果没有,则括号不匹配。
  4. 遍历结束后,检查栈是否为空:
    • 如果为空,括号匹配正确。
    • 如果不为空,括号不匹配。

代码实现

#include <iostream>
#include <stack>
#include <string>
#include <unordered_map>bool isValidParentheses(const std::string& s) {std::stack<char> stk;std::unordered_map<char, char> mapping = { {')', '('}, {'}', '{'}, {']', '['} };for(char c : s) {if(mapping.find(c) != mapping.end()) {if(!stk.empty() && stk.top() == mapping[c]) {stk.pop();} else {return false;}} else if(c == '(' || c == '{' || c == '[') {stk.push(c);}// 忽略其他字符}return stk.empty();
}int main() {std::string expr = "{[()()]}";if(isValidParentheses(expr)) {std::cout << "括号匹配正确" << std::endl;} else {std::cout << "括号匹配错误" << std::endl;}return 0;
}

输出

括号匹配正确

复杂度分析

  • 时间复杂度:O(n),其中n是字符串的长度。
  • 空间复杂度:O(n),在最坏情况下,栈中可能存储所有的左括号。

队列(Queue)数据结构详解

1. 队列的基本概念

队列是一种**先进先出(FIFO, First In First Out)**的数据结构。这意味着最先进入队列的元素将最先被移出队列。队列广泛应用于需要按顺序处理任务的场景,如任务调度、广度优先搜索等。

基本操作

  • Enqueue:向队尾添加一个元素。
  • Dequeue:移除队头的元素。
  • Front/Peek:查看队头的元素而不移除它。
  • IsEmpty:检查队列是否为空。

【体验最新的GPT系列模型!支持Open API调用、自定义助手、文件上传等强大功能,助您提升工作效率!点击链接体验:CodeMoss & ChatGPT-AI中文版】
在这里插入图片描述

2. 队列的实现原理

与栈类似,队列的实现也可以基于数组或链表。基于数组的队列通常采用循环数组来有效利用空间;基于链表的队列则通过前后指针实现高效的入队和出队操作。

基于数组的循环队列实现

class Queue {
private:int* arr;int front;int rear;int capacity;
public:Queue(int size = 100);~Queue();void enqueue(int x);int dequeue();int peek();bool isEmpty();
};Queue::Queue(int size) {arr = new int[size];capacity = size;front = 0;rear = -1;
}Queue::~Queue() {delete[] arr;
}void Queue::enqueue(int x) {if((rear + 1) % capacity == front) {throw std::overflow_error("Queue Overflow");}rear = (rear + 1) % capacity;arr[rear] = x;
}int Queue::dequeue() {if(isEmpty()) {throw std::underflow_error("Queue Underflow");}int item = arr[front];front = (front + 1) % capacity;return item;
}int Queue::peek() {if(!isEmpty()) {return arr[front];}throw std::underflow_error("Queue is empty");
}bool Queue::isEmpty() {return rear == -1;
}

基于链表的实现

struct Node {int data;Node* next;
};class Queue {
private:Node* front;Node* rear;
public:Queue() : front(nullptr), rear(nullptr) {}~Queue();void enqueue(int x);int dequeue();int peek();bool isEmpty();
};Queue::~Queue() {while(!isEmpty()) {dequeue();}
}void Queue::enqueue(int x) {Node* temp = new Node();temp->data = x;temp->next = nullptr;if(rear == nullptr) {front = rear = temp;return;}rear->next = temp;rear = temp;
}int Queue::dequeue() {if(isEmpty()) {throw std::underflow_error("Queue Underflow");}Node* temp = front;front = front->next;if(front == nullptr) {rear = nullptr;}int dequeued = temp->data;delete temp;return dequeued;
}int Queue::peek() {if(!isEmpty()) {return front->data;}throw std::underflow_error("Queue is empty");
}bool Queue::isEmpty() {return front == nullptr;
}

3. C++中的队列实现

C++标准库提供了std::queue,这是一个模板类,基于其他容器(如dequelist)实现。默认情况下,std::queue使用std::deque作为底层容器,但也可以选择其他容器。

使用std::queue的示例

#include <iostream>
#include <queue>int main() {std::queue<int> q;// Enqueue elementsq.push(10);q.push(20);q.push(30);// Display and dequeue elementswhile(!q.empty()) {std::cout << ' ' << q.front();q.pop();}return 0;
}

输出:

 10 20 30

std::queue提供了以下关键成员函数:

  • push(const T& value):入队元素。
  • pop():出队元素。
  • front():访问队头元素。
  • back():访问队尾元素。
  • empty():检查队列是否为空。
  • size():返回队列的大小。

4. 队列的应用场景

队列在计算机科学和工程中有着广泛的应用,主要包括:

  • 任务调度:操作系统中的任务调度器通常使用队列来管理待处理的任务。
  • 广度优先搜索(BFS):在图或树的遍历中,BFS使用队列来记录访问顺序。
  • 消息队列:在分布式系统中,消息队列用于异步通信和任务分发。
  • 缓冲区管理:如打印任务缓冲、网络数据包缓冲等。

5. 队列的性能分析

队列的主要操作(Enqueue、Dequeue、Front、Back)通常具有常数时间复杂度(O(1)),无论是基于数组还是链表的实现。这使得队列在需要频繁进行这些操作的场景下,表现出色。

基于数组的队列(循环队列)

  • 时间复杂度
    • Enqueue: O(1) 平均,O(n) 在需要扩展数组时
    • Dequeue: O(1)
    • Front: O(1)
    • Back: O(1)
  • 空间复杂度:预先分配的空间固定,但循环数组能有效利用空间,减少浪费。

基于链表的队列

  • 时间复杂度
    • Enqueue: O(1)
    • Dequeue: O(1)
    • Front: O(1)
    • Back: O(1)
  • 空间复杂度:无需预先分配固定大小的空间,但每个元素需要额外的指针存储空间。

6. 实战示例:任务调度

在多线程或异步编程中,任务调度是一个常见的需求。通过队列,我们可以实现一个简单的任务调度器,确保任务按照提交的顺序依次执行。

问题描述:构建一个简单的任务调度器,支持任务的提交和按顺序执行。

解决思路

  1. 使用std::queue存储待执行的任务。
  2. 提供一个接口用于提交任务。
  3. 提供一个接口用于执行下一个任务。

代码实现

#include <iostream>
#include <queue>
#include <functional>
#include <thread>
#include <mutex>
#include <condition_variable>class TaskScheduler {
private:std::queue<std::function<void()>> tasks;std::mutex mtx;std::condition_variable cv;bool stop;std::thread worker;void workerThread() {while(true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this]{ return !tasks.empty() || stop; });if(stop && tasks.empty()) return;task = tasks.front();tasks.pop();}task();}}public:TaskScheduler() : stop(false), worker(&TaskScheduler::workerThread, this) {}~TaskScheduler() {{std::unique_lock<std::mutex> lock(mtx);stop = true;}cv.notify_all();worker.join();}void submit(std::function<void()> task) {{std::unique_lock<std::mutex> lock(mtx);tasks.push(task);}cv.notify_one();}
};int main() {TaskScheduler scheduler;// 提交任务scheduler.submit([](){ std::cout << "任务1执行\n"; });scheduler.submit([](){ std::cout << "任务2执行\n"; });scheduler.submit([](){ std::cout << "任务3执行\n"; });// 给出一些时间让任务执行std::this_thread::sleep_for(std::chrono::seconds(1));return 0;
}

输出

任务1执行
任务2执行
任务3执行

说明

  • TaskScheduler类内部维护一个任务队列,使用互斥锁和条件变量确保线程安全和高效的任务调度。
  • workerThread函数在后台线程中不断等待任务,并按顺序执行。
  • 当任务调度器销毁时,会通知后台线程停止工作。

复杂度分析

  • 提交任务:O(1),入队操作。
  • 执行任务:依赖任务本身的执行时间,入队和出队操作为O(1)。

栈与队列的比较与选择

在实际开发中,选择合适的数据结构是提升代码效率和可读性的关键。队列虽然都是线性数据结构,但它们的访问顺序不同,适用于不同的应用场景。

【体验最新的GPT系列模型!支持Open API调用、自定义助手、文件上传等强大功能,助您提升工作效率!点击链接体验:CodeMoss & ChatGPT-AI中文版】

特性栈(Stack)队列(Queue)
访问顺序后进先出(LIFO)先进先出(FIFO)
常见应用函数调用管理、表达式解析、DFS任务调度、BFS、消息队列
实现方式基于数组或链表基于数组的循环队列或基于链表
操作复杂度常数时间(O(1))常数时间(O(1))
内存使用需要预先分配或动态调整基于循环数组时内存利用率高,基于链表时每个元素有额外指针

选择建议

  • 使用栈

    • 当需要逆序处理数据时,如后进先出。
    • 在递归实现中,用于模拟系统调用栈。
    • 实现浏览器的后退功能。
  • 使用队列

    • 当需要按顺序处理任务时,如先到先服务。
    • 在广度优先搜索算法中。
    • 实现消息传递系统。

性能优化技巧

尽管栈和队列的基础操作已经具有高效的性能,但在特定场景下,进一步优化仍然可以带来显著的性能提升。以下是一些实用的优化技巧:

1. 选择合适的底层容器

C++中的std::stackstd::queue默认基于std::deque实现,但在某些情况下,选择其他容器(如std::vectorstd::list)可能更合适。

  • :如果不需要频繁的插入和删除操作,可以选择std::vector作为底层容器,因为它具有更好的缓存局部性。

    std::stack<int, std::vector<int>> s;
    
  • 队列:通常std::deque已经足够高效,但在特定情况下,可以考虑自定义的循环数组实现以减少内存分配。

2. 避免不必要的内存分配

频繁的内存分配和释放会影响程序性能。通过预先分配足够的空间,可以减少内存分配的次数。

  • std::vector<int> vec;
    vec.reserve(1000); // 预先分配空间
    std::stack<int, std::vector<int>> s(vec);
    
  • 队列

    如果使用基于数组的实现,可以设计循环队列以有效利用已分配的空间。

3. 使用移动语义

在C++11及以上版本中,利用移动语义可以减少不必要的对象拷贝,提升性能。

#include <stack>
#include <string>
#include <utility> // for std::moveint main() {std::stack<std::string> s;std::string str = "Hello, World!";s.push(std::move(str)); // 移动而非拷贝return 0;
}

4. 并行处理

对于队列,尤其是在多线程环境中,可以通过锁或无锁队列实现安全的并行访问,提升系统吞吐量。

无锁队列示例(基于C++11的原子操作):

#include <atomic>
#include <memory>template<typename T>
class LockFreeQueue {
private:struct Node {std::shared_ptr<T> data;Node* next;Node() : next(nullptr) {}};std::atomic<Node*> head;std::atomic<Node*> tail;public:LockFreeQueue() {Node* dummy = new Node();head.store(dummy);tail.store(dummy);}~LockFreeQueue() {while(Node* node = head.load()) {head.store(node->next);delete node;}}void enqueue(T value) {std::shared_ptr<T> newData = std::make_shared<T>(std::move(value));Node* newNode = new Node();newNode->data = newData;Node* oldTail = nullptr;while(true) {oldTail = tail.load();Node* tailNext = oldTail->next;if(tail.load() == oldTail) {if(tailNext == nullptr) {if(std::atomic_compare_exchange_weak(&oldTail->next, &tailNext, newNode)) {break;}} else {std::atomic_compare_exchange_weak(&tail, &oldTail, tailNext);}}}std::atomic_compare_exchange_weak(&tail, &oldTail, newNode);}std::shared_ptr<T> dequeue() {while(true) {Node* oldHead = head.load();Node* oldTail = tail.load();Node* headNext = oldHead->next;if(oldHead == head.load()) {if(oldHead == oldTail) {if(headNext == nullptr) {return std::make_shared<T>(); // Queue is empty}std::atomic_compare_exchange_weak(&tail, &oldTail, headNext);} else {if(std::atomic_compare_exchange_weak(&head, &oldHead, headNext)) {return headNext->data;}}}}}
};

说明

  • 无锁队列通过原子操作确保并发安全,适用于高性能和多线程环境。
  • 需要深入理解原子操作和内存模型,确保正确性。

5. 编译器优化

利用编译器优化选项,可以进一步提升代码执行效率。常用的优化选项包括-O2-O3等。

g++ -O3 -std=c++17 -o program program.cpp

注意:过度优化可能导致调试困难,应在确保程序正确性的前提下进行优化。


更多文章

【OpenAI】获取OpenAI API Key的多种方式全攻略:从入门到精通,再到详解教程!!

【VScode】VSCode中的智能编程利器,全面揭秘ChatMoss & ChatGPT中文版

结论

栈与队列作为C++中两种基础且重要的数据结构,在软件开发中具有广泛的应用场景。通过深入理解它们的实现原理、使用方法及性能特点,开发者能够更加高效地选择和应用合适的数据结构,提升程序性能和代码质量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/61045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【css实现收货地址下边的平行四边形彩色线条】

废话不多说&#xff0c;直接上代码&#xff1a; <div class"address-block" ><!-- 其他内容... --><div class"checked-ar"></div> </div> .address-block{height:120px;position: relative;overflow: hidden;width: 500p…

从零开始配置Qt+VsCode环境

从零开始配置QtVsCode环境 文章目录 从零开始配置QtVsCode环境写在前面扩展安装及配置Qt Configure配置 VsCode创建Qt工程VsCodeQMakeMinGwVsCodeQMakeMsvcVsCodeCMakeMinGwVsCodeCMakeMsvcQtCreatorQMakeMinGw->VsCodeQtCreatorQMakeMsvc->VsCodeQtCreatorCMakeMinGw-&g…

【前端】JavaScript中的字面量概念与应用详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;字面量1. 数字字面量2. 字符串字面量3. 布尔字面量4. 空值字面量&#xff08;null&#xff09;5. 对象字面量6. 数组字面量7. 正则表达式字面量8. 特殊值字面量9. 函数字…

Python使用ffmpeg进行本地视频拉流,并使用训练模型识别人脸,并将识别后的模型推流源码

前言&#xff1a; Windows上搭建nginx-rtsp流媒体服务器&#xff0c;实现FFmpeg推流、录像转rtsp推流 - WayWayWayne - 博客园参考上述文章和一些webRTC前端拉流文章 主要是缕一缕思路和每个部分的代码功能&#xff0c;文件命名高度相似导致。 效果&#xff1a; 代码&#x…

【去毛刺】OpenCV图像处理基础:腐蚀与膨胀操作入门

在数字图像处理中&#xff0c;形态学操作是一种常用的技术&#xff0c;用于提取图像中的特定形状或特征。其中&#xff0c;腐蚀&#xff08;Erosion&#xff09;和膨胀&#xff08;Dilation&#xff09;是两种基本的形态学运算。本文将通过一个简单的例子来演示如何使用Python中…

2024年11月27日Github流行趋势

项目名称&#xff1a;screenshot-to-code 项目维护者&#xff1a;abi clean99 sweep-ai kachbit vagusX项目介绍&#xff1a;通过上传截图将其转换为整洁的代码&#xff08;支持HTML/Tailwind/React/Vue&#xff09;。项目star数&#xff1a;62,429项目fork数&#xff1a;7,614…

Linux八股积累与笔记

1、iptables 是一个用于配置Linux内核防火墙规则的工具。四表五链&#xff1a;在iptables中&#xff0c;有四个表&#xff08;tables&#xff09;和五个链&#xff08;chains&#xff09;&#xff0c;用于管理不同类型的数据包过滤规则。如下&#xff1a; 表&#xff08;Tabl…

Qt5.14.2的安装与环境变量及一些依赖库的配置

目录 1.Qt5.14.2安装 2.Qt环境变量及一些依赖库的配置 1.Qt5.14.2安装 QT从入门到入土&#xff08;一&#xff09;——Qt5.14.2安装教程和VS2019环境配置 - 唯有自己强大 - 博客园 2.Qt环境变量及一些依赖库的配置 假设QT安装目录为: D:\Qt\Qt5.14.2 将目录: D:\Qt\Qt5.14.…

初识Linux(4):Linux基础环境工具(下)

1. Git Git是一种版本控制系统&#xff0c;是一种工具&#xff0c;用于代码的存储和版本控制。 而我们常见的Gitee和Gitehub都是基于Git&#xff08;Git是开源的&#xff09;实现的在线代码仓库&#xff0c;而前者服务器位于中国&#xff0c;后者服务器位于美国。 总的来说&…

12.Three.js纹理动画与动效墙案例

12.Three.js纹理动画与动效墙案例 在Three.js的数字孪生场景应用中&#xff0c;我们通常会使用到一些动画渲染效果&#xff0c;如动效墙&#xff0c;飞线、雷达等等&#xff0c;今天主要了解一下其中一种动画渲染效果&#xff1a;纹理动画。下面实现以下动效墙效果&#xff08…

《白帽子讲Web安全》13-14章

《白帽子讲Web安全》13-14章 《白帽子讲Web安全》13-14章13、应用层拒绝服务攻击13.1、DDOS简介13.2、应用层DDOS13.2.1、CC攻击13.2.2、限制请求频率13.2.3、道高一尺&#xff0c;魔高一丈 13.3、验证码的那些事儿13.4、防御应用层DDOS13.5、资源耗尽攻击13.5.1、Slowloris攻击…

【电子元器件】Nand Flash基础介绍

本文章是笔者理论结合实践进行整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、什么是Nand Flash Flash主要分两种&#xff0c;Nand Flash和Nor flash。 Nor的成本相对高&#xff0c…

JVM_垃圾收集器详解

1、 前言 JVM就是Java虚拟机&#xff0c;说白了就是为了屏蔽底层操作系统的不一致而设计出来的一个虚拟机&#xff0c;让用户更加专注上层&#xff0c;而不用在乎下层的一个产品。这就是JVM的跨平台&#xff0c;一次编译&#xff0c;到处运行。 而JVM中的核心功能其实就是自动…

python除了熟悉的pandas,openpyxl库也很方便的支持编辑Excel表

excel表格是大家经常用到的文件格式&#xff0c;各行各业都会跟它打交道。之前文章我们介绍了使用openpyxl和xlrd库读取excel表数据&#xff0c;使用xlwt库创建和编辑excel表&#xff0c;在办公自动化方面可以方便我们快速处理数据&#xff0c;帮助我们提升效率。 python之open…

网络知识1-TCP/IP模型

从用户端到服务端&#xff0c;tcp/ip模型可分为应用层、传输层、网络层、网络接口层 以下使用寄快递为例进行解释 应用层职责&#xff1a; 只关注与为用户提供应用功能&#xff0c;如HTTP、FTP、telnet、DNS、SMTP等 &#xff0c;应用层的职责就像我们寄快递时将快递给快递员…

机器学习(二十五):决策树算法以及决策树和神经网络的对比

一、决策树集合 单一决策树会对训练数据的变化很敏感。例子&#xff1a;输入十个数据&#xff0c;判断是否是猫。只替换其中一个数据&#xff0c;信息增益最高的分裂特征就发生了改变&#xff0c;决策树就发生了变化。 使用决策树集合可以使算法更加健壮。例子&#xff1a;使用…

通俗理解人工智能、机器学习和深度学习的关系

最近几年人工智能成为极其热门的概念和话题&#xff0c;可以说彻底出圈了。但人工智能的概念在1955年就提出来了&#xff0c;可以说非常古老。我在上小学的时候《科学》课本上就有人工智能的概念介绍&#xff0c;至今还有印象&#xff0c;但那些年AI正处于“寒冬”&#xff0c;…

CDAF / PDAF 原理 | PDAF、CDAF 和 LAAF 对比 | 图像清晰度评价指标

注&#xff1a;本文为 “CDAF / PDAF 原理 | PDAF、CDAF 和 LAAF 对比 | 图像清晰度评价指标” 几篇相关文章合辑。 文章中部分超链接、图片异常受引用之前的原文所限。 相机自动对焦原理 TriumphRay 于 2020-01-16 18:59:41 发布 凸透镜成像原理 这一部分大家中学应该就学过…

文件上传upload-labs-docker通关

&#xff08;图片加载不出&#xff0c;说明被和谐了&#xff09; 项目一&#xff1a; sqlsec/ggctf-upload - Docker Image | Docker Hub 学习过程中,可以对照源码进行白盒分析. 补充&#xff1a;环境搭建在Linux虚拟机上的同时&#xff0c;以另一台Windows虚拟机进行测试最…

Linux的介绍及虚拟机centOS系统的下载与应用

1、什么是Linux Linux 是一种类 Unix 操作系统&#xff0c;它的内核&#xff08;Kernel&#xff09;由 Linus Torvalds 于 1991 年首次发布。作为一个开源、免费的操作系统&#xff0c;Linux 被广泛用于服务器、桌面计算机、嵌入式设备、移动设备等各种场景。 1、操作系统 操…