队列的实现和OJ练习(c语言)

目录

概念

队列的实现

利用结构体存放队列结构

为什么单链表不使用这种方法?

初始化队列

小提示:

队尾入队列

队头出队列

获取队头元素

获取队尾元素

获取队列中有效元素个数

检测队列是否为空

销毁队列

最终代码

循环队列 

队列的OJ题

用队列实现栈

用栈实现队列


概念

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特性,进行插入操作的一端称为队尾,进行删除操作的一端称为队头

队列的实现

tip:队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

利用结构体存放队列结构

        我们之前在实现单链表的时候使用到了二级指针来达到修改头尾结点的效果,这样会增加代码复杂性和理解难度......

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

//队列的结构(使用结构体避免了二级指针的使用)
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;        //存放队列大小
}Queue;

        现在我们依然选择用单链表实现队列,但是我们将指向链表的头尾结点的指针信息都存放在一个结构体中,这样就起到了简化参数传递的作用。即在初始化队列时只需分配一个包含头尾节点、队列大小等信息的结构对象,并将其作为参数传递给相关函数。这样就可以直接通过访问该结构对象中的相应成员变量来修改或获取所需信息

为什么单链表不使用这种方法?

        这是因为在单链表中,使用结构体来表示整个单链表可能会带来一些不必要的复杂性,并且没有明显的好处,相比于这种方法使用二级指针会有以下有点:

1. 简化插入和删除操作:由于插入或删除操作需要调整前后两个节点之间的连接关系(而队列不需要考虑在指定位置插入的问题),将头尾结点分别存放在结构体中可能需要更多额外处理步骤才能保持正确连接关系。

2. 节省内存空间:如果每个节点都包含了头尾信息,则会导致额外占用内存空间,并且增加了维护数据一致性所需付出成本。

3. 降低复杂度:通过仅保存对首元素(即头部)进行引用,在大多数情况下足以满足对单链表进行各种操作所需求。这样可以简化代码逻辑并提高代码可读性与可维护性。

初始化队列

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

实现步骤: 

1、利用断言检测队列指针的有效性

2、有效则将队列初始化为空队列,即将指向队头元素和队尾元素的指针及队列元素个数都置为空

小提示:

这个看不看都行

队尾入队列

//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->val = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、malloc申请新的结点空间,并进行开辟失败的判断

3、若开辟成功则向链表结点中插入有效数据,以及下一个结点的置空

4、检测队列是否为空,若为空则将新申请的结点作为队列的第一个元素,令指向队头和队尾的指针指向该元素

5、若不为空,则将指向队尾元素的指针指向新元素,同时将指向队尾的指针向后移动指向该元素

6、完成一次队尾入队操作后,将队列元素个数加一

队头出队列

// 对头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、利用断言检测队列头元素不为空

3、利用临时指针变量删除队头结点,并将指向队头结点指针向后移动,最后释放该指针并置空

4、队头元素出队列后,若头指针变为空,则将尾指针也变为空

获取队头元素

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->phead);

	return pq->phead->val;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、利用断言检测队列头元素不为空

3、返回此时队头元素的值

获取队尾元素

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->phead);

	return pq->phead->val;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、利用断言检测队列头元素不为空

3、返回此时队尾元素的值

获取队列中有效元素个数

//获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、返回结构体中的size值

检测队列是否为空

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、返回对pq->phead == NULL的判断结果,返回结果为真则队列为空,为假则队列非空

销毁队列

//销毁队列
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;
}

实现步骤: 

1、利用断言检测队列指针的有效性

2、遍历每一个队头元素并销毁,最后将头尾指针及队列元素数量均置为空

最终代码

Queue.h文件

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

//队列的结构(使用结构体避免了二级指针的使用)
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//队尾入队列
void QueueDestroy(Queue* pq);

//队头出队列
void QueuePush(Queue* pq, QDataType x);

//获取队头元素
void QueuePop(Queue* pq);

//获取队尾元素
QDataType QueueFront(Queue* pq);

//获取队列中有效元素个数
QDataType QueueBack(Queue* pq);

//检测队列是否为空
bool QueueEmpty(Queue* pq);

