leetcode解题 - #用栈实现队列 #用队列实现栈 #循环队列

利用栈实现队列功能 栈的功能的总结: 递归的实现是基于栈的 四则运算表达式 从中缀表达式转为后缀表达式(系统内部实现)。(1)借助辅助栈。(2)遇到数字直接打印。(3)遇到符号,将当前符号与栈顶元素进行优先级比较,如果当前元素优先级高直接入栈,若当前元素优先级低则将栈内元素出栈,直到比栈内元素高为止。(4)遇到左括号无条件入栈,遇右括号栈内依次出栈直到左括号停止。 从后缀转为中缀表达式。(1)借助辅助栈。(2)遇到数字入栈。(3)遇符号将栈顶元素下一个和栈顶元素构成表达式。 使用两个栈完成队列的功能:一个作为入栈,一个 阅读详情

文章目录

前言

一、用栈实现队列

1、分析

2、代码如下:

二、用队列实现栈

1、分析:

三、循环队列

1、分析

2、代码如下:

总结


前言

路漫漫其修远兮,吾将上下而求索;


一、用栈实现队列

1、分析

我们先简单回顾一下栈和队列的基础知识;

栈,是一个数据在一端进、出的数据结构,满足后进先出原则;入栈出栈均在栈顶,如下图所示:

由于栈的结构的特殊性,利用数组实现(也可以使用单链表实现,头结点当作栈顶,但是由于数组的CPU高速缓存命中率更高(相较于单链表),故而用数组实现栈更好);

队列,是一个在固定一端进数据,固定一端出数据的数据结构,满足先进先出的原则;入队列在队头,出队列在队尾,如下图所示:

队列用单链表来实现(用数组实现势必会出现挪动数据的情况,效率低下;队列可以用单链表实现也可以用双链表实现,但是能用单链表实现就用单链表实现,能节约一点空间就节约一点空间);

所要实现的接口函数如下:

用栈实现队列,即用一个一端进出的结构来实现一个固定一端进固定一端出的结构;假设这两个栈分别为st1 与 st2 ,如下图所示:

如果想要将数据1 2 3 ,push到栈(后进先出)中,那么取出来的数据也一定是栈顶的元素3;但对于队列(先进先出)而言,pop 的数据应该是 1;对于栈,应该将栈st1 中的数据依次(从栈顶开始)push 进 st2 中,然后pop st2 中的数据(st2 中栈顶的数据)便是队列所要出数据的顺序;

也就是说将数据放入另外一个栈中该表该数据的顺序,在此栈中pop 数据的顺序才是队列中pop 数据的顺序;

在两栈中一个有数据一个没有数据的情况下(例如上述的例子中),插入数据的时候应该选择哪一个呢?

倘若在push 1 2 3 之后再pop 一次,然后再push 4 5 进入,再将数据全部pop 出,那么“队列”pop数据的顺序为1 2 3 4 5;

经过上图的分析,push 4 5 的时候肯定是不会放在有数据的栈(准确来说是“翻转”过数据的栈中)中的(因为会打乱数据pop的顺序),显然我们可以将栈分功能命名,假设st1 为pushst , st2 为 popst

于是在push 、pop的便有了一下的逻辑:

  • 1、push 数据时,将数据放在pushst 中
  • 2、pop数据时,先看popst 中是否有数据,popst 中有数据的话先pop它里面的数据;如果popst 为空,但还要pop 数据,便可以将pushst 中的数据(从栈顶开始)“翻转”放到popst 中(注:由于题干中已经说明了不会传空,故而此处没有考虑pushst 中没有数据的情况)

显然,获取队列的队头中的数据过程:

  • 1、先看popst 中为不为空,popst 不为空的话,那么该“队列”的队头的数据为popst 的栈顶中的数据
  • 2、如果popst 中不为空,那么该“队列”的队头的数据为pushst 中栈底的数据,由于实现栈的时候并没有得到栈底数据的接口函数,故而此处需要转换;既然popst 中为空,完全可以将pushst 中的数据(从栈顶开始)push 放入popst 中,然后popst 栈顶中的数据便为该“队列”的队头的数据;

