数据结构中的栈与队列:原理、实现与应用

前言:栈和队列是计算机科学中两种最基础的线性数据结构,它们的独特操作规则和广泛的应用场景使其成为每一位开发者必须掌握的核心知识。本文将通过生活案例、代码实现和实际应用场景,带您深入理解这两种数据结构的精髓。

1.栈(Stack)

基本定义

栈是一种受限的线性表,仅允许在同一端(栈顶)进行数据插入(push)和删除(pop)操作。其核心特性遵循LIFO(后进先出)(Last In First Out)原则,即后进入的元素优先被访问。

LIFO原则

  • 后进先出(Last In First Out):最后入栈的元素总是最先出栈

  • 入栈(Push):将新元素放入栈顶

  • 出栈(Pop):移除并返回栈顶元素

核心机制
  • 单端操作:所有操作集中在栈顶完成

  • 动态指针:通过栈顶指针(top)实时跟踪最新元素位置

  • 操作限制禁止直接访问中间元素,必须按序操作

  • 空间管理:顺序栈需预判容量,链式栈动态扩展但需额外指针空间

2.栈的实现

实现方式优点缺点适用场景
动态数组缓存友好,访问速度快扩容时需要拷贝数据通用场景
链表无需预分配空间每个元素需要额外指针空间元素数量变化剧烈
静态数组
内存分配确定容量固定,不够灵活嵌入式系统/内存受限环境

本文将以动态数组为基础,手把手带你用C语言实现一个智能的“自动扩容栈”。