//销毁队列
int QueueSize(Queue* pq);

Queue.c文件

#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	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;
}

//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->val = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

// 对头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->phead);

	return pq->phead->val;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->ptail);

	return pq->ptail->val;
}

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

//获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

test.c文件

#include "Queue.h"

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 4);
	QueuePush(&q, 5);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	QueueDestroy(&q);

	return 0;
}

循环队列 

概念:循环队列是一种特殊的队列数据结构,它通过使用固定大小的数组来实现。与普通队列不同的是,在循环队列中,当尾指针(rear)达到数组末尾时,会从数组开头重新开始存储元素

优点:充分利用出队操作后释放出来的空间,避免频繁地移动元素

适用场景:循环队列常用于需要高效处理连续输入输出流数据的场景,如缓冲区、任务调度等

关于循环队列的实现放在下一篇文章......

队列的OJ题

用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode)

具体解题思路如下:

1、题目要求使用两个队列,但队列的基本功能需要自己实现

2、利用栈的结构体存储两个队列的头尾指针及队列元素个数的信息

3、为队列申请结点空间并利用QueueInit函数初始化队列

4、 入栈时,利用if判断谁为非空队列后,将要入栈的元素尾插进非空队列中(QueueBack函数),出栈的大体过程如下图所示:

5、出栈时,为了将队列的最后一个元素先排出,就需要让原来存储有效数据的队列的前N-1个元素放入空的队列中,然后再将元队列中的最后一个元素排出,两个队列的作用是来回切换的需要利用if语句判断队列的空与非空后在进行操作,出栈的大体过程如下图所示:

6、获取栈顶元素时,只需要判断两个队列谁不为空,将不为空的队列的队尾元素返回即可

7、判断栈是否为空时,当两个队列均为空时栈才能为空,所以需要用&&

8、销毁栈时、不仅要释放掉malloc申请的内存空间,还要将两个队列一同销毁

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

//队列的结构(使用结构体避免了二级指针的使用)
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//队尾入队列
void QueueDestroy(Queue* pq);

//队头出队列
void QueuePush(Queue* pq, QDataType x);

//获取队头元素
void QueuePop(Queue* pq);

//获取队尾元素
QDataType QueueFront(Queue* pq);

//获取队列中有效元素个数
QDataType QueueBack(Queue* pq);

//检测队列是否为空
bool QueueEmpty(Queue* pq);

//销毁队列
int QueueSize(Queue* pq);



//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	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;
}

//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->val = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

// 对头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	// 
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);

	assert(pq->phead);

	return pq->phead->val;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	 
	assert(pq->ptail);

	return pq->ptail->val;
}

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

//获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

typedef struct {
  Queue q1;
  Queue q2;  
} MyStack;

//初始化栈
MyStack* myStackCreate() {
    //申请结点空间
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    //取地址后就可以操作栈结构体中p1和p2中的内容
    QueueInit(&pst->q1);       //->的优先级大于&
    QueueInit(&pst->q2);       //->的优先级大于&
	return pst;
}

//入栈
void myStackPush(MyStack* obj, int x) {
    //如果q1队列不为空则q1队列用于存放导出的数据
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    //如果q1队列为空则q2队列用于存放导出的数据
    else
    {
        QueuePush(&obj->q2,x);
    }
}

//出栈
int myStackPop(MyStack* obj) {
    //起始假设q1为空,q2不为空,empty指针指向空队列,noempty指向非空队列
    Queue* empty = &obj->q1;
    Queue* noempty = &obj->q2;
    //如果我们假设失败则证明q1不为空,q2为空,交换标志
    if(!QueueEmpty(&obj->q1))
    {
        empty = &obj->q2;
        noempty = &obj->q1;
    }
    
    //然后将队列中的前N-1个元素导入空队列,所以循环结束的条件就是队列中元素等于1(这个剩下的就是要出栈的元素)
    while(QueueSize(noempty) > 1)
    {
        QueuePush(empty,QueueFront(noempty));
        QueuePop(noempty);
    }

    //将队列中前N-1个元素导出后剩余的就是要出栈的元素,将该元素存储进整型变量top中
    int top = QueueFront(noempty);
    //存储完成后将该元素也进行出队列操作
    QueuePop(noempty);

    //最后返回要出栈的元素
    return top;
}