2、代码如下:

//栈的类型的声明 - 栈用数组实现
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top ;
    int capacity;

}ST;

//初始化
void StackInit(ST* pst)
{
    assert(pst);
    pst->a = NULL;
    pst->top = pst->capacity=0;
}

//入栈
void StackPush(ST* pst, STDataType x)
{
    assert(pst);
    //确保空间足够
    if(pst->top == pst->capacity)
    {
        int newcapacity = pst->capacity==0 ? 4: 2*pst->capacity;
        STDataType* tmp = (STDataType *)realloc(pst->a , newcapacity* sizeof(STDataType));
        if(tmp==NULL)
        {
            perror("StackPush realloc");
            exit(-1);
        }
        pst->a = tmp;
        pst->capacity = newcapacity;
    }
    //放置、处理细节
    pst->a[pst->top++] = x;

}

//出栈
void StackPop(ST* pst)
{
    assert(pst);
    assert(pst->top);
    pst->top--;
}

//获取栈顶的数据
STDataType StackTop(ST * pst)
{
    assert(pst);
    assert(pst->top);
    return pst->a[pst->top-1];
}

//判空
bool StackEmpty(ST* pst)
{
    assert(pst);
    return pst->top == 0;
}

//获得栈中数据的个数
int StackSize(ST* pst)
{
    assert(pst);
    return pst->top;
}

//销毁
void StackDestroy(ST* pst)
{
    //释放所有动态开辟的空间
    free(pst->a);
    pst->a = NULL;
    pst->top = pst->capacity = 0;
}


//两个栈实现队列
typedef struct {
    ST pushst;
    ST popst;
    
} MyQueue;

//初始化
MyQueue* myQueueCreate() 
{
    MyQueue * obj = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&(obj->pushst));
    StackInit(&(obj->popst));

    return obj;
    
}
//push
void myQueuePush(MyQueue* obj, int x) 
{
    StackPush(&(obj->pushst),x);
}
//pop
int myQueuePop(MyQueue* obj)
{
    //先判断popst 中有没有数据,为空的话需要将pushst 中的数据push 放入popst 中
    if(StackEmpty(&(obj->popst)))
    {
        while(!StackEmpty(&(obj->pushst)))
        {
            StackPush(&(obj->popst),StackTop(&(obj->pushst)));
            StackPop(&(obj->pushst));
        }
    }
    int top = StackTop(&(obj->popst));
    StackPop(&(obj->popst));
    return top;
    
}

//获取“队头”的数据
int myQueuePeek(MyQueue* obj)
{
    //先判断popst 中有没有数据,为空的话需要将pushst 中的数据push 放入popst 中
    if(StackEmpty(&(obj->popst)))
    {
        while(!StackEmpty(&(obj->pushst)))
        {
            StackPush(&(obj->popst),StackTop(&(obj->pushst)));
            StackPop(&(obj->pushst));
        }
    }
    int top = StackTop(&(obj->popst));
    return top;    
}
//判空
bool myQueueEmpty(MyQueue* obj) 
{
    return (StackEmpty(&(obj->pushst)) && StackEmpty(&(obj->popst)));
}
//销毁
void myQueueFree(MyQueue* obj)
{
    //销毁所有动态开辟的空间
    StackDestroy(&(obj->pushst));
    StackDestroy(&(obj->popst));

    free(obj);
    obj = NULL;
}

二、用队列实现栈

1、分析:

用队列实现栈,即让先进先出的结构变为后进先出的结构;

我们先来看一下所要实现的接口函数有哪些:

直接来分析:

当要push 1 2 3 的时候,栈中pop数据的顺序为: 3  2  1 ,而队列中pop 数据的顺序为 1 2 3 ,如下图:

