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

数据结构(栈和队列的实现)

1. 栈(Stack)

1.1 栈的概念与结构

  • 栈是一种特殊的线性表,其只允许固定的一段插入和删除操作;进行数据插入和删除的一段叫做栈顶,另一端叫栈底;栈中的元素符合后进先出LIFO(Last In First Out)的原则。
  • 入栈:入栈就是往栈里放入数据;
  • 出栈:出栈就是从栈里拿出数据;(注意这两个操作都是对栈顶的数据进行操作);

2be032cadfd94a079a2bf58337718e8d.png

  • 栈底层结构选型:栈的实现选用的是数组;选用链表也可以,但在插入和删除操作更方便一些;因为链表尾插需要遍历找到尾结点,那么尾插的操作时间复杂度就为O(N),不过我们可以优化一下,拿链表的头部作为栈顶,插入和删除就为O(1)了,但是由于我们可能还需要创建多个指针进行操作,所以最优还是选择数组,因为数据的尾插和尾删时间复杂度都为O(1),而且找到尾部的位置不需要创建什么指针啥的,因为顺序表里本身就有一个size代表数组的大小,而最后一个位置则是下标为size-1的位置;

1.2 栈的实现

       --定义栈的模型

        由于栈底层可以使用顺序表实现,那就和之前顺序表一样,需要一个计算总空间有多大的capacity和一个储存有效元素个数的top(在顺序表其实就是size,但这里为了符合栈所以用top),还有一个储存整型类型的数组,但后面会出现增容的操作所以使用指针,STDateType* arr;如下代码所示:

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDatetype;typedef struct Stack
{STDatetype* arr;int capacity;  //栈空间大小int top;       //栈顶
}ST;

        --栈的初始化和销毁

void STInit(ST* st)
{assert(st);st->arr = NULL;st->capacity = st->top = 0;
}void STDestory(ST* st)
{assert(st);if(st->arr)free(st->arr);st->arr = NULL;st->capacity = st->top = 0;
}

        --入栈操作的实现

        入栈的实现:首先我们要判空间是否足够,而判断空间是否足够的条件就是栈顶是不是等于整个数组空间的容量,如果等于就需要使用realloc进行扩容,那么我们就需要创建一个Newcapacity作为扩容的空间,我们按2倍进行扩容则:2* capacity, 但是如果说capacity为0的话那么就无法进行扩容,因为0 * 任何数都等于0,所以我们写一个三目运算符,如果说capacity等于0的话就让Newcapacity等于4,否则等于2* capacity;后面我们就需要创建一个新的指针指向扩容的空间,因为如果说扩容失败的话就会对整块空间造成影响,所以我们需要创建一个新的空间进行扩容;而入栈的实现其实就是在数组最后面插入元素;代码实现如下:

void StackPush(ST* ps, STDatetype x)
{assert(ps);if (ps->capacity == ps->top  ){int Newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDatetype* temp = (STDatetype*)realloc(ps->arr, Newcapacity *sizeof(STDatetype) );if (temp == NULL){perror("realloc fail!");exit(1);}ps->arr = temp;ps->capacity = Newcapacity;}ps->arr[ps->top++] = x;
}

      

          --判断栈是否为空

        判空实现:其实就是看看栈顶是否为0,当栈顶都为0,那肯定就是没有数据了;代码实现如下:

bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}

        --出栈操作的实现

        出栈的实现:实际上其实就是顺序表进行尾删操作,那我们就直接让数组内的有效元素减少一个就可以,即size--;但是我们还需要判断栈是否为空,如果为空那就没有元素可以出;代码如下:

void StackPop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));--ps->top;
}

        --取栈顶元素

        取栈顶元素:取栈顶元素则是直接返回数组的尾部元素即是:ps->arr[ps->top-1];为什么要-1?因为top是有效元素的个数,但下标的起始位置是0,所以要-1;如下代码所示:

//取栈顶元素
STDatetype StackTop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->arr[ps->top-1];
}

        --获得栈中有效元素的个数

        由上可知,有效元素的个数其实就是top,所以直接返回即可;如下代码所示:

//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}

1.3 栈的完整代码

Stack.h