/* 栈元素类型定义(方便修改数据类型) */
typedef int STDataType;/* 栈结构体定义arr: 动态数组首地址top: 栈顶指针(当前元素数量)capacity: 数组总容量 */
typedef struct Stack {STDataType* arr;int top;        int capacity;   
} ST;/* 功能:初始化栈结构参数:ps - 指向栈的指针关键点:初始状态数组为空,容量为0 */
void STInit(ST* ps) {assert(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}/* 功能:压入元素到栈顶参数:ps - 栈指针,x - 要入栈的元素关键点:自动扩容策略(4→8→16...)示例:初始容量0时首次push会扩容到4 */
void STPush(ST* ps, STDataType x) {assert(ps);// 容量检查与扩容if (ps->capacity == ps->top) {int newcapacity = ps->capacity ? 2*ps->capacity : 4;STDataType* temp = realloc(ps->arr, newcapacity*sizeof(STDataType));if (!temp) { perror("malloc"); exit(1); }ps->arr = temp;ps->capacity = newcapacity;}ps->arr[ps->top++] = x; // 先存数据再移动指针
}/* 功能:判断栈是否为空参数:ps - 栈指针返回:true表示空栈,false非空示例:初始化后返回true,push后返回false */
bool STEmpty(ST* ps) {assert(ps);return ps->top == 0;
}/* 功能:弹出栈顶元素参数:ps - 栈指针关键点:实际只移动指针,不删除数据注意:空栈弹出会导致断言失败 */
void STPop(ST* ps) {assert(ps && !STEmpty(ps));ps->top--; // 逻辑删除
}/* 功能:获取栈元素数量参数:ps - 栈指针返回:当前元素个数示例:push三次后返回3 */
int STSize(ST* ps) {assert(ps);return ps->top; // top直接表示数量
}/* 功能:查看栈顶元素(不弹出)参数:ps - 栈指针返回:栈顶元素值关键点:top-1才是最后存入的位置示例:栈内[10,20]时返回20 */
STDataType STTop(ST* ps) {assert(ps && !STEmpty(ps));return ps->arr[ps->top-1];
}/* 功能:销毁栈并释放内存参数:ps - 栈指针关键点:必须调用防止内存泄漏注意:调用后栈不可再使用 */
void STDestroy(ST* ps) {assert(ps);if (ps->arr) free(ps->arr);ps->arr = NULL;ps->capacity = ps->top = 0;
}

测试用例 

int main()
{ST st;          // 声明栈变量(未初始化状态)STInit(&st);    // 初始化栈 → arr=NULL, capacity=0, top=0STPush(&st, 1);  // 触发扩容(0→4)→ arr[0]=1, top=1printf("%d\n", STSize(&st)); // 输出1(当前元素数量)STPush(&st, 2);  // arr[1]=2, top=2printf("%d\n", STSize(&st)); // 输出2STPush(&st, 3);  // arr[2]=3, top=3printf("%d\n", STSize(&st)); // 输出3STPush(&st, 4);  // arr[3]=4, top=4(栈满)printf("%d\n", STSize(&st)); // 输出4while (!STEmpty(&st)) //打印栈中所有元素{          printf("%d ", STTop(&st));  // 从栈顶开始输出:4 3 2 1STPop(&st);           // 每次循环top减1}printf("\n%d", STSize(&st)); // 输出0(此时栈空)STDestroy(&st);          // 必须调用!释放动态内存return 0;
}

3.队列(Queue)

基本定义

队列是另一种受限线性表,遵循FIFO(First In First Out)原则。元素从队尾(rear)插入(enqueue),从队首(front)移除,保持严格的先进先出顺序。

核心机制
  • 双指针系统:通过 front rear 指针分别管理两端

  • 循环优化:使用模运算实现环形缓冲区解决"假溢出"问题

  • 等待特性:元素按到达顺序被处理,具有公平性特征

  • 容量管理:动态队列可扩展,但需考虑内存碎片问题

4.队列的实现

实现方式优点缺点适用场景
普通数组队列实现简单出队效率低(需要搬移元素)教学演示
循环数组队列高效利用内存需处理队空/队满边界条件固定容量生产消费场景
链表队列动态扩容灵活内存碎片化,缓存不友好频繁扩容/元素大小不一
动态数组队列平衡性能与灵活性扩容时产生瞬时延迟通用场景

在本文中,会着重介绍下面三种队列,普通数组队列的使用并不常见,我们在这里并不过多介绍,大家可以自己尝试着实现。首先,我们实现链式队列。循环数组队列和动态数组队列我们作为后面习题补充会涉及到。

注意:链式队列的实现与单链表的实现大差不差,前面单链表的实现非常重要,能够完成单链表的实现代码,实现链式队列代码就不成问题。

typedef int QDataType;  // 队列元素类型别名,方便修改存储类型// 队列节点结构(链式存储)
typedef struct QueueNode {int val;              // 存储数据struct QueueNode* next; // 指向下一个节点
} QNode;// 队列管理结构
typedef struct Queue {QNode* phead;  // 队头指针(删除端)QNode* ptail;  // 队尾指针(插入端)int size;       // 当前元素数量
} Queue;// 功能:初始化队列结构
// 参数:pq - 指向队列的指针
// 关键点:将指针置空,size清零
void QueueInit(Queue* pq) {assert(pq);  // 防御性编程,确保指针有效pq->phead = pq->ptail = NULL;pq->size = 0;  // 初始为空队列
}// 功能:创建新节点
// 参数:x - 要存储的数据
// 返回:新节点指针
// 关键点:内存分配与初始化
QNode* Createnode(QDataType x) {QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL) {          // 内存不足处理perror("malloc");           // 打印错误信息exit(1);                    // 直接终止程序}newnode->val = x;               // 存储数据newnode->next = NULL;           // 初始化指针return newnode;
}// 功能:在队尾插入新元素
// 参数:pq - 队列指针,x - 要插入的数据
// 关键点:维护头尾指针关系
void QueuePush(Queue* pq, QDataType x) {assert(pq);QNode* newnode = Createnode(x);  // 创建新节点if (pq->phead == NULL) {        // 空队列情况pq->phead = pq->ptail = newnode;} else {                        // 非空队列pq->ptail->next = newnode;  // 链接新节点pq->ptail = newnode;        // 更新尾指针}pq->size++;  // 元素计数增加
}// 功能:判断队列是否为空
// 参数:pq - 队列指针
// 返回:true为空,false非空
// 关键点:只需检查头指针
bool QueueEmpty(Queue* pq) {assert(pq);return pq->phead == NULL;  // 头指针为空即队列空
}// 功能:删除队头元素
// 参数:pq - 队列指针
// 关键点:处理最后一个节点的特殊情况
void QueuePop(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));  // 确保队列非空QNode* del = pq->phead;     // 保存待删除节点pq->phead = pq->phead->next;// 头指针后移// 处理删除最后一个元素的情况if (pq->phead == NULL) {pq->ptail = NULL;      // 尾指针也必须置空}free(del);      // 释放节点内存pq->size--;     // 元素计数减少
}// 功能:查看队头元素(不删除)
// 参数:pq - 队列指针
// 返回:队头元素值
// 注意:队列为空时触发断言
QDataType QueueFront(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));  // 防御空队列访问return pq->phead->val;    // 直接返回头节点值
}// 功能:查看队尾元素(不删除)
// 参数:pq - 队列指针
// 返回:队尾元素值
QDataType QueueBack(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));  // 防御空队列访问return pq->ptail->val;    // 直接返回尾节点值
}// 功能:获取当前元素数量
// 参数:pq - 队列指针
// 返回:队列元素个数
// 优势:O(1)时间复杂度
int QueueSize(Queue* pq) {assert(pq);return pq->size;  // 直接返回计数器值
}// 功能:释放队列所有内存
// 参数:pq - 队列指针
// 关键点:必须遍历释放所有节点
void QueueDestroy(Queue* pq) {assert(pq);QNode* curr = pq->phead;while (curr) {QNode* del = curr;   // 保存当前节点curr = curr->next;   // 移动到下一个节点free(del);           // 释放当前节点}pq->phead = pq->ptail = NULL;  // 重置指针pq->size = 0;                  // 重置计数器
}

测试用例

int main() {Queue q;QueueInit(&q);  // 必须初始化QueuePush(&q, 10);QueuePush(&q, 20);printf("队头:%d\n", QueueFront(&q));  // 输出10QueuePop(&q);  // 删除10printf("当前大小:%d\n", QueueSize(&q));  // 输出1QueueDestroy(&q);  // 必须销毁return 0;
}

 5.栈与队列算法题

5.1 力扣:有效的括号

 1. 核心思想

  • 栈的LIFO特性:利用栈后进先出的特性,确保最近打开的括号必须最先闭合

  • 匹配原则:遍历字符串时,遇到左括号入栈,遇到右括号必须与栈顶左括号类型匹配

2. 执行流程

  1. 初始化栈:创建空栈用于存储待匹配的左括号

  2. 遍历字符:逐个处理输入字符串的每个字符

    • 左括号处理(/[/{ → 压入栈顶

    • 右括号处理

      • 检查栈空 → 栈空直接返回false

      • 栈非空 → 检查栈顶是否匹配 → 匹配则弹出,不匹配返回false

  3. 最终校验:遍历完成后检查栈是否为空(所有左括号均已匹配)

//代码中所用函数都是上文栈的实现中的函数
bool isValid(char* s) {ST st;STInit(&st);while(*s != '\0'){if(*s == '(' || *s == '[' || *s == '{'){STPush(&st,*s);}else{if(STEmpty(&st))return false;STDataType ch1 = STTop(&st);if((ch1 == '(' && *s == ')')|| (ch1 == '[' && *s == ']')|| (ch1 == '{' && *s == '}'))STPop(&st);else{STDestroy(&st);return false;}}s++;}if(!STEmpty(&st))return false;STDestroy(&st);return true;
}

5.2力扣:用队列实现栈 

// 代码中所用函数都是上文队列的实现中的函数// 使用两个队列实现栈的结构定义
typedef struct {Queue q1;Queue q2;
} MyStack;// 创建栈实例并初始化两个队列
MyStack* myStackCreate() 
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}// 入栈操作:将元素压入非空队列(若都为空则选择q1)
void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1))QueuePush(&obj->q1, x);elseQueuePush(&obj->q2, x);
}// 出栈操作:将非空队列的前n-1个元素转移到空队列,弹出最后一个元素
int myStackPop(MyStack* obj) {Queue* emptyQue = &obj->q1;Queue* nonEmptyQue = &obj->q2;// 确定空队列和非空队列if(QueueEmpty(nonEmptyQue)) {emptyQue = &obj->q2;nonEmptyQue = &obj->q1;}// 将非空队列的前n-1个元素转移到空队列while(QueueSize(nonEmptyQue) > 1) {QDataType data = QueueFront(nonEmptyQue);QueuePop(nonEmptyQue);QueuePush(emptyQue, data);}// 弹出并返回最后一个元素QDataType ret = QueueFront(nonEmptyQue);QueuePop(nonEmptyQue);return ret;
}// 获取栈顶元素:直接返回非空队列的队尾元素
int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->q1))return QueueBack(&obj->q1);elsereturn QueueBack(&obj->q2);
}// 判断栈是否为空:当两个队列都为空时栈为空
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}// 释放栈内存:销毁两个队列并释放栈结构
void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);free(obj);obj = NULL;
}

 5.3力扣:用栈实现队列

 