如上图所示,想要让队列中的数据pop 的顺序与栈中的数据一致就得让队列中队尾前的数据全部push 到另外一个队列中;由于队列先进先出的特点,数据在队列之间倒来倒去并不会改变数据的顺序,故而每次pop均会进行上述的操作;

如果又要push 数据应该放在哪一个队列中呢?

  • 显然,push 的数据放在已经有数据的队列中是比较合适的,倘若两队列均没有数据,任意选择一个放入即可;

由于队列结构的特性,多次挪动数据并不会改变数据的顺序,于是乎想要满足栈的pop 就得pop 队列的队尾,即将队尾前面的数据均push 到另外一个队列中,然后再将原本队尾的数据走到该队列的队头的位置,最后再pop ;如此便可;

获取栈顶的数据:栈顶的数据相当于队列中队尾的数据,先找到不为空的队列直接获取其队尾的数据即可;

2、代码如下:

//队列 - 用单链表实现
//队列的结点的类型声明
typedef int QDataType;
typedef struct QueueNode
{
    QDataType data;
    struct QueueNode* next;

}QNode;

//维护队列的变量
typedef struct Queue
{
    QNode* phead;
    QNode* ptail;
    int size;
}Queue;

//初始化
void QueueInit(Queue* pq)
{
    assert(pq);
    pq->phead = pq->ptail = NULL;
    pq->size =0;
}