#pragma once#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDatetype;typedef struct Stack
{STDatetype* arr;int capacity;  //栈空间大小int top;       //栈顶
}ST;//首先创建这些都是要实现初始化
void STInit(ST* st);
void STDestory(ST* st);//栈顶---入数据、出数据
void StackPush(ST* ps, STDatetype x);
void StackPop(ST* ps);//取栈顶元素
STDatetype StackTop(ST* ps);bool StackEmpty(ST* ps);//获取栈中有效元素个数
int STSize(ST* ps);

Stack.cpp

#include"Stack.h"void STInit(ST* st)
{assert(st);st->arr = NULL;st->capacity = st->top = 0;
}
void STDestory(ST* st)
{assert(st);if(st->arr)free(st->arr);st->arr = NULL;st->capacity = st->top = 0;
}void StackPush(ST* ps, STDatetype x)
{assert(ps);if (ps->capacity == ps->top  ){int Newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDatetype* temp = (STDatetype*)realloc(ps->arr, Newcapacity *sizeof(STDatetype) );if (temp == NULL){perror("realloc fail!");exit(1);}ps->arr = temp;ps->capacity = Newcapacity;}ps->arr[ps->top++] = x;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}void StackPop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));--ps->top;
}//取栈顶元素
STDatetype StackTop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->arr[ps->top-1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}

 

2. 队列(Queue)

2.1 队列的概念与结构

  • 概念:只允许在一端插入数据和从另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out);
  • 入队列:进行插入操作的一端称为队尾;
  • 出队列:进行删除操作的一端称为队头;3752428e35374bd0b144a601c3255fd0.png
  • 队列的底层结构选型:队列底层结构选的是链表,因为要进行入队列和出队列的操作,如果说用顺序表的话,在进行出队列的操作的时候删除了头部数据那就得让后面数据往前走,那时间复杂度就为O(N),所以我们使用链表;但使用链表的时候要注意,我们需要头结点出数据实现出队列操作,尾结点入数据实现入队列操作,不然的话我们要遍历到尾结点删除数据实现出队列 时间复杂度又为O(N)了;

2.2 队列的实现

        --定义队列的模型

        队列的底层实现就是用链表来实现,那么我们就要定义结点还有队列的模型;为什么要定义两个呢?因为我们还需要一个指向头结点和指向尾结点的指针,还有一个整型size记录队列内的数据;

#pragma once
#include<iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;typedef int QDataType;
//每个结点
typedef struct QueueNode
{QDataType data;QueueNode* next;
}QueueNode;//队列
typedef struct Queue
{QueueNode* phead;QueueNode* tail;int size;
}Queue;

        --队列的初始化和销毁

        队列的销毁其实就和链表的销毁一样,首先创建一个指向头结点下一个节点的next指针,然后把头结点销毁掉,在让头结点指针指向next指针,next指针再走向头结点的下一个指针,一直循环到头结点为NULL的时候结束‘;代码如下:

void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->tail = NULL;pq->size = 0;
}void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->tail = NULL;pq->size = 0;
}

        --入队列的实现

        入队列的实现:需要结合链表的buynode(动态申请一个结点空间)来实现,首先我们先申请一个newnode的空间,让其指向空,data = x;再就要判断队列是否为空,如果说队列为空的话那就说明队列里面没有结点,那就需要让phead和ptail指针指向该结点,如果有结点的话就让ptail->next指针指向newnode,再让ptail走到newnode的位置,最后还要记得size++,因为size是计算队列内有效元素的个数;如下图和代码所示:

队列为空:c37f4e64bb334c14b62a998189b9f5f7.gif

队列不为空:0e56d732009c4c888509b140233e66ba.gif

void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//说明链表为空if (pq->phead == NULL){pq->phead = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = pq->tail->next;}pq->size++;
}

        --队列判空操作

        队列判空其实就是看头结点和尾结点的指针是否都指向NULL,如果都指向NULL就为空;代码如下:

//队列判空
bool QueueEmpty(Queue* pq)
{return pq->phead == NULL && pq->tail == NULL;
}

        --出队列的操作

        出队列的操作本质上就是链表删除头结点;我们只需要定义一个Del指针指向当前的头结点pcur,再让当前的头结点走到头结点的下一个结点pcur = pcur->next,然后free掉Del结点即可;图和代码如下:7ddc014d159a4d6dbcd5ae0c1584edb9.gif