//获取栈顶元素
int myStackTop(MyStack* obj) {
    //谁不为空就获取谁的队尾元素
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    //如果q1队列为空则q2队列用于存放导出的数据
    else
    {
        return QueueBack(&obj->q2);
    }
}

//判断栈是否为空
bool myStackEmpty(MyStack* obj) {
    //只有当两个队列均为空的时候,该栈才不会有有效数据
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

//销毁栈
void myStackFree(MyStack* obj) {
    //初始化时除了malloc了一个结点,还初始化了两个队列,所以除了要将申请的结点释放还要将申请的两个队列销毁
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);

    free(obj);
}

关于&pst->q1的解释:

为了操作队列,我们在初始化栈时需要将队列对象作为参数传递给函数,即&pst->q1,表示获取指针 pst 所指向的结构体中成员变量 q1 的地址(可以理解为队列q1的地址),而pst是一个指向MyStack结构体的指针,所以说就是获取MyStack结构体中成员变量的地址。

用栈实现队列

232. 用栈实现队列 - 力扣(LeetCode)

具体解题思路如下:

1、题目要求使用两个栈,但栈的基本功能需要自己实现

2、利用栈的结构体存储两个队列的头尾指针及队列元素个数的信息

3、为栈申请结点空间并利用STInit函数初始化栈

4、入队时,直接利用STPush函数进行入栈即可

5、先返回队头元素然后再出队,先判断用于出数据的popst栈是否为空,如果为空则将pushst栈中的数据倒顺序后放入popst栈中,利用STPush函数将pushst栈的栈顶元素放入空的popst栈中

6、出队时,用临时变量front接收返回的队头元素,然后利用STPop函数将该元素出队,最后返回该元素的值

7、判断队列是否为空时,当两个栈均为空时队列才能为空,所以需要用&&

8、销毁栈时、不仅要释放掉malloc申请的内存空间,还要将两个栈一同销毁

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
 
//支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;	  //表示栈顶位置
	int capacity; //栈的容量
}ST;
 
// 初始化栈
void STInit(ST* ps);
 
// 销毁栈
void STDestroy(ST* ps);
 
// 入栈
void STPush(ST* ps, STDataType data);
 
// 出栈
void STPop(ST* ps);
 
// 获取栈顶元素
STDataType STTop(ST* ps);
 
// 获取栈中有效元素个数
int STSize(ST* ps);
 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int STEmpty(ST* ps);

// 初始化栈
void STInit(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
 
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;    //令pop表示栈顶元素的下一个元素的下标
}
 
// 销毁栈
void STDestroy(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	
    //正常操作不再过多复述
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}
 
//入栈
void STPush(ST* pst, STDataType x)
{
    //首先要指向一个栈
	assert(pst);
    
    //判断栈是否已满,如果栈满则申请新的内存空间
	if (pst->top == pst->capacity)
	{
		int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
 
    //如果栈未满则进行入栈操作(若初始化时pop=-1,则下面两行代码交换执行顺序)
	pst->a[pst->top] = x;    //此时pop表示的是栈顶元素的下一个元素的下标 
	pst->top++;              //top表示的下标数++
}
 
//出栈
void STPop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);
 
    //直接对top执行减减操作以获取实际数组元素下标
    pst->top--;
}
 
// 获取栈顶元素
STDataType STTop(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
	//top<0表示栈为空,top=0表示没有元素入栈,存在这两种情况就不能执行出栈操作(可以提供的图理解)
	assert(pst->top > 0);
	
    //当初始化top=0时,top的值与实际数组元素下标的值之间的关系是:实际下标 = top-1
    //所以这里要进行减一操作得到实际的数组元素下标后再输出
	return pst->a[pst->top - 1];
}
 
//获取栈中有效元素个数
int STSize(ST* pst)
{
    //首先要指向一个栈
	assert(pst);
    
    //初始化top=0,则top等于多少栈中就有多少个元素
	return pst->top;
}
 
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int STEmpty(ST* pst)
{
	//首先要指向一个栈
	assert(pst);
	
    //如果pst->top不为空则返回结果为真,为空则返回假
	return pst->top == NULL;
}