//销毁
void QueueDestroy(Queue* pq)
{
    //释放所有动态开辟的空间 - 结点
    QNode* pcur = pq->phead;
    while(pcur)
    {
        QNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    //置空、置为初始值
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}

//入队列
void QueuePush(Queue* pq , QDataType x)
{
    assert(pq);
    //开辟结点的空间
    QNode* newnode = (QNode* )malloc(sizeof(QNode));
    if(newnode==NULL)
    {
        perror("QueuePush malloc");
        exit(-1);
    }
    newnode->data = x;
    newnode->next =NULL;
    //相当于单链表的尾插,需要分情况讨论
    if(pq->phead==NULL)
    {
        pq->phead = pq->ptail = newnode;
    }
    else
    {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
    pq->size++;
}

//出队列
void QueuePop(Queue* pq)
{
    assert(pq);
    assert(pq->phead);
    //由于此处多定义了一个变量,于是也要分情况讨论
    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--;
}

//获取队头的数据
QDataType QueueFront( Queue* pq)
{
    assert(pq && pq->phead);
    return pq->phead->data;
}

//获取队尾的数据
QDataType QueueBack(Queue* pq)
{
    assert(pq && pq->ptail);
    return pq->ptail->data;
}

//队列中数据的个数
int QueueSize(Queue* pq)
{
    assert(pq);
    return pq->size;
}

//判空
bool QueueEmpty(Queue* pq)
{
    assert(pq);
    return pq->size == 0;
}

//两个队列实现栈
typedef struct {
    Queue q1;
    Queue q2;
    
} MyStack;

//初始化
MyStack* myStackCreate() 
{
    MyStack * obj = (MyStack* )malloc(sizeof(MyStack));
    QueueInit(&(obj->q1));
    QueueInit(&(obj->q2));

    return obj;
}

//入栈
void myStackPush(MyStack* obj, int x)
{
    //找有数据的队列,均为空的话,任意选一个队列放置数据
    if(QueueEmpty(&(obj->q1)))
    {
        QueuePush(&(obj->q2) , x);
    }
    else
    {
        QueuePush(&(obj->q1) , x);
    }
    
}

//出栈
int myStackPop(MyStack* obj) 
{
    //将有数据的队列中的前size-1 个数据push 到另外一个队列中
    //然后在pop 原来哪个队尾的数据
    //假设法
    QNode* empty = &(obj->q1);
    QNode* nonempty = &(obj->q2);
    if(QueueEmpty(&(obj->q2)))
    {
        empty = &(obj->q2);
        nonempty = &(obj->q1);
    }
    while(QueueSize(nonempty)>1)
    {
        //push
        QueuePush(empty , QueueFront(nonempty));
        //pop
        QueuePop(nonempty);
    }
    int top = QueueFront(nonempty);
    QueuePop(nonempty);

    return top;
}

//获取栈顶的数据
int myStackTop(MyStack* obj) 
{
     QNode* empty = &(obj->q1);
    QNode* nonempty = &(obj->q2);
    if(QueueEmpty(&(obj->q2)))
    {
        empty = &(obj -> q2);
        nonempty = &(obj->q1);
    }
    
    return QueueBack(nonempty);
    
}

//判空
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; 
}

三、循环队列

1、分析

对于此题,我们首先需要思考,该循环队列用什么实现合适;

我们先来思考能否用链表实现;必然是会用到循环链表(仅使用单链表实现循环队列是非常麻烦的,因为单链表中的结点(假设该结点存在上一结点与下一结点)只能找到下一结点,找该结点的上一结点是非常麻烦的;当然,可以使用双链表来实现,但是此处还是先考虑"简单、节约空间"的结构是否能解决此问题),循环链表中的尾结点中的next 指向头结点;

(假设用单向循环链表来实现会使用到两个变量phead 与 ptail 分别指向循环队列中的头和尾)

假设题干要求循环队列有 k 个空间,那么我们需要创建多少个结点?

如果就创建k个结点,队列空与满的时候,phead 与 ptail 均会指向同一块空间,不好区分;于是此处有两种方法来解决这个问题:

  • 1、方法一:再设置一个变量size 记录队列中数据的个数;
  • 那么当 phead == ptail 并且 size 为0 的时候,该队列为空;
  • 当phead == ptail 而 size 等于 k 时,该队列为满;

  • 2、方法二:多开辟一个结点的空间;
  • 当phead == ptail 的时候,该队列为空;
  • 当 ptail->next == phead 的时候,该队列为满;

假设此处采用的方法二,多开辟一个空间的形式;

  • 那么队列为空,当phead == ptail 时;
  • 队列为满,当 ptail->next = phead 时;
  • “入队列”(尾插):先判断队列是否还有空间可以存下数据,如果队列不为空,便可以将数据存放在ptail 所指向的结点中,然后ptail 往后走;

  • “出队列”(头删):先判断该队列中是否有数据可以出,如果有数据可以出,那么直接让phead 指向下一个结点便可,无需处理空间中的数据;

  • 获取队列中队头的数据,就是指针phead 指向的空间中的数据;

  • 获取队列中队尾的数据,为ptail 所指向结点的前一个结点中的数据;单链表结构存在的缺陷:不能以时间复杂度为O(1)的方式找到该结点的前一个结点,可以利用循环时间复杂度为O(N);

想要处理找ptail 前一个结点的问题也不难,

  • 方式一:利用循环遍历查找,时间复杂度位O(N),效率不高
  • 方式二:在每个指针中再增加一个变量用来指向前一个结点,即单向循环链表变成双向循环链表;

我们更倾向于用空间换时间,方式二可以待选,在此之前我们再来分析一下数组是否可以实现循环队列;

如何让数组体现循环?

  • 同样此处需要两个“指针” head 与 tail 分别记录“头”的下标与“尾下一空间”的下标;那么循环就得在下标上动手;

同样的,如果题干要求循环队列有 k 个存放数据的空间

那么我们需要创建多少个空间来存放数据?

1、方法一:再设置一个变量size 记录队列中数据的个数;

  • 那么当 head == tail 并且 size 为0 的时候,该队列为空;
  • 当 head == tail  而 size 等于 k 时,该队列为满;

2、方法二:多开辟一个空间(即总空间有 k+1 个);

  • 当head == tail 的时候,该队列为空;
  • 当 (tail + 1)%(k+1) == head 的时候,该队列为满;

注:此处需要考虑“回绕”的问题;

为何多开辟一块空间之后,判满的条件为:(tail + 1)% (k+1) == head ?

  • 因为多开辟出来了一块空间,那么当该队列满的时候,tail 会指向一块空的空间(tail 指向尾数据的下一个空间),而tail 所指向的这块空间的下一空间为 head 所指向的空间,即 tail + 1 = head ,但是由于存在”回绕“的问题,即 tail 可能指向该数组最后一个空间上,那么 tail + 1 便存在越界的风险,如果该队列为满,那么head 一定指向下标为0 的空间,故而判满条件为:(tail + 1)% (k+1) == head;

当然,还有一种理解方式,tail 永远指向尾数据的下一空间,故而多开辟一块空间,可以作为tail 的缓冲空间进行判断;当tail 走在所开辟空间的最后一个空间的时候,如果tail 还要往前走是要回到下标为0的空间处的,而数组的下标是从0开始的,即有效数据有k 个,会开辟k+1 个空间,那么最后一个空间的下标为 k ,想要让指向下标为k 的tail 向前走一步又回到下标为0的空间去,显然tail = (tail+1)%(k+1); 思考判满也同理,tail(tail 此时指向的空间中没有数据,即尾数据的下一个空间) 先前走一个空间如果遇到head ,那便说明该队列满了,由于tail 存在回绕的情况,故而需要对tail 进行处理,即 (tail+1)%(k+1) == head; 则说明满了;

假设此处也是采用方式二来解决问题:

  • 那么判断队列为空:head == tail ;
  • 判断队列满: (tail + 1)% (k+1) == head ;
  • “入队列”(尾插):先判断队列是否还有空间可以存下数据,如果队列不为空,便可以将数据存放在tail 所指向的空间中,然后tail 往后走(此处需要解决tail回绕的问题, 即 tail = (tail+1)%(k+1));

  • “出队列”(头删):先判断该队列中是否有数据可以出,如果有数据可以出,那么直接让head 指向下一个空间便可(此处也需要解决head回绕的问题, 即 head = (head+1)%(k+1)),无需处理空间中的数据;

  • 获取队列中队头的数据,首先判断“队列中是否有数据”,队尾数据就是指针head 指向的空间中的数据;
  • 获取队列中队尾的数据,首先判断“队列中是否有数据”,队头数据为tail 所指向结点的前一个空间中的数据(此处需要解决tail 回绕的问题 , 即 [(tail-1) + (k+1)]%(k+1) 为tail 指向空间的前一个空间的下标);

相较而言,此处可以使用数组来解决循环队列的问题,故而该题底层便使用数组来解决;

2、代码如下:



//循环队列使用数组实现更好
typedef struct {
    int * a;
    int head;
    int tail;
    int k;
    
} MyCircularQueue;

//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->head == obj->tail;
    
}