// 代码中所用函数都是上文栈的实现中的函数// 使用两个栈实现队列的结构定义
typedef struct {ST Pushst;    // 用于入队操作的栈ST Popst;     // 用于出队操作的栈
} MyQueue;// 创建队列实例并初始化两个栈
MyQueue* myQueueCreate() {MyQueue* pst = (MyQueue*)malloc(sizeof(MyQueue));STInit(&pst->Pushst);STInit(&pst->Popst);return pst;
}// 入队操作:直接压入Push栈
void myQueuePush(MyQueue* obj, int x) {STPush(&obj->Pushst, x);
}// 出队操作:若Pop栈为空,则将Push栈元素全部倒入Pop栈,再弹出Pop栈顶元素
int myQueuePop(MyQueue* obj) {if(STEmpty(&obj->Popst)) {// 将Push栈的元素逆序倒入Pop栈while(!STEmpty(&obj->Pushst)) {STDataType data = STTop(&obj->Pushst);STPop(&obj->Pushst);STPush(&obj->Popst, data);}}STDataType ret = STTop(&obj->Popst);STPop(&obj->Popst);return ret;
}// 获取队首元素:逻辑同出队,但不弹出元素
int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->Popst)) {while(!STEmpty(&obj->Pushst)) {STDataType data = STTop(&obj->Pushst);STPop(&obj->Pushst);STPush(&obj->Popst, data);}}return STTop(&obj->Popst);
}// 判断队列是否为空:当两个栈都为空时队列为空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->Popst) && STEmpty(&obj->Pushst);
}// 释放队列内存:销毁两个栈并释放队列结构
void myQueueFree(MyQueue* obj) {STDestroy(&obj->Popst);STDestroy(&obj->Pushst);free(obj);obj = NULL;
}

 5.4力扣:设计循环队列