typedef struct {
    ST pushst;
    ST popst;    
} MyQueue;

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

//入队
void myQueuePush(MyQueue* obj, int x) {
        STPush(&obj->pushst,x);
}

//出队
int myQueuePop(MyQueue* obj) {
    int front = myQueuePeek(obj);
    STPop(&obj->popst);
    return front;
}

//返回队头元素
int myQueuePeek(MyQueue* obj) {
    //栈不为空就倒数据
    if(STEmpty(&obj->popst))
    {
        //将pusust里面的数据倒转顺序后传递给popst
        while(!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst, STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }

    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);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

关于&pst->pushst和&pst->popst的解释:

~over~ 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

git clone -mirror 和 git clone 的区别

目录 前言两则区别git clone --mirrorgit clone 获取到的文件有什么不同瘦身仓库如何选择结语开源项目 前言 Git是一款强大的版本控制系统&#xff0c;通过Git可以方便地管理代码的版本和协作开发。在使用Git时&#xff0c;常见的操作之一就是通过git clone命令将远程仓库克隆…

SHAP - 机器学习模型可解释性工具

github地址&#xff1a;shap/docs/index.rst at master shap/shap (github.com) SHAP使用文档&#xff1a;欢迎使用 SHAP 文档 — SHAP 最新文档 SHAP介绍 SHAP&#xff08;SHapley Additive exPlanations&#xff09;是一种用于解释预测结果的方法&#xff0c;它基于Shapley…

好的程序员有什么特质呢?

程序员想要提升自己&#xff0c;一定要关注到工作中的方方面面。而一个好的程序员&#xff0c;一般都有这些特质&#xff1a; 弱者抱怨环境&#xff0c;强者改变环境 不要试图通过抱怨环境来获得工作环境上的改变&#xff0c;这不仅不会给你带来任何实质性的改变&#xff0c;…

一文详解Vue生命周期

Vue是一种流行的用于构建用户界面的渐进式JavaScript框架。Vue框架在开发过程中&#xff0c;特别强调对生命周期的理解和管理。通过使用生命周期钩子函数&#xff0c;开发者能够精确地控制Vue实例的创建、挂载、更新以及销毁过程。本文将对Vue的生命周期进行详细的介绍&#xf…

Nginx模块开发之http handler实现流量统计(1)

文章目录 一、handler简介二、Nginx handler模块开发2.1、示例代码2.2、编写config文件2.3、编译模块到Nginx源码中2.4、修改conf文件2.5、执行效果 三、Nginx的热更新总结 一、handler简介 Handler模块就是接受来自客户端的请求并产生输出的模块。 配置文件中使用location指令…

微软发布最新.NET 8长期支持版本,云计算、AI应用支持再强化

11 月 15 日开始的为期三天的 .NET Conf 在线活动的开幕日上&#xff0c;.NET 8作为微软的开源跨平台开发平台正式发布。.NET 团队着重强调云、性能、全栈 Blazor、AI 和 .NET MAUI 是.NET 8的主要亮点。.NET团队在 .NET Conf 2023 [1]活动开幕式上表示&#xff1a;“通过这个版…

JSP过滤器和监听器

什么是过滤器 Servlet过滤器与Servlet十分相似&#xff0c;但它具有拦截客户端&#xff08;浏览器&#xff09;请求的功能&#xff0c;Servlet过滤器可以改变请求中的内容&#xff0c;来满足实际开发中的需要。 对于程序开发人员而言&#xff0c;过滤器实质就是在Web应用服务器…

不会代码也能拿高薪?掌握面试法宝,轻松10000+

快速排序&#xff08;Quicksort&#xff09;是对冒泡排序的一种改进。 快速排序由 C.A.R.Hoare 在 1962 年提出。 它的基本思想是&#xff1a;通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后…

代码随想录算法训练营第五十二天|1143.最长公共子序列 1035.不相交的线 53. 最大子序和

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 1143.最长公共子序列 class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:dp [[0] * (len(text2) 1) for _ i…

Redisson分布式锁源码解析、集群环境存在的问题

一、使用Redisson步骤 Redisson各个锁基本所用Redisson各个锁基本所用Redisson各个锁基本所用 二、源码解析 lock锁 1&#xff09; 基本思想&#xff1a; lock有两种方法 一种是空参 另一种是带参 * 空参方法&#xff1a;会默认调用看门狗的过期时间30*1000&…

从Discord的做法中学习 — 使用Golang进行请求合并

正如你可能之前看到的&#xff0c;Discord去年发布了一篇有价值的文章&#xff0c;讨论了他们成功存储了数万亿条消息。虽然有很多关于这篇文章的YouTube视频和文章&#xff0c;但我认为这篇文章中一个名为“数据服务为数据服务”的部分没有得到足够的关注。在这篇文章中&#…

redis运维(十九)redis 的扩展应用 lua(一)

一 redis 的扩展应用 lua redis如何保证原子操作 说明&#xff1a;引入lua脚本,核心解决原子性问题 ① redis为什么引入lua? lua脚本本身体积小,启动速度快 ② redis引入lua的优势 小结&#xff1a; 类似自定义redis命令 ③ redis中如何使用lua ④ EVAL 说明&#…

【性能优化】JVM调优与写出JVM友好高效的代码

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

2023年11个最佳免费WordPress主题

如果您刚刚开始使用 WordPress&#xff0c;您可能会很自然地认为&#xff0c;只要免费的WordPress主题看起来像您想要的网站主题&#xff0c;那么它就很合适。不幸的是&#xff0c;事情并没有那么简单。这就是为什么在今天的文章中&#xff0c;我们概述了一份可靠的标准清单&am…

旅行商问题(枚举,回溯,动态规划,贪心,分支界限)

文章目录 问题描述暴力枚举回溯法动态规划法贪心法分支界限法 问题描述 假设有一个货郎担要拜访n个城市&#xff0c;他必须选择所要走的路程&#xff0c;路程的限制时每个城市只能拜访一次&#xff0c;而且最后要走到原来出发的城市&#xff0c;要求路径长度。 旅行商问题将要…

爬虫逆向你应该懂得Javascript知识

背景 大家在学习爬虫逆向的时候&#xff0c;一般都会涉及到对js源文件进行代码扣去&#xff0c;但是有的时候&#xff0c;你最好有js基础&#xff0c;能发现加密或者解密在那个位置&#xff0c;或者是能用python改写js代码&#xff0c;这就对个人的Javascript的能力有一定要求…

python如何快速查找到想要的文档

字多不看版&#xff0c;直接体验 待补充 演示代码 # -*- coding:UTF-8 -*-# region 导入必要的依赖包 import os import subprocess from enum import Enum模块名 pyperclip try:import pyperclip # 需要安装 pyperclip 模块&#xff0c;以支持粘贴板操作 except ImportEr…

激光雷达与惯导标定 | Lidar_IMU_Init : 编译

激光雷达与惯导标定&#xff1a;Lidar_IMU_Init 编译 功能包安装安装ceres-solver-2.0.0 &#xff08;注意安装2.2.0不行&#xff0c;必须要安装2.0.0&#xff09; LI-Init是一种鲁棒、实时的激光雷达惯性系统初始化方法。该方法可校准激光雷达与IMU之间的时间偏移量和外部参数…

【Spring】 IoCDI

回顾 企业命名规范 大驼峰:BookDao(首字母都大写) 类名 小驼峰:bookDao(第一个字母小写) 方法名 蛇形:book_dao(小写下划线_) 数据库 串形:book-dao(小写连字符-) 项目文件夹 各种注解 学习Spring MVC, 其实就是学习各种Web开发需要⽤的到注解 a. RequestMapping: 路由…

(一)C语言之入门:使用Visual Studio Community 2022运行hello world

使用Visual Studio Community 2022运行c语言的hello world 一、下载安装Visual Studio Community 2022 与 新建项目二、编写c helloworld三、编译、链接、运行 c helloworld1. 问题记录&#xff1a;无法打开源文件"stdio.h"2. 问题记录&#xff1a;调试和执行按钮是灰…
最新文章