//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return (obj->tail+1)%(obj->k+1)==obj->head;
}


MyCircularQueue* myCircularQueueCreate(int k) {
    //为循环队列这个封装的结构体开辟空间
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //为循环队列中底层存储数据的数组开辟空间
    obj->a = (int*)malloc(sizeof(int)*(k+1));
    //初始化
    obj->head = obj->tail = 0;
    obj->k = k;

    return obj;
}

//入队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //判断空间是否足够
    if(myCircularQueueIsFull(obj))
    return false;

    //将数据放在tail 所指向的空间中
    obj->a[obj->tail]=value;
    //tail 向后走,并且解决回绕的问题
    obj->tail = (obj->tail+1)%(obj->k+1);
    return true;
    
}
//出队列
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //判空
    if(myCircularQueueIsEmpty(obj))
    return false;

    //head 向前走并且处理回绕的问题
    obj->head = (obj->head+1)%(obj->k+1);
    return true;
    
}
//获取队头的数据
int myCircularQueueFront(MyCircularQueue* obj) 
{
    //判空
    if(myCircularQueueIsEmpty(obj))
    return -1;

    return obj->a[obj->head];
    
}

//获取队尾的数据
int myCircularQueueRear(MyCircularQueue* obj) 
{
    //判空
    if(myCircularQueueIsEmpty(obj))
    return -1;
    
    return obj->a[(obj->tail-1+obj->k+1)%(obj->k+1)];
}