// 出队列,队头
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//只有一个结点if (pq->tail == pq->phead){free(pq->phead);pq->phead = NULL;pq->tail = NULL;}else{QueueNode* Del = pq->phead;pq->phead = pq->phead->next;free(Del);}--pq->size;
}

注意!!!我们还需要单独判断只有一个结点的时候,当ptail和phead都指向同一个空间的时候就代表队列中只有一个结点,那么这时候我们还进行phead = phead->next就是让phead走向一块未知的空间,进行非法访问;


        --取队头和队尾数据

        因为有指针指向队头和队尾,就不过多阐述了;注意一点就是我们要判断队列是否为空;代码如下所示:

//取队头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}

        --取队列有效元素个数

        因为我们一开始就已经创建了一个size 对队列的有效元素个数进行记录,所以直接返回size即可;如下代码所示:

//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

2.3 队列的完整代码

Queue.h

#pragma once
#include<iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;typedef int QDataType;
//每个结点
typedef struct QueueNode
{QDataType data;QueueNode* next;
}QueueNode;//队列
typedef struct Queue
{QueueNode* phead;QueueNode* tail;int size;
}Queue;void QueueInit(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
// 出队列,队头
void QueuePop(Queue* pq);//队列判空
bool QueueEmpty(Queue* pq);//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);//销毁队列
void QueueDestroy(Queue* pq);

Queue.cpp

#include"Queue.h"void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->tail = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//说明链表为空if (pq->phead == NULL){pq->phead = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = pq->tail->next;}pq->size++;
}//队列判空
bool QueueEmpty(Queue* pq)
{return pq->phead == NULL && pq->tail == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//只有一个结点if (pq->tail == pq->phead){free(pq->phead);pq->phead = NULL;pq->tail = NULL;}else{QueueNode* Del = pq->phead;pq->phead = pq->phead->next;free(Del);}--pq->size;
}//取队头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}
//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->tail = NULL;pq->size = 0;
}

3.栈和队列的OJ题

3.1 有效的括号

原题链接:20. 有效的括号 - 力扣(LeetCode)

b1bbee2919574999a3acc186aef1318e.png