// 使用数组实现的环形队列结构
typedef struct {int* arr;        // 存储队列元素的数组int front;       // 队头指针:指向队列第一个有效元素int rear;        // 队尾指针:指向队列最后一个元素的下一个位置int capacity;    // 队列的最大容量(实际存储元素个数为capacity)
} MyCircularQueue;// 创建环形队列实例,k为队列容量
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));// 分配k+1大小的数组,浪费一个空间用于区分队列满和空的状态obj->arr = (int*)malloc(sizeof(int) * (k + 1));obj->front = obj->rear = 0;    // 初始化队头队尾指针obj->capacity = k;             // 设置队列容量return obj;
}// 判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {// 当队头和队尾指针指向同一位置时,队列为空return obj->rear == obj->front;
}// 判断队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {// 当队尾指针的下一个位置是队头指针时,队列已满// 使用取模运算实现环形效果return (obj->rear + 1) % (obj->capacity + 1) == obj->front;
}// 入队操作
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj))return false;    // 队列已满,入队失败obj->arr[obj->rear] = value;    // 将元素放入队尾位置obj->rear = (obj->rear + 1) % (obj->capacity + 1);    // 更新队尾指针,循环前进return true;
}// 出队操作
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return false;    // 队列为空,出队失败obj->front = (obj->front + 1) % (obj->capacity + 1);    // 更新队头指针,循环前进return true;
}// 获取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;    // 队列为空,返回-1表示无效值return obj->arr[obj->front];    // 返回队头指针指向的元素
}// 获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;    // 队列为空,返回-1表示无效值// 计算队尾元素的实际位置(队尾指针前一个位置)int prev = (obj->rear - 1 + (obj->capacity + 1)) % (obj->capacity + 1);return obj->arr[prev];
}// 释放队列内存
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->arr);    // 释放存储元素的数组free(obj);         // 释放队列结构体obj = NULL;        // 防止野指针
}
  1. 数组大小

    • 实际分配大小为 capacity + 1浪费一个空间用于区分队列满和队列空的状态
    • 队列满条件:(rear + 1) % (capacity + 1) == front
    • 队列空条件:rear == front
  2. 指针移动

    • 队头指针front:指向队列的第一个有效元素
    • 队尾指针rear:指向队列最后一个元素的下一个位置
    • 使用% (capacity + 1)实现环形效果
  3. 边界处理

    • 入队时直接在rear位置赋值,然后rear指针后移
    • 出队时直接将front指针后移,无需真正删除元素
    • 获取队尾元素时需要计算rear的前一个位置,处理rear为 0 的情况