//销毁
void myCircularQueueFree(MyCircularQueue* obj) 
{
    //释放所有动态开辟的空间
    free(obj->a);
    free(obj);
}

注意:

1、在leetcode 中接口函数的实现其底层可能并没有对函数进行声明,如果所要实现的接口函数会调用下面将要实现的接口函数,要将所要被调用的接口函数放在前面,因为函数的实现相当于一次特殊的函数声明;

2、在leetcode 中使用动态开辟空间的函数(malloc、calloc、realloc)可以不进行判空操作,大概率均不会开辟空间失败,故而此处没有进行判空的处理,加上判空的处理也完全没有问题;


总结

三道leetcode 的题目链接(按照上述的分析顺序):

OJ链接icon-default.png?t=O83Ahttps://leetcode.cn/problems/implement-queue-using-stacks/OJ链接icon-default.png?t=O83Ahttps://leetcode.cn/problems/implement-stack-using-queues/OJ链接icon-default.png?t=O83Ahttps://leetcode.cn/problems/design-circular-queue/

栈与队列 10 大经典面试题解析:从递归调用到循环队列实现 本文深度解析栈与队列的10大经典面试题,涵盖递归调用、循环队列实现等核心知识点。通过对比顺序栈与链栈、讲解循环队列技巧及单调栈应用,帮助读者掌握数据结构基础与高级应用,提升面试解题能力与代码实现水平。 阅读详情

相关推荐

python数据结构实现(二):栈、队列和递归及相关LeetCode题

Python数据结构实现(二)一:栈1. 用数组实现一个顺序栈2. 用链表实现一个链式栈3.编程模拟实现一个浏览器的前进、后退功能4. leetcode相关习题(1). Valid Parentheses(有效的括号)(2). Longest Valid Parentheses(最长有效的括号)(3). Evaluate Reverse Polish Notatio(逆波兰表达式求值)二:队列1....

Andrewings 552

【数据结构】用栈实现队列

本篇总结利用栈如何实现队列的相关操作,不难观察,栈和队列是可以相互转化的,需要好好总结它们的特性,构造出一个恰当的结构来实现即可,所以本篇难点不在代码思维,而是对结构的理解。

Extreme_wei的博客 4351

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

zhulewen的博客 7884

leetcode:232. 用栈实现队列

详解leetcode:232. 用栈实现队列

2301_76197086的博客 308

功率开关管驱动串扰(Crosstalk):深度解析与优化方案

介绍了功率开关器件驱动串扰产生机理,并整理了常见的工程应用优化措施

qq_38326420的博客 559

基于机器学习和辐射传输的地表温度模拟方法对比

对于不同的地表类型和不同的年份,经验方法是实用的,虽然整体精度不如物理方法,但具有计算速度快、对模型内部结构考虑较少等优点。尽管深度学习等回归的经验方法不需要考虑辐射传输和能量平衡的具体过程, 但在其输入参数的选择上也不能是无序和随机的,而是应选择有关相应物理过程的参数。经验方法在处理不同年份相同地表类型或表现相对较好, RMSE 约为 1.50K,但在不同地表类型且不同年份模拟结果较差, RMSE 约为 3.0K, 虽然精度不如物理方法,但它具有计算速度快,不需过多考虑模型内部结构的优点。

2301_77907355的博客 1022

Python用栈实现队列

1 问题如何用栈实现队列。2 方法(1)void push(int x)将元素 x 推到队列的末尾(2)int pop()从队列的开头移除并返回元素(3)int peek()返回队列开头的元素(4)boolean empty()如果队列为空,返回true;否则,返回false代码清单 1class MyQueue: def __init__(self): """ ...

算法与编程之美 450

如何实现Docker镜像导入导出 ?

摘要: Docker镜像迁移与备份可通过两种方式实现:1)镜像操作(save/load)完整保存分层结构和元数据,适用于完整备份;2)容器操作(export/import)仅保存文件系统快照,适合创建轻量模板。关键区别在于前者保留历史记录和分层,后者生成单层镜像。save/load命令示例:docker save -o nginx.tar nginx:latest,export/import示例:docker export -o container.tar abc123 && docker

weixin_39291964的博客 875

Python--栈与队列的相互实现

其实都是重点是增加值的过程中需要改变.其他的都还好.

qq_34786604的博客 766

马达种类全解析:VCM、Stepper、Piezo 在手机相机中的驱动原理与工程差异

在现代手机影像系统中,马达承担着核心成像组件的动态调节任务,如自动对焦(AF)、光学防抖(OIS)以及变焦调节。目前主流采用三类驱动器件:VCM(Voice Coil Motor)、Stepper(步进马达)、Piezo(压电马达)。不同马达方案在结构复杂度、响应速度、功耗、驱动方式与模组厚度适配等方面差异显著,对成像系统的性能表现有直接影响。本文将结合最新旗舰设备的应用现状,系统剖析这三类驱动马达的工作原理、架构特点与工程应用经验,为工程实践中选型与调试提供参考。

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 3783

【Windows Server 2019】企业虚拟专用网络服务的配置和管理(上)

博文介绍了部署企业虚拟专用网的定义,目的,以及好处。同时说明了实验拓扑图以及虚拟专用网络的工作原理。

VMware, Windows, Linux, Cisco, Container, Kubernetes, Kali 1万+

Leetcode 622. 设计循环队列

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。Python代码 – 单链表法。Python代码 – 数组法。C++代码 – 单链表法。数组法 / 单链表法。

m0_51437455的博客 952

YOLO11 改进系列 | 引入2023年 MLCA 的 C3k2_MLCA 模块,近零参数开销的本地-全局通道注意力,适合检测与分割特征增强

本文提出将MLCA(混合局部通道注意力)模块嵌入YOLOv11的C3k2框架中,形成C3k2_MLCA改进结构。该模块通过5×5局部池化和1×1全局池化双路融合,结合ECA式Conv1d生成通道注意力,在几乎不增加参数量的前提下(仅增加66个参数),实现了局部空间信息与全局通道统计的联合建模。相比传统SE和ECA注意力,MLCA保留了空间分布信息,更适合实例分割等需要精细定位的任务。实验表明改进后模型参数量仅增加0.002%,计算量基本不变,是一种高效的注意力增强方案。

qq_44224801的博客 93

【数据结构#4】栈和队列?看这个就够了

这篇博客文章详细讲解了栈和队列这两种基础数据结构及其实际应用。文章首先介绍了栈的基本概念(FILO/LIFO特性)、结构操作(压栈、出栈)以及实现方式(推荐使用数组)。随后通过经典算法题(如括号匹配、表达式求值)展示栈的应用,重点讲解了中缀、前缀、后缀表达式的转换和求值方法,包括双栈法和调度场算法。 第二部分讲解了队列(FIFO特性)的实现方式(链表和数组),比较了各自的优缺点,并提供了基于链表的队列实现代码。最后通过算法题展示了如何用栈实现队列、用队列实现栈,以及循环队列的设计方法。 文章强调数据结构核心

ApolloMonasa的博客 812

Unreal Engine开发:多人联机与网络同步_延迟补偿与预测

通过延迟补偿和预测技术,我们可以显著减少网络延迟对多人在线动作游戏的影响,提升玩家的体验。延迟补偿主要在服务器端实现,通过回溯时间点来更准确地处理输入和事件。预测技术主要在客户端实现,通过预先计算玩家的动作结果来减少延迟感。结合这两种技术,可以实现更平滑、更公平的多人游戏体验。

chenlz2007的博客 1191

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

栈和队列(一) ------基本概念,循环队列

栈和队列基本概念,循环队列,力扣622,力扣20

Dexter的博客 1321

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

LeetCode(622):设计循环队列(Java)

2019.11.3 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新) github:https://github.com/ChopinXBP/LeetCode-Babel 数据结构题,应知应会。 传送门:设计循环队列 Design your implementation of the circular queue. The circular queue is a line...

NJU_ChopinXBP的博客 2199

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

力扣(LeetCode)数据结构——循环队列

力扣(LeetCode)数据结构——循环队列 耗时短的(76ms): class MyCircularQueue: def __init__(self, k: int): """ Initialize your data structure here. Set the size of the queue to be k. """ ...

chentao326的博客 408

LeetCode 232.用栈实现队列 LeetCode 225. 用队列实现栈 LeetCode 20. 有效的括号 LeetCode 1047. 删除字符串中的所有相邻重复项

思路:如果遇到对应的左括号,我们则将其对应的右括号push到栈中,随后根据这个顺序,来继续判断后续右括号是否以相反的顺序进行匹配——晚入的左括号需要先与右括号进行匹配,如①中遍历完左括号好,栈(左边是栈底)的顺序为 [ ‘)’, ‘]’, ‘}’ ],那后续字符串的右括号顺序应为[ ‘}’, ']', ')']。针对情况②,在遍历左括号后,栈为 [ ']', '}', ')'],但遇到右括号 '}' 时,此时栈顶元素为 ')',与当前右括号 '}' 不相等,则证明在配对的左右括号内存在未配对的左右括号。

weixin_59234335的博客 1018

数据结构——3道栈和队列OJ题

栈和队列3道OJ题

weixin_49449676的博客 391

LeetCode-Python-622. 设计循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。 你的实现应该支持如下操作: MyCircul...

Keep Coding 2660

leetcode练习:设计循环队列, python实现

做这道题踩了几个坑。 首先是要注意好逻辑结构,什么时候是空的,什么时候是非空的,什么时候enqueue和dequeue时要移动指针,什么时候不需要。 其次是注意类内变量是要在__init__内定义,而不是在__init__外定义在__init__内初始化。 最后是题目虽然说操作数在1-1000中间,但测试样例中有输入为0的情况,因此空位不能设置为0,最好设置为-1,这样front()和rea...

yzl14的博客 535

LeetCode|622.设计循环队列

LeetCode习题答案汇总 题目: 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。 你的实现应该支持如下操作: MyCircularQueue(k): 构造器,设置队列长度为 k 。 Front: 从队首

小黄的博客 427

栈与队列面试题解析:数据结构特性与力扣实战

栈与队列作为基础数据结构,在算法面试中占据重要地位。栈遵循LIFO(后进先出)原则,适合处理括号匹配、函数调用等场景;队列采用FIFO(先进先出)机制,常用于任务调度、BFS等场景。理解其底层原理和特性是解题关键,如单调栈能高效解决元素查找问题,优先队列基于堆实现适合TopK场景。在力扣(LeetCode)等编程平台中,这类题目常考察数据结构转换能力(如用栈实现队列)和组合应用技巧(如滑动窗口结合双端队列)。掌握这些核心概念和模板代码,能有效提升面试解题效率。

weixin_33910385的博客 379

数据结构 栈和队列

系列文章目录 文章目录系列文章目录一、栈1.1概念1.2栈在现实生活中应用ctrl+z撤销操作执行方法二、栈的实现2.1顺序栈2.1压栈(入栈)2.2出栈。2.3查看栈顶元素三、栈的常见问题3.1 leetcode20号问题 一、栈 1.1概念 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 1.2栈在现实生活中应用 ctrl+z撤销操作 此时c

ttthe_shy_的博客 805
上一篇: 数据结构与算法 - 树 #数的概念 #二叉树 #堆 - 堆的实现/堆排序/TOP-K问题
下一篇: 数据结构与算法 - 树-堆 相关算法的时间复杂度分析 #向上调整 #向下调整 #向上调整建堆 #向下调整建堆 #堆排序
秦少游在淮海
博客等级 码龄3年 1071粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值