思路:我们可以创建一个栈,先把开口向右的括号全部入栈里面,当栈不为空的时候取栈顶元素;这步的操作其实就是实现当有{【( 这三个括号的时候能直接取到(,然后再和左开口的括号进行比较,如果说取出的栈顶元素是“(”,并且数组的下一个元素是“)”的话就是匹配上了,然后pos++;当不符合的时候直接返回false;如果不返回false一直到循环结束的时候就返回true,因为这说明全部括号都匹配上了;如下图和代码所示:f4d7dd70d5814d8eb8423f96547662e9.gif

#include<stdbool.h>
typedef char STDateType;
typedef struct Stack
{STDateType* arr;int top ;int capacity;
}ST;void STInit(ST* st)
{st->arr=NULL;st->top = 0;st->capacity = 0;
}void STPush(ST* st, STDateType x)
{if(st->capacity==st->top){int Newcapacity = st->capacity==0?4:2*st->capacity;STDateType* temp = (STDateType*)realloc(st->arr, Newcapacity* sizeof(STDateType));if(temp==NULL){perror("realloc fails!");exit(1);}st->arr = temp;st->capacity = Newcapacity;}st->arr[st->top++] = x;
}bool STempty(ST* st)
{return st->top==0;
}STDateType StackTop(ST* st)
{return st->arr[st->top-1];
}void STPop(ST* st)
{assert(!STempty(st));--st->top;
}void STDestory(ST* st)
{free(st->arr);st->arr = NULL;st->capacity = 0;st->top = 0;
}bool isValid(char* s) 
{ST st;STInit(&st);char* ps = s;while(*ps!='\0'){if(*ps=='('||*ps=='{'||*ps=='['){STPush(&st, *ps);}else{if(STempty(&st)){return false;}char ch = StackTop(&st);if((*ps==')'&&ch=='(')||(*ps==']'&&ch=='[')||(*ps=='}'&&ch=='{')){STPop(&st);}else{STDestory(&st);return false;}}ps++;}bool ret = STempty(&st)==true;STDestory(&st);return ret;
}

这里还有几个需要注意的点是:

  1. 当栈为空的时候就代表没有开口向右的括号,那就没有意义再进行下去了,因为没有括号可以匹配;
  2. 在我们取栈顶元素比较完后要删除比较过的元素,因为取栈顶元素只是取值,并没有实现取并删;
  3. 到最后我们需要判断栈是否为空,栈不为空则说明多出了一个开口向右的括号例如"{ [ ( ) ]"这样是不符合的;

3.2 用队列实现栈

原题链接:225. 用队列实现栈 - 力扣(LeetCode)

2962997933e54fe2b04fb5d672f7fcf5.png

思路:首先我们先分析,队列是先进先出,栈是后进先出,那我们就先来使用1234进行模拟;如果把1234放到栈里面再取出来的话就是4321;8c7bb546caad4b6cba109aa25e5aee7b.png

那么如果说我们想用两个队列来实现的话就得换一种思路,因为队列是先进先出,那么我们就可以尝试先把数据放到一个队列里面,然后把size-1个数据放到另一个空队列里面,再把剩下的那个数据取出来,一直这样循环;如下图所示:fb6a1132c7964153b78083ce41209d1e.gif

typedef int QDataType;
typedef struct QueueNode 
{QDataType data;struct QueueNode* next;
}QueueNode;//队列
typedef struct Queue
{QueueNode* phead;QueueNode* tail;int size;
}Queue;
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->tail = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//说明链表为空if (pq->phead == NULL){pq->phead = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = pq->tail->next;}pq->size++;
}//队列判空
bool QueueEmpty(Queue* pq)
{return pq->phead == NULL && pq->tail == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//只有一个结点if (pq->tail == pq->phead){free(pq->phead);pq->phead = NULL;pq->tail = NULL;}else{QueueNode* Del = pq->phead;pq->phead = pq->phead->next;free(Del);}--pq->size;
}//取队头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}
//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->tail = NULL;pq->size = 0;
}typedef struct 
{Queue q1;Queue q2;
} MyStack;//初始化
MyStack* myStackCreate() 
{MyStack* temp = (MyStack*)malloc(sizeof(MyStack));QueueInit(&temp->q1);QueueInit(&temp->q2);return temp;
}void myStackPush(MyStack* obj, int x) 
{//首先我们找哪个队列为空if(!QueueEmpty(&obj->q1)){QueuePush(&obj->q1,x);}elseQueuePush(&obj->q2,x);
}int myStackPop(MyStack* obj) 
{Queue* empQ = &obj->q1;Queue* noneQ = &obj->q2;if(!QueueEmpty(&obj->q1)){noneQ = &obj->q1;empQ = &obj->q2;}while(QueueSize(noneQ)>1) //大于1的原因是要留一个数据{int front = QueueFront(noneQ);QueuePush(empQ, front);QueuePop(noneQ);}int pop = QueueFront(noneQ);QueuePop(noneQ);return pop;
}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);obj = NULL;}

3.3 用栈实现队列

原题链接:232. 用栈实现队列 - 力扣(LeetCode)

e3654c89fadb41f695fbe27e6ee1f3d2.png

思路:首先我们分析,用两个栈实现队列的话,栈是先进后出,而队列是先进先出,我们使用1234来进行模拟;

队列是怎么进怎么出,1234进1234出;092b5c2eed7f462b81c50d77e4a8cb14.png

那么我们来看一下首先是堆,如果说我们直接把1234放进去,再取出来的话那就是4321,但是我们通过两个堆可以改变这个先让1234入一个名为pushST的堆,然后再让pushST堆的数据堆顶数据入一个名为popST的堆,这时候popST的堆的数据就为1234了;再取出来即可;那么用栈实现队列push数据到队列末尾的则只需要直接把数据放到堆顶即可;删除数据就如刚刚上面所说,取堆顶然后把最后一个数据留下来再把该数据删除就可以实现;取队头元素的实现也是如此;判空则是看两个堆是否都为空,都为空返回1即可;如下图和代码所示:652ea7dd28794dab9f01cff6063f0614.gif