5.5设计动态循环列表

#include <stdlib.h>
#include <assert.h>typedef struct {int* data;       // 存储队列元素的动态数组int front;       // 队头指针,指向队列第一个元素int rear;        // 队尾指针,指向队列最后一个元素的下一个位置int capacity;    // 当前队列容量int size;        // 当前队列元素个数
} DynamicQueue;// 创建动态队列,初始容量为initialCapacity
DynamicQueue* createQueue(int initialCapacity) {DynamicQueue* queue = (DynamicQueue*)malloc(sizeof(DynamicQueue));assert(queue != NULL);queue->data = (int*)malloc(sizeof(int) * initialCapacity);assert(queue->data != NULL);queue->front = 0;queue->rear = 0;queue->size = 0;queue->capacity = initialCapacity;return queue;
}// 判断队列是否为空
bool isEmpty(DynamicQueue* queue) {return queue->size == 0;
}// 判断队列是否已满(实际不会满,会自动扩容)
bool isFull(DynamicQueue* queue) {return queue->size == queue->capacity;
}// 扩容队列,新容量为原容量的2倍
void resizeQueue(DynamicQueue* queue) {int newCapacity = queue->capacity * 2;int* newData = (int*)malloc(sizeof(int) * newCapacity);assert(newData != NULL);// 复制原队列元素到新数组for (int i = 0; i < queue->size; i++) {newData[i] = queue->data[(queue->front + i) % queue->capacity];}free(queue->data);queue->data = newData;queue->front = 0;queue->rear = queue->size;queue->capacity = newCapacity;
}// 入队操作
void enqueue(DynamicQueue* queue, int value) {if (isFull(queue)) {resizeQueue(queue);}queue->data[queue->rear] = value;queue->rear = (queue->rear + 1) % queue->capacity;queue->size++;
}// 出队操作,返回队头元素
int dequeue(DynamicQueue* queue) {assert(!isEmpty(queue));int value = queue->data[queue->front];queue->front = (queue->front + 1) % queue->capacity;queue->size--;return value;
}// 获取队头元素
int getFront(DynamicQueue* queue) {assert(!isEmpty(queue));return queue->data[queue->front];
}// 获取队列大小
int getSize(DynamicQueue* queue) {return queue->size;
}// 释放队列内存
void freeQueue(DynamicQueue* queue) {free(queue->data);free(queue);
}

 

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

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

相关文章

如何选择自己喜欢的cms

选择内容管理系统cms what is cms1.whatcms.org2.IsItWP.com4.Wappalyzer5.https://builtwith.com/6.https://w3techs.com/7. https://www.netcraft.com/8.onewebtool.com如何在不使用 CMS 检测器的情况下手动检测 CMS 结论 在开始构建自己的数字足迹之前&#xff0c;大多数人会…

SDC命令详解:使用all_outputs命令进行查询

相关阅读 SDC命令详解https://blog.csdn.net/weixin_45791458/category_12931432.html all_outputs命令用于创建一个输出端口对象集合&#xff0c;关于设计对象和集合的更详细介绍&#xff0c;可以参考下面的博客。 Synopsys&#xff1a;设计对象https://chenzhang.blog.csdn…

vue 中的ref

vue 中的ref vue 中的ref 1. ​​ref​​ ** 的基本作用** 在 Vue 中&#xff0c;ref 是用来获取 DOM 元素或者组件实例的一种方式。对于 <el-form> 组件&#xff0c;通过 ref 可以获取到该表单组件的实例&#xff0c;进而调用表单组件提供的各种方法和访问其属性。 …

数据库版本控制工具--flyway

一. 什么是Flyway Flyway 是一款开源的数据库迁移工具。它采用简单直观的方式管理数据库变更&#xff0c;通过版本化的迁移脚本确保数据库结构的一致性和可重复性。无论是开发环境、测试环境还是生产环境&#xff0c;Flyway 都能确保数据库变更按照预期顺序执行&#xff0c;避…

C++使用PoDoFo库处理PDF文件

&#x1f4da; PoDoFo 简介 PoDoFo 是一个用 C 编写的自由开源库&#xff0c;专用于 读取、写入和操作 PDF 文件。它适用于需要程序化处理 PDF 文件的应用程序&#xff0c;比如批量生成、修改、合并、提取元数据、绘图等。 &#x1f31f; 核心特点 特性说明&#x1f4c4; P…

论文分享➲ arXiv2025 | TTRL: Test-Time Reinforcement Learning

TTRL: Test-Time Reinforcement Learning TTRL&#xff1a;测试时强化学习 https://github.com/PRIME-RL/TTRL &#x1f4d6;导读&#xff1a;本篇博客有&#x1f9a5;精读版、&#x1f407;速读版及&#x1f914;思考三部分&#xff1b;精读版是全文的翻译&#xff0c;篇幅较…

dify插件接入fastmcp示例

文章目录 1. 使用python完成mcp服务1.1 准备环境&#xff08;python安装fastmcp&#xff09;1.2 mcp服务端示例代码1.3 启动mcp服务端 2. dify接入2.1 安装MCP SSE和 Agent 策略&#xff08;支持 MCP 工具&#xff09; 插件2.2 dify agent插件配置mcp:2.3 mcp服务配置&#xff…

Linux 挖矿木马排查命令清单

Linux 挖矿木马排查命令清单 1. 系统资源使用情况检查 # 查看CPU、内存使用情况 top -c# 检查CPU占用最高的进程 ps aux --sort-%cpu# 查找可疑进程名 ps -ef | grep -i miner\|cpu\|GPU\|xmr# 检查网络连接情况 lsof -i2. 可疑进程和隐藏进程检查 # 检查僵尸进程 ps -ef | …

PyTorch 中如何针对 GPU 和 TPU 使用不同的处理方式

一个简单的矩阵乘法例子来演示在 PyTorch 中如何针对 GPU 和 TPU 使用不同的处理方式。 这个例子会展示核心的区别在于如何获取和指定计算设备&#xff0c;以及&#xff08;对于 TPU&#xff09;可能需要额外的库和同步操作。 示例代码&#xff1a; import torch import tim…

自主shell命令行解释器

目标 能处理普通命令能处理内建命令 实现原理 用下面的时间轴来表示时间发生次序。时间从左向右。shell由标识为sh的方块&#xff0c;它随着时间从左向右移动。 shell从用户读入字符串“ls”。shell建立一个新的进程&#xff0c;然后等待进程中运行ls程序并等待进程结束。 …

如何在sheel中运行Spark

启动hdfs集群&#xff0c;打开hadoop100:9870&#xff0c;在wcinput目录下上传一个包含很多个单词的文本文件。 启动之后在spark-shell中写代码。 // 读取文件&#xff0c;得到RDD val rdd1 sc.textFile("hdfs://hadoop100:8020/wcinput/words.txt") // 将单词进行切…

【入门】数字走向II

描述 输入整数N&#xff0c;输出相应方阵。 输入描述 一个整数N。&#xff08; 0 < n < 10 ) 输出描述 一个方阵&#xff0c;每个数字的场宽为3。 #include <bits/stdc.h> using namespace std; int main() {int n;cin>>n;for(int in;i>1;i--){for(…

Python自动化-python基础(下)

六、带参数的装饰器 七、函数生成器 运行结果&#xff1a; 八、通过反射操作对象方法 1.添加和覆盖对象方法 2.删除对象方法 通过使用内建函数: delattr() # 删除 x.a() print("通过反射删除之后") delattr(x, "a") x.a()3 通过反射判断对象是否有指定…

重新定义高性能:Hyperlane —— Rust生态中的极速HTTP服务器

重新定义高性能&#xff1a;Hyperlane —— Rust生态中的极速HTTP服务器 &#x1f680; 为什么选择Hyperlane&#xff1f; 在追求极致性能的Web服务开发领域&#xff0c;Hyperlane 凭借其独特的Rust基因和架构设计&#xff0c;在最新基准测试中展现出令人惊艳的表现&#xff…

通俗的理解MFC消息机制

1. 消息是什么&#xff1f; 想象你家的门铃响了&#xff08;比如有人按门铃、敲门、或者有快递&#xff09;&#xff0c;这些都是“消息”。 在 MFC 中&#xff0c;消息就是系统或用户触发的各种事件&#xff0c;比如鼠标点击&#xff08;WM_LBUTTONDOWN&#xff09;、键盘输入…

腾讯开源SuperSonic:AI+BI如何重塑制造业数据分析?

目录 一、四款主流ChatBI产品 二、ChatBI应用案例与实际落地情况 三、SuperSonic底层原理 3.1、Headless BI 是什么 3.2、S2SQL 是什么 3.3、SuperSonic 平台架构 四、ChatBI应用细节深挖 五、与现有系统的集成方案 六、部署和安全 七、开源生态、可扩展性与二次开…

AI生成视频推荐

以下是一些好用的 AI 生成视频工具&#xff1a; 国内工具 可灵 &#xff1a;支持文本生成视频、图片生成视频&#xff0c;适用于广告、电影剪辑和短视频制作&#xff0c;能在 30 秒内生成 6 秒的高清视频&#xff08;1440p&#xff09;&#xff0c;目前处于免费测试阶段。 即…

OrangePi Zero 3学习笔记(Android篇)5 - usbutils编译(更新lsusb)

目录 1. Ubuntu中编译 2. AOSP编译 3. 去掉原来的配置 3. 打包 4. 验证lsusb 在Ubuntu中&#xff0c;lsusb的源代码源自usbutils。而OrangePi Zero 3中lsusb的位置可以看文件H618-Android12-Src/external/toybox/Android.bp&#xff0c; "toys/other/lsusb.c",…

bcm5482 phy 场景总结

1,BCM5482是一款双端口10/100/1000BASE-T以太网PHY芯片,支持多种速率和双工模式。其配置主要通过MDIO(Management Data Input/Output)接口进行,MDIO接口用于访问PHY芯片内部的寄存器,从而配置网络速率、双工模式以及其他相关参数。 a,具体以下面两种场景举例 2. 寄存器和…

RedHat磁盘的添加和扩容

前情提要 &#x1f9f1; 磁盘结构流程概念图&#xff1a; 物理磁盘 (/dev/sdX) └── 分区&#xff08;如 /dev/sdX1&#xff09;或整块磁盘&#xff08;直接使用&#xff09; └── 物理卷 (PV, 用 pvcreate) └── 卷组 (VG, 用 vgcreate) …