LeetCode/NowCoder-栈和队列OJ练习
孜孜不倦:孜孜:勤勉,不懈怠。指工作或学习勤奋不知疲倦。💓💓💓
目录
说在前面
题目一:括号匹配问题
题目二:用队列实现栈
题目三:用栈实现队列
题目四:设计循环队列
SUMUP结尾
说在前面
dear朋友们大家好!💖💖💖我们又见面了,有到了我们数据结构的刷题时间了。我们上次刚学完了栈和队列,现在正好练练手~
👇👇👇
友友们!🎉🎉🎉点击这里进入力扣leetcode学习🎉🎉🎉
以下是leetcode题库界面:
👇👇👇
🎉🎉🎉点击这里进入牛客网NowCoder刷题学习🎉🎉🎉
以下是NowCoder题库界面:
题目一:括号匹配问题
题目链接:20. 有效的括号 - 力扣(LeetCode)
题目描述:
题目分析:
思路:由于C语言没有单独提供栈的实现,我们首先需要把我们之前写的栈的实现的接口都复制到题当中,接口如下:
typedef char STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;//栈的初始化
void STInit(ST* pst)
{assert(pst);pst->a = NULL;//top指的是栈顶数据的下一个位置pst->top = pst->capacity = 0;
}//扩容
static void STCheckCapacity(ST* pst)
{if (pst->top == pst->capacity){int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;STDataType* temp = (STDataType*)realloc(pst->a, NewCapacity * sizeof(STDataType));if (temp == NULL){perror("realloc operation failed");exit(1);}pst->a = temp;pst->capacity = NewCapacity;}
}//入栈
void STPush(ST* pst, STDataType x)
{assert(pst);STCheckCapacity(pst);pst->a[pst->top++] = x;
}//出栈
void STPop(ST* pst)
{assert(pst && pst->top);pst->top--;
}//获取栈顶元素
STDataType STTop(ST* pst)
{assert(pst && pst->top);return pst->a[pst->top - 1];
}//检测栈是否为空
bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}//获取栈中有效元素个数
int STSize(ST* pst)
{assert(pst);return pst->top;
}//栈的销毁
void STDestroy(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}
在此基础上我们才能完成题解。
这题为什么会想到用栈来解决呢?我们可以认为每次得到一个右括号,都和从右边往左的第一个左括号进行匹配,如果匹配成功,就是对的,如果有哪一次匹配失败了,说明不正确,返回false。那么我们观察左括号很明显就有个特点,就是最后输入的左括号要先和右括号匹配,这和我们栈的LIFO(Last In First Out)是一样的。
比如:
像上面这个例子,我们可以用栈存放左括号。如果是三种左括号的一种,我们就把它压入栈中,如果是三种右开括号的一种,我们取出栈顶的左括号,和它进行匹配。
当放入这三个左括号,我们输入了一个右括号,此时我们需要得到栈顶的括号(STTop)和当前右有括号对比看是否匹配。如果匹配成功,我们还需要将这个数据删除,然后继续这个操作,也就是每次对比都会消耗一个左括号。
显然两个左括号都匹配成功,此时再继续压栈。最后两个右括号刚好也和两个左括号匹配成功,所以最后我们可以判断栈是否为空(STEmpty)来判断是否整体都是匹配成功的。
代码如下:
bool isValid(char* s) {ST st;STInit(&st);while(*s){//左括号入栈if(*s == '(' || *s == '[' || *s == '{'){STPush(&st, *s);}else//右括号去栈顶左括号尝试匹配{if(STEmpty(&st))//栈为空{STDestroy(&st);return false;}char top = STTop(&st);STPop(&st);//匹配失败if(*s == ')' && top != '('|| *s == ']' && top != '['|| *s == '}' && top != '{')return false;}s++;}bool ret = STEmpty(&st);STDestroy(&st);return ret;}
最后有一个地方需要注意,就是如果我们刚开始就进入右括号,此时没有做括号匹配,那么直接就跳出循环,栈也是为空的,就返回了true,这显然是错误的。所以我们在输入右括号的时候也要判断一下栈是否为空,若为空直接返回false。
题目二:用队列实现栈
题目链接:225. 用队列实现栈 - 力扣(LeetCode)
题目描述:
题目分析:
思路:和题目一同样的,我们需要将队列的实现的所有接口都复制到题目当中,才能在接下来使用队列,接口如下:
typedef int QDataType;typedef struct QueueNode
{QDataType val;struct QueueNode* next;
}QNode;typedef struct Queue
{struct QueueNode* phead;struct QueueNode* ptail;int size;
}Queue;//队列的初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc operation failed");exit(1);}newnode->next = NULL;newnode->val = x;if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;
}//队头出队列
void QueuePop(Queue* pq)
{assert(pq && pq->size);if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;
}//获取队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}//获取队列头部元素
QDataType QueueFront(Queue* pq)
{assert(pq && pq->phead);return pq->phead->val;
}//获取队列尾部元素
QDataType QueueBack(Queue* pq)
{assert(pq && pq->ptail);return pq->ptail->val;
}//检测队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}
有了上述接口以后,我们思考接下来如何用两个队列实现栈。
我们知道,栈是后入先出的,而队列是先入先出的。函数需要我们实现栈的基本操作:push(压栈)、pop(弹栈)、top(取栈顶元素)、empty(判空)。对于压栈来说,其实可以直接实现,我们把一个队列的队头当做栈底,队尾入队列(QueuePush)就相当于压栈。问题是如何弹栈呢?队列只能是一端入,另一端出,没办法直接实现删除队尾的数据。这个时候,我们可以借助另一个队列。我们可以将队列中的数据从队头开始都入到另一个队列的中,只留下最后一个队尾的数据,然后再删除这个数据就可以了。
这也是这道题中重要的思想 ,去栈顶元素可以直接用QueueBack实现,而判空就是两个队列都为空即为空,同时要注意压栈应该压入到不为空的队列中,栈顶元素返回的也是不为空的队列中的尾元素,请大家注意。
代码如下:
//创建包含两个队列的匿名结构体
typedef struct {Queue q1;Queue q2;
} MyStack;//初始化这两个队列
MyStack* myStackCreate() {MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}压栈,即压入不为空的队列中,如果都为空那就都行
void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1)){QueuePush(&obj->q1, x);}else{QueuePush(&obj->q2, x);}
}//弹栈
int myStackPop(MyStack* obj) {//假设法,假设q1是空的,若不成立那就q2是空的Queue* empty = &obj->q1;Queue* noempty = &obj->q2;if(!QueueEmpty(empty)){empty = &obj->q2;noempty = &obj->q1;}//把不空的队列入到空队列中,并留下最后一个while(QueueSize(noempty) - 1){QueuePush(empty, QueueFront(noempty));QueuePop(noempty);}int top = QueueFront(noempty);QueuePop(noempty);return top;
}//取栈顶元素
int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return 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);
}
题目三:用栈实现队列
题目链接:232. 用栈实现队列 - 力扣(LeetCode)
题目描述:
题目分析:
思路1:和队列实现栈是基本类似的,建议大家做了第二题再看这道题。不过这里有个地方不太一样,就是Pop,在队列实现栈中,我们只需要将队列中的数据插入到另外一个队列中,再删除剩下的那一个就行了,但是栈实现队列,将栈中的数据压栈到另外的一个栈,删除栈底的那个数据后,由于顺序反了,我们还需要将另一个栈的数据再放回来才行。
也就是这里会有区别,其他地方其实都和题目二是一样的。
代码如下:
//创建包含两个栈的匿名结构体
typedef struct {ST st1;ST st2;
} MyQueue;//初始化结构体
MyQueue* myQueueCreate() {MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));STInit(&pq->st1);STInit(&pq->st2);return pq;
}//队尾入队列
void myQueuePush(MyQueue* obj, int x) {if(!STEmpty(&obj->st1)){STPush(&obj->st1, x);}else{STPush(&obj->st2, x);}
}//队头出队列
int myQueuePop(MyQueue* obj) {ST* empty = &obj->st1;ST* noempty = &obj->st2;if(!STEmpty(empty)){empty = &obj->st2;noempty = &obj->st1;}while(STSize(noempty) > 1){STPush(empty, STTop(noempty));STPop(noempty);}int top = STTop(noempty);STPop(noempty);while(STSize(empty)){STPush(noempty, STTop(empty));STPop(empty);}return top;
}//获得队头数据
int myQueuePeek(MyQueue* obj) {ST* empty = &obj->st1;ST* noempty = &obj->st2;if(!STEmpty(empty)){empty = &obj->st2;noempty = &obj->st1;}while(STSize(noempty) > 1){STPush(empty, STTop(noempty));STPop(noempty);}int top = STTop(noempty);STPush(empty, STTop(noempty));STPop(noempty);while(STSize(empty)){STPush(noempty, STTop(empty));STPop(empty);}return top;
}//判空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->st1) && STEmpty(&obj->st2);
}//销毁队列
void myQueueFree(MyQueue* obj) {STDestroy(&obj->st1);STDestroy(&obj->st2);free(obj);
思路2:除了思路1,我们其实还有另外一种方法,就是将一个栈的栈顶当做队头,入数据,另外一个栈的栈顶当做队尾,出数据:
那么入队列就相当于将数据压入到左边的栈(pushst)中,出队列就相当于是删除右边的栈(popst)的栈顶数据,如果右边的栈中没有数据了,再把左边的栈的数据全部导入到右边,这样往复就可以了。
代码如下:
//创建包含两个栈的匿名结构体
typedef struct {ST pushst;ST popst;
} MyQueue;//初始化结构体内容
MyQueue* myQueueCreate() {MyQueue* pst = (MyQueue*)malloc(sizeof(MyQueue));STInit(&pst->pushst);STInit(&pst->popst);return pst;
}//队尾入队列
void myQueuePush(MyQueue* obj, int x) {STPush(&obj->pushst, x);//相当于压入pushst中
}//取得队头数据
int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->popst))//如果popst中没有数据,就把pushst中的数据导到popst中{while(!STEmpty(&obj->pushst)){STPush(&obj->popst, STTop(&obj->pushst));STPop(&obj->pushst);}}return STTop(&obj->popst);
}//队头出队列
int myQueuePop(MyQueue* obj) {int front = myQueuePeek(obj);//有数据,直接获取,没数据,先将pushst导入STPop(&obj->popst);return front;
}//判空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}//销毁队列
void myQueueFree(MyQueue* obj) {STDestroy(&obj->pushst);STDestroy(&obj->popst);free(obj);
}
题目四:设计循环队列
题目链接:622. 设计循环队列 - 力扣(LeetCode)
题目描述:
题目分析:
思路:创建数组,利用模运算使得操作控制在0~k元素范围内,并保证tail不放数据。
我们一般的队列是用链表实现的,但是对于循环队列,数组的方法可能更好实现,链表的方法并不比数组的方法简单多少。
//创建包含数组相关内容的匿名结构体
typedef struct {int* arr;int head;int tail;int k;
} MyCircularQueue;//初始化结构体中的各项数据
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* st = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));st->arr = (int*)malloc((k + 1) * sizeof(int));st->head = 0;st->tail = 0;st->k = k;return st;
}//如果head和tail位置相同,循环队列为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->head == obj->tail;
}//如果head在tail的下一个位置,循环队列为满
bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->head == (obj->tail + 1) % (obj->k + 1);
}//队尾入队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))return false;obj->arr[obj->tail] = value;obj->tail++;obj->tail %= (obj->k + 1);return true;
}//队头出队列
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return false;obj->head++;obj->head %= (obj->k + 1); return true;
}//取队头数据
int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;elsereturn obj->arr[obj->head];
}//取队尾数据
int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;elsereturn obj->tail == 0 ? obj->arr[obj->k] : obj->arr[obj->tail - 1];//return obj->arr[(obj->tail + obj->k) % (obj->k + 1)];
}//销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->arr);free(obj);
}
这道题锻炼并加深了对模运算的理解,对上面每个函数体中的模运算大家一定要理解清楚。那链表的方式为什么复杂呢?原因就是因为,在数组中我们取尾的前一个只要下标-1就可以了,最多再加上模运算,但是对于链表是不好找前一个尾节点的前一个节点的。当然有解决办法,如改用双向链表,或者记录tail的前一个节点prev,也可以重新遍历链表。但是这些解决方法无一不大大增加了代码的复杂度且降低了可读性,所以对于循环链表来说,数组的解决方法是更好的。
SUMUP结尾
数据结构就像数学题,需要刷题才能对它有感觉。之后还会更新数据结构相关的练习题、面试题,希望大家一起学习,共同进步~
如果大家觉得有帮助,麻烦大家点点赞,如果有错误的地方也欢迎大家指出~
相关文章:

LeetCode/NowCoder-栈和队列OJ练习
孜孜不倦:孜孜:勤勉,不懈怠。指工作或学习勤奋不知疲倦。💓💓💓 目录 说在前面 题目一:括号匹配问题 题目二:用队列实现栈 题目三:用栈实现队列 题目四:设…...
VSCODE终端输出中文乱码 菱形问号?
问题现象 VSCODE终端输出中文乱码 菱形问号? 解决方法 方法一 设置系统环境变量 变量名:PYTHONIOENCODING 值:utf8 方法二 安装插件Code Runner插件在设置中搜索 code-runner.executorMap,再点击在setting.json中编辑&#x…...
域名绑定ip和端口的方法是什么?
在互联网世界中,域名绑定IP和端口是实现网站精准访问的关键步骤。域名是用户访问网站的直观标识,而IP地址和端口号则指明了服务器的具体位置和通信接口。本文将详细介绍域名绑定IP和端口的过程。 域名与IP地址的关系 域名是互联网上网站的人类可读地址…...

视频监控平台AS1000:通过网络SDK接入松下视频监控设备(Panasonic监控摄像机) 的源代码的函数和功能介绍及分享
目录 一、视频监控平台介绍 1、概述 2、视频接入能力介绍 3、功能介绍 二、PANASONIC网络摄像机 1、产品种类与定位 2、规格参数 3、功能特点 4、环境适应性 5、网络功能 6、其他特性 三、代码和解释 1、代码和注释 2、函数功能说明 (1)处…...

GitLab项目中添加用户,并设置其角色权限等
注意:创建用户(new user),创建完用户然后再项目邀请用户,选择创建过的用户 一、以管理员身份登录GitLab的WebUI并创建用户 1>.使用管理员登录GitLab 使用管理员(root)用户登录成功后,点击如下图所示的小扳手,点击…...
asio之winsock的初始化
简介 asio中,winsock初始化工作是放在winsock_init类中来处理的 类结构 #mermaid-svg-aC4x3cdr8TKGhsnX {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aC4x3cdr8TKGhsnX .error-icon{fill:#552222;}#…...

打造智能化未来:智能运维系统架构解析与应用实践
在数字化转型的大背景下,智能运维系统成为了企业提升效率、降低成本、增强安全性的关键利器。本文将深入探讨智能运维系统的技术架构,介绍其核心要素和应用实践,帮助读者全面了解智能运维系统的概念、优势和应用价值。 ### 1. 智能运维系统的…...

【GeoServer系列】——安装与发布shapefile数据
GeoServer是一个基于java的服务器,它允许用户查看和编辑地理空间数据。使用OGC制定的开放标准,GeoServer在地图创建和数据共享方面具有极大的灵活性。 功能概述: Open and Share Your Spatial Data GeoServer允许您向世界显示您的空间信息。G…...

Rust 第三方库创建和导入(cargo --lib)
前言 日常开发过程中,难免会有一些工具方法,多个项目之间可能会重复使用。 所以将这些方法集成到一个第三方包中方便后期维护和管理, 比如工具函数如果需要修改,多个项目可能每个都需要改代码, 抽离到单独的包中只需要…...

node-sass和sass-loader安装Error经验
一、问题 当前笔记本环境版本:node-v16.15.1;npm-8.11.0,在面对五年前vue项目的依赖sass-loader8.0.2,node-sass4.14.1的情况下,怎么参考大神们的安装教程,始终存在Error,经过坚持不懈的努力&a…...

LabVIEW车体静强度试验台测控系统
LabVIEW车体静强度试验台测控系统 开发了一种基于LabVIEW的车体静强度试验台测控系统,通过自动化技术提高试验的精度和效率。系统采用LabVIEW软件与S7-200 SMART PLC硬件平台相结合,实现了对液压缸作用力的精确控制和试验数据的实时采集及管理。 传统的…...

软件测试进阶
目录 一、自动化测试 1.概念 2.Selenium 2.1 概念 2.1.1 Selenium是什么? 2.1.2 Selenium特点 2.1.3 工作原理 2.2 SeleniumJava环境搭配 2.3 定位元素 2.3.1 CSS语法 2.3.2 XPath语法 2.4 应用 2.4.1 点击提交文本 2.4.2 模拟输入 2.4.3 清除文本 2…...
将字符串 “()“ ““ “|“ 条件组成的复杂表达式转换为ES查询语句
应用场景 "()" "&" "|" 这几个条件对于我们来说并不陌生, 其表达的逻辑非常明了, 又能通过很少的字符表达很复杂的嵌套关系, 在一些复杂的查询中会经常用到, 因此我最近也遇到了类似的问题,一开始觉得这类的工具应该挺常见的, 结果搜了半天…...
2024上半年软考 考试心得
考试的时候感觉选择题有点偏,很多概念题都不知道是什么,好像没怎么见过,什么拖库洗库,linux权限号不会,python也不确定,但也算顺利;下午题的数据库竟然没考主键外键,我的天哪&#x…...
【redis】set和zset常用命令
set 无序集合类型 sadd 和 smembers SADD:将一个或者多个元素添加到set中。注意,重复的元素无法添加到set中。 语法:SADD key member [member] 把集合中的元素,叫做member,就像hash类型中,叫做field类似. 返回值表示本次操作,添加成功了几个元素. 时间复…...
【面试题-006】java中的垃圾回算法有哪些?
Java中的垃圾回收(Garbage Collection,简称GC)是指自动内存管理的一种机制,用于回收不再使用的对象占用的内存。Java中的垃圾回收算法主要有以下几种: 标记-清除(Mark-Sweep)算法: …...

最小时间差
首先可以想到,可以计算出任意两个时间之间的差值,然后比较出最小的,不过这种蛮力方法时间复杂度是O(n^2)。而先将时间列表排序,再计算相邻两个时间的差值,就只需要计算n个差值,而排序阶段时间复杂度通常为O…...

动态SQL IF语句
IF语句学习 第一种写法(标准) 我们先来看以下标准写法: select * from .. <where> <if test""> and ....... <if test""> and ....... <where> 我们用了一个where标签 , 内嵌if语句 第二种写法: 这是第二种写法:不用where标…...

961题库 北航计算机 操作系统 附答案 选择题形式
有题目和答案,没有解析,不懂的题问大模型即可,无偿分享。 第1组 习题 计算机系统的组成包括( ) A、程序和数据 B、处理器和内存 C、计算机硬件和计算机软件 D、处理器、存储器和外围设备 财务软件是一种ÿ…...
SylixOS 版本与 RealEvo-IDE 版本对应关系说明
SylixOS 版本与 RealEvo-IDE 版本对应关系说明 SylixOS 版本IDE 版本发布日期1.4.13.1.52017/01/171.5.23.5.12017/10/121.7.13.8.32018/06/221.8.33.9.52018/10/081.9.9-103.9.102020/01/021.11.63.10.22020/05/131.11.73.10.x2020/06/121.12.93.11.02020/09/111.12.11&#…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...