typedef int STDatetype;typedef struct Stack
{STDatetype* arr;int capacity;  //栈空间大小int top;       //栈顶
}ST;void STInit(ST* st)
{st->arr = NULL;st->capacity = st->top = 0;
}
void STDestory(ST* st)
{if(st->arr)free(st->arr);st->arr = NULL;st->capacity = st->top = 0;
}void StackPush(ST* ps, STDatetype x)
{if (ps->capacity == ps->top  ){int Newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDatetype* temp = (STDatetype*)realloc(ps->arr, Newcapacity *sizeof(STDatetype) );if (temp == NULL){perror("realloc fail!");exit(1);}ps->arr = temp;ps->capacity = Newcapacity;}ps->arr[ps->top++] = x;
}bool StackEmpty(ST* ps)
{return ps->top == 0;
}void StackPop(ST* ps)
{assert(!StackEmpty(ps));--ps->top;
}//取栈顶元素
STDatetype StackTop(ST* ps)
{return ps->arr[ps->top-1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{return ps->top;
}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) 
{StackPush(&obj->pushST, x);
}int myQueuePop(MyQueue* obj) 
{if(StackEmpty(&obj->popST)){//为空就把pushST的数据放到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) 
{if(StackEmpty(&obj->popST)){while(!StackEmpty(&obj->pushST)){StackPush(&obj->popST, StackTop(&obj->pushST));StackPop(&obj->pushST);}}return StackTop(&obj->popST);
}bool myQueueEmpty(MyQueue* obj) 
{return StackEmpty(&obj->pushST) &&  StackEmpty(&obj->popST);
}void myQueueFree(MyQueue* obj) 
{STDestory(&obj->pushST);STDestory(&obj->popST);free(obj);obj = NULL;
}

END!

 

相关文章:

数据结构(栈和队列的实现)

1. 栈&#xff08;Stack&#xff09; 1.1 栈的概念与结构 栈是一种特殊的线性表&#xff0c;其只允许固定的一段插入和删除操作&#xff1b;进行数据插入和删除的一段叫做栈顶&#xff0c;另一端叫栈底&#xff1b;栈中的元素符合后进先出LIFO&#xff08;Last In First Out&…...

Python批量处理客户明细表格数据,挖掘更大价值

批量处理 .xls 数据并进行归类分析以挖掘内在价值&#xff0c;通常涉及以下步骤&#xff1a; 读取数据&#xff1a;使用 pandas 库读取 .xls 文件。数据清洗&#xff1a;处理缺失值、异常值、重复值等。数据转换&#xff1a;对数据进行必要的转换&#xff0c;如日期格式统一、…...

NAND Flash虚拟层索引表机制

​​​​​ NAND Flash虚拟层的索引表用于建立逻辑块与数据块、日志块之间的关系,用于NAND Flash虚拟层在运行过程中的读写、擦除操作;由于NAND Flash虚拟层采用集中索引的方式,因此在NAND Flash虚拟层启动时需要在NAND Flash存放索引表区域扫描并确定NAND Flash中存…...

Spring Boot框架:新闻推荐系统开发新趋势

3系统分析 3.1可行性分析 通过对本新闻推荐系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本新闻推荐系统采用JAVA作为开发语言&#xff0c;Spring Boot框…...

RK3568平台(opencv篇)opencv处理图像

一.颜色转换 cv2.cvtColor()函数功能: 将一幅图像从一个色彩空间转换到另一个色彩空间。 函数原型: cv2.cvtColor(src,code,dst=None,dstCn=None) 参数定义: src:要转换的源文件 code,转换的色彩空间,在 opencv 中有超过 150 种颜色空间转换方法,但是经常用的只有 B…...

【移动端】Viewport 视口

1. 什么是 Viewport&#xff08;视口&#xff09;&#xff1f; Viewport&#xff08;视口&#xff09;是指浏览器中用户可见的那部分网页内容的区域&#xff0c;简单来说&#xff0c;它是用户当前看到的网页的“窗口”区域。在不同的设备上&#xff0c;Viewport 的大小会有所不…...

PWM 模式

一、介绍 PWM&#xff08;脉宽调制&#xff0c;Pulse-width modulation&#xff09;是一种通过调节脉冲信号的宽度来控制电能输出的方法。PWM是一种方波信号&#xff0c;通常在电子和电气工程中用于调节功率输送&#xff0c;控制电机速度&#xff0c;调节LED亮度&#xff0c;以…...

模拟算法(3)_Z字形变换

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(3)_Z字形变换 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目链…...

Go语言实现长连接并发框架 - 任务执行流路由模块

文章目录 前言接口结构体接口实现项目地址最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;上篇博客中我们实现了任务执行流上下文部分&#xff0c;接下来我们实现一下任务执行流的路由模块&#xff0c;基于该模块可以实现将消息转发到相应注册的任务执行流中进行处理 接…...

Windows 编译 FFmpeg 源码详细教程

FFmpeg FFmpeg 是一个开源的多媒体框架,它包括了一整套工具和库,可以用来处理(转码、转换、录制、流式传输等)音频和视频。FFmpeg 支持广泛的音视频格式,并且可以在多种操作系统上运行,包括 Windows、Linux 和 macOS。 FFmpeg 的主要组件包括: ffmpeg:这是一个命令行工…...

JavaCV 实现视频链接截取封面工具

引入必要依赖 <!--JavaCV--> <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.7</version> </dependency> <dependency><groupId>cn.hutool</groupI…...

初识Linux · 进程替换

目录 前言&#xff1a; 1 直接看代码和现象 2 解释原理 3 将代码改成多进程版本 4 认识所有函数并使用 前言&#xff1a; 由前面的章节学习&#xff0c;我们已经了解了进程状态&#xff0c;进程终止以及进程等待&#xff0c;今天&#xff0c;我们学习进程替换。进程替换我…...

项目-坦克大战学习-人机ai

我们要知道&#xff0c;人机的移动和玩家的移动方式是一样的&#xff0c;所以我们可以将玩家移动代码以及检测碰撞代码移过来&#xff0c;唯一不同的就是人机检测到碰撞后会改变方向继续移动而不是停止 所以我们需要一个随机数使人机检测到碰撞后随机修改方向 Random rd new …...

YOLOv11改进 | Conv篇 | YOLOv11引入SKConv

1. SKConv介绍 1.1 摘要:在标准卷积神经网络(CNN)中,每层中阿尔蒂神经元的感受野被设计为共享相同的大小。在神经科学界众所周知,视觉皮层神经元的感受野大小受到刺激的调制,这在构建CNN时很少考虑。我们在CNN中提出了一种动态选择机制,允许每个神经元根据输入信息的多…...

招联2025校招内推

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...

美容院管理创新:SpringBoot系统设计与开发

摘 要 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让美容院信息的管理模式进行升级&#xff0c;也为了更好的维护美容院信息&#xff0c;美容院管理系统的开发运用就显得很有必要。并且通过开发…...

文心一言 VS 讯飞星火 VS chatgpt (361)-- 算法导论24.3 3题

三、假定将 Dijkstra 算法的第4行改为&#xff1a; 4 while |Q|>1 这种改变将让 while 循环的执行次数从 ∣ V ∣ |V| ∣V∣ 次降低到 ∣ V ∣ − 1 |V|-1 ∣V∣−1 次。这样修改后的算法正确吗? 如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 将 Dijkst…...

ArkTS 开发中,有两种网络请求

在鸿蒙开发中&#xff0c;ArkTS&#xff08;Ark TypeScript&#xff09;是用于构建鸿蒙应用的一种开发语言&#xff0c;它基于TypeScript&#xff0c;并提供了丰富的API和工具集来简化开发过程。其中&#xff0c;网络请求是开发应用时经常需要用到的一个功能。 ArkTS 网络请求…...

记录一次病毒启动脚本

在第一次下载软件时&#xff0c;目录中配了一个使用说明&#xff0c;说是需要通过start.bat 这个文件来启动程序&#xff0c;而这个 start.bat 就是始作俑者&#xff1a; 病毒作者比较狡猾&#xff0c;其中start.bat 用记事本打开是乱码&#xff0c;但是可以通过将这个批处理…...

2019~2023博文汇总目录

2023 大厂实践 - 哈啰&#xff1a;记录一次ElasticSearch的查询性能优化-CSDN博客 Shiro安全框架-CSDN博客 MQ知识点汇总-CSDN博客 工作学习记录-CSDN博客 后端架构师技术图谱-CSDN博客 2020 Elasticsearch相关技术点_elasticsearch技术点-CSDN博客 Kafka相关技术点_kafka…...

springboot项目配置部分依赖从私服拉取,部分从阿里云拉取

在Java项目中&#xff0c;配置部分依赖从私服拉取&#xff0c;部分从阿里云拉取&#xff0c;可以在Maven的配置文件settings.xml中设置多个镜像&#xff0c;Maven会根据镜像的顺序尝试下载依赖。 ‌配置私服镜像‌&#xff1a;首先配置你的私服镜像&#xff0c;例如Nexus私服&…...

返回索引对象中各元素的数据类型 pandas.Index.dtype

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 返回索引对象中 各元素的数据类型 pandas.Index.dtype [太阳]选择题 根据题目代码&#xff0c;执行idx3.dtype的结果是&#xff1f; import pandas as pd idx1 pd.Index([1, 2, 3, 4, 5])…...

通过freepbx搭建小型电话系统的过程

领导说公司的客服电话需要实现语音导航和非工作时间自动接听播放语音提示的功能。任务自然落到了伟大的程序员的头上&#xff0c;本着为公司节约成本原则遂百度了一番&#xff0c;找到了asterisk 和freeswitch两个比较流行的电话系统。经过对比和考虑公司的情况选择了asterisk系…...

pdf处理1

处理PDF文件以构建数据索引是一个复杂但关键的步骤&#xff0c;尤其是因为PDF格式的文件通常包含多种元素&#xff0c;如文本、图片、表格、标题等。以下是一个通俗易懂的详细解释&#xff0c;帮助你理解PDF文件是如何被处理和解析的&#xff1a; 1. PDF文件的基本结构 PDF&a…...

区间覆盖(贪心)

给定 NN 个闭区间 [ai,bi][ai,bi] 以及一个线段区间 [s,t][s,t]&#xff0c;请你选择尽量少的区间&#xff0c;将指定线段区间完全覆盖。 输出最少区间数&#xff0c;如果无法完全覆盖则输出 −1−1。 输入格式 第一行包含两个整数 ss 和 tt&#xff0c;表示给定线段区间的两…...

[rk3588 debain]cpu死锁问题解决

1 问题 rk3588机器上运行客户如下程序程序发生“BUG: spinlock recursion on CPU#0” ./rtsp RtspWrapper.xml 应用程序功能是&#xff1a;ip摄像头推流&#xff0c;通过rtsp协议拉流&#xff0c;对视频流做裁剪&#xff0c;缩放工作。首先&#xff0c;根据视频帧率每秒钟处理…...

CMU 10423 Generative AI:lec18(大模型的分布式训练)

这个文档主要讲解了分布式训练&#xff08;Distributed Training&#xff09;&#xff0c;特别是如何在多GPU上训练大规模的语言模型。以下是主要内容的概述&#xff1a; 1. 问题背景 训练大规模语言模型的主要挑战是内存消耗。 训练过程中&#xff0c;内存消耗主要来源于两个…...

项目级别的配置文件 `.git/config`||全局配置文件 `~/.gitconfig`

Git 项目级别的配置文件 .git/config&#xff0c;该文件包含了当前项目&#xff08;仓库&#xff09;的特定配置。 与全局配置文件 ~/.gitconfig 不同&#xff0c;这里的设置仅对当前项目生效。 配置内容解释 [core]repositoryformatversion 0filemode truebare falselog…...

【Docker】配置文件

问题 学习Docker期间会涉及到docker的很多配置文件&#xff0c;可能会涉及到的会有&#xff1a; /usr/lib/systemd/system/docker.service 【docker用于被systemd管理的配置文件】 /etc/systemd/system/docker.service.d【覆盖配置文件的存放处】 /etc/systemd/system/mul…...

坐标系变换总结

二维情况下的转换 1 缩放变换 形象理解就是图像在x方向和y方向上放大或者缩小。 代数形式&#xff1a; { x ′ k x x y ′ k y y \begin{cases} x k_x x \\ y k_y y \end{cases} {x′kx​xy′ky​y​ 矩阵形式&#xff1a; ( x ′ y ′ ) ( k x 0 0 k y ) ( x y ) \be…...