数据结构:栈和队列(Leetcode20. 有效的括号+225. 用队列实现栈+232. 用栈实现队列)

news2025/2/26 9:42:42

目录

一.数据结构--栈

1.栈的基本介绍

2.栈的实现

二.数据结构--队列

1.队列的基本介绍

2.队列的实现 

三.栈的运用(Leetcode20. 有效的括号+225) 

1.问题描述 

2.问题分析

题解代码:

四.用两个队列实现栈(225. 用队列实现栈 - 力扣(Leetcode))

题解代码:

五.用两个栈实现队列(232. 用栈实现队列 - 力扣(Leetcode))

​题解代码:


一.数据结构--栈

1.栈的基本介绍

栈是一种数据先进后出,后进先出的数据结构:

  • 栈一般用数组来实现(用单链表实现的栈缺陷明显:数据的出栈操作时间复杂度为O(N),原因在于要找到表尾数据的前一个数据的地址

2.栈的实现

总体图解:

栈的头文件:

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

typedef int STDataType;
typedef struct Stack
{
	STDataType * arry;	   //栈的堆区空间指针
	int top;               //维护栈顶下标,同时记录栈中的元素个数
	int capacity;		   //记录栈的容量
}ST;

void StackInit(ST* Stack);					//栈对象的初始化
STDataType StackTop(ST*Stack);				//返回栈顶元素
void StackPush(ST* Stack,STDataType val);   //元素入栈
void StackPop(ST* Stack);					//元素出栈
int StackSize(ST* Stack);				    //返回栈对的元素个数的接口;
bool StackEmpty(ST* Stack);					//判断栈是否为空的接口
void StackDestroy(ST* Stack);				//销毁栈

栈初始化接口:

void StackInit(ST* Stack)
{
	assert(Stack);
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

返回栈顶元素的接口:

STDataType StackTop(ST* Stack)
{
	assert(Stack);
	assert(Stack->top > 0);
	return Stack->arry[Stack->top - 1];
}

返回栈数据个数的接口:

int StackSize(ST* Stack)
{
	assert(Stack);
	return Stack->top;
}

判断栈是否为空的接口:

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

数据入栈操作接口:

void StackPush(ST* Stack, STDataType val)
{
	assert(Stack);
	if (Stack->capacity == Stack->top)    //检查容量是否足够
	{
		int newcapacity = (0 == Stack->capacity) ? 4 : 2 * Stack->capacity;
        //如果容量为零,则将容量设置为4,其他情况下按照两倍扩容方式增容
		STDataType* tem = (STDataType*)realloc(Stack->arry, newcapacity*sizeof(STDataType));
		if (NULL == tem)//判断realloc是否成功
		{
			perror("realloc failed:");
			exit(-1);
		}
		Stack->capacity = newcapacity;
		Stack->arry = tem;
	}
    //元素入栈
	Stack->arry[Stack->top] = val;
	Stack->top++;
}

数据出栈操作接口:

void StackPop(ST* Stack)
{
	assert(Stack);
	assert(Stack->top > 0);
	Stack->top--;
}

销毁栈的接口:

void StackDestroy(ST* Stack)
{
	assert(Stack);
	if (Stack->arry)
	{
		free(Stack->arry);
	}
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

二.数据结构--队列

1.队列的基本介绍

队列是一种数据先进先出,后进后出的数据结构.

  • 数据只能从队尾入,从队头出 
  • 队列一般用单链表实现,原因在于队列涉及数据的头删操作,单链表的数据头删操作时间复杂度为O(1).

2.队列的实现 

队列总体图解:

队列的头文件: 

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


typedef int QDataType;

typedef struct QueueNode         //单链表结构体
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue             //维护队列的结构体
{
	QNode* head;
	QNode* tail;
}Queue;


void QueueInit(Queue* queue);                  //队列的初始化接口
void QueueDestroy(Queue* queue);               //销毁队列的接口
void QueuePush(Queue* queue,QDataType val);    //数据入队接口
void QueuePop(Queue* queue);                   //数据出队接口
QDataType QueueFront(Queue* queue);            //返回队头数据接口
QDataType QueueBawck(Queue* queue);            //返回队尾数据接口
int QueueSize(Queue* queue);                   //返回队列数据个数的接口
bool QueueEmpty(Queue* queue);                 //判断队列是否为空的接口

队列的初始化接口:

void QueueInit(Queue* queue)       //队列初始化   
{
	assert(queue);
	queue->head = NULL;
	queue->tail = NULL;
}

返回队头数据的接口:

QDataType QueueFront(Queue* queue) //返回队列头部数据
{
	assert(queue);
	assert(queue->head);
	return queue->head->data;
}

返回队尾数据的接口:

QDataType QueueBawck(Queue* queue) //返回队列尾部数据
{
	assert(queue);
	assert(queue->tail);
	return queue->tail->data;
}

返回队列数据个数的接口:

int QueueSize(Queue* queue)        //返回队列节点个数
{
	assert(queue);
	int count = 0;
	QNode* tem = queue->head;
	while(tem)
	{
		count++;
		tem = tem->next;
	}
	return count;
}

判断队列是否为空的接口:

bool QueueEmpty(Queue* queue)      //判断队列是否为空
{
	assert(queue);
	return (NULL == queue->head);
}

数据入队接口:

  

void QueuePush(Queue* queue,QDataType val)         //链表尾插数据(数据从队列尾入队)
{
	assert(queue);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));//申请堆区链表节点
	if (NULL == NewNode)
	{
		perror("malloc failed:");
		exit(-1);
	}
	NewNode->data = val;
	NewNode->next = NULL;

	if (NULL == queue->tail)                       //链表为空时的数据插入操作
	{
		assert(NULL == queue->head);
		queue->tail = NewNode;
		queue->head = NewNode;
	}
	else                                           //链表非空时的数据插入操作
	{
		queue->tail->next = NewNode;
		queue->tail = NewNode;
	}
}

数据出队的接口:

void QueuePop(Queue* queue)		    //删去链表头节点(数据从队列头部出队)
{
	assert(queue);
	assert(queue->head && queue->tail);
	QNode* Next = queue->head->next;
	free(queue->head);
	queue->head = Next;
	if (NULL == queue->head)        //若删去的是链表的最后一个元素,则要将尾指针也置空
	{
		queue->tail = NULL;
	}
}

销毁队列的接口:

void QueueDestroy(Queue* queue)    //销毁链表(队列)
{
	assert(queue);
	QNode* tem = queue->head;
	while (tem)
	{
		QNode* Next = tem->next;
		free(tem);
		tem = Next;
	}
	queue->head = NULL;
	queue->head = NULL;
}

三.栈的运用(Leetcode20. 有效的括号+225) 

20. 有效的括号 - 力扣(Leetcode)

1.问题描述 

 

给定一个只包括 '(' ,   ')' ,    '{'  ,      '}'  ,   '['  ,   ']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例:

输入:s = "()[]{}"
输出:true

C题解接口:

bool isValid(char * s)
{

}

2.问题分析

左向右遍历字符串的情况下的括号匹配思路:

  • 由于在遍历字符串时,我们无法确定当前的左括号会和哪一个右括号匹配,因此匹配的思路应该是先找到第一个右括号,然后再向前寻找相应的左括号,若匹配失败则返回false,若匹配成功则继续寻找下一个右括号重复上述过程,直到结束
  •  暴力匹配算法动画解析:
  • 根据上面的分析可知暴力匹配法的时间复杂度为O(N^2) 

然而本题如果使用辅助栈来求解,则可以达到用空间来换取时间效率的目的。

基本思路如下:

  • 用一个指针遍历字符串,每当遇到左括号,我们就将其压入栈中
  • 每当遇到右括号,我们就将其与栈顶括号进行匹配,若匹配失败则说明字符串不满足条件,接口返回false,若匹配成功则弹出栈顶元素,并继续遍历字符串重复上述步骤直到找到'\0'则接口返回true
  • 算法动画图解: 

我们可以将实现好的栈(本期第一节中)用于解题(顺便还可以验证我们栈的实现有没有bug)

题解代码:

用于题解的栈:

typedef char STDataType;
typedef struct Stack
{
	STDataType * arry;	   //栈的堆区空间指针
	int top;               //维护栈顶下标,同时记录栈中的元素个数
	int capacity;		   //记录栈的容量
}ST;

void StackInit(ST* Stack)
{
	assert(Stack);
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

STDataType StackTop(ST* Stack)
{
	assert(Stack);
	assert(Stack->top > 0);
	return Stack->arry[Stack->top - 1];
}

void StackPush(ST* Stack, STDataType val)
{
	assert(Stack);
	if (Stack->capacity == Stack->top)    //检查容量是否足够
	{
		int newcapacity = (0 == Stack->capacity) ? 4 : 2 * Stack->capacity;
		STDataType* tem = (STDataType*)realloc(Stack->arry, newcapacity*sizeof(STDataType));
		if (NULL == tem)
		{
			perror("realloc failed:");
			exit(-1);
		}
		Stack->capacity = newcapacity;
		Stack->arry = tem;
	}
	Stack->arry[Stack->top] = val;
	Stack->top++;
}

void StackPop(ST* Stack)
{
	assert(Stack);
	assert(Stack->top > 0);
	Stack->top--;
}


int StackSize(ST* Stack)
{
	assert(Stack);
	return Stack->top;
}

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



void StackDestroy(ST* Stack)
{
	assert(Stack);
	if (Stack->arry)
	{
		free(Stack->arry);
	}
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

两个辅助判断接口代码:

    //判断指针指向的括号是否为右括号的接口
    bool CheckRightQutoe(const char quote)  //左括号返回true,有括号返回false
    {
        if('{' == quote || '(' == quote || '[' == quote)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    //判断两个括号是否匹配的接口
    bool JudgeMatch(const char right,const char left)
    {
        if(('}' == right && '{' == left) || 
           (']' == right && '[' == left) || 
           (')' == right && '(' == left) 
          )
        {
            return true;
        }
        return false;
    }

题解接口代码:

bool isValid(char * s)
{
    ST ans;
    StackInit(&ans);         //创建一个栈
    int lenth = strlen(s);   //字符串有效字符个数为奇数直接返回false
    if(lenth % 2 == 1)       
    {
        return false;
    }
    char * tem = s;          //tem用于遍历字符串
    while('\0' != *tem)
    {
        if(CheckRightQutoe(*tem))  //遇到右括号则入栈
        {
            StackPush(&ans,*tem);
        }
        else                       //遇到左括号则与栈顶右括号匹配
        {
            if(StackEmpty(&ans) || !JudgeMatch(*tem,StackTop(&ans)))
            {
                return false;      //匹配前注意判断栈是否为空
            }
            StackPop(&ans);        //匹配完后弹出栈顶括号       
        }
        tem ++;
    }
    if(!StackEmpty(&ans))          //栈为空说明全部括号都完成了匹配
    {
        return false;
    }
    return true;
}
  • 该方法充分利用了栈数据后进先出的特点
  • 算法的时间复杂度为O(N)

 

四.用两个队列实现栈(225. 用队列实现栈 - 力扣(Leetcode))

本题并没有什么实际意义,求解它的目的仅仅在于加深对栈和队列这两种数据结构的理解

  • 用两个队列实现数据的先进后出

题解接口:

typedef struct 
{

} MyStack;


//创建MyStack结构体
MyStack* myStackCreate() 
{

}


//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{

}


//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{

}



//栈顶的数据其实就是非空队列尾的数据
int myStackTop(MyStack* obj) 
{

}


//返回栈顶的数据
bool myStackEmpty(MyStack* obj) 
{

}


//销毁栈
void myStackFree(MyStack* obj) 
{

}

我们可以给自己设置一个规定:用队列实现的'栈'的接口中,我们只能使用队列的标准操作接口来操作'栈'中的元素的进出.

  • 本题我们利用本期第二节实现好的队列来实现栈

用两个队列实现栈的数据结构总体图解:

两个队列实现一个栈的结构体的定义:

typedef struct 
{
    Queue queue1;
    Queue queue2;
} MyStack;

数据入'栈'接口:

void myStackPush(MyStack* obj, int x) 
  • 向MyStack两个队列成员中的非空队列中插入数据
  • (若两个队列都为空则向任意一个队列插入数据都可以) 
//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        QueuePush(&(obj->queue1),x);
    }
    else
    {
        QueuePush(&(obj->queue2),x);
    }
}

数据出栈接口:

int myStackPop(MyStack* obj) 


数据出栈过程动画图解:

 

//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{
    int ret = 0;
    //将非空队列里面的(除了队尾的元素)移到另外一个空队列中
    if(!QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2)))
    {
        while(obj->queue1.head != obj->queue1.tail) //转移数据
        {
            QueuePush(&(obj->queue2),(obj->queue1).head->data);
            QueuePop(&(obj->queue1));               
        }
        ret = QueueFront(&(obj->queue1));   //记录要弹出的数据
        QueuePop(&(obj->queue1));           //弹出数据
    }
    else
    {
        while(obj->queue2.head != obj->queue2.tail) //转移数据
        {
            QueuePush(&(obj->queue1),(obj->queue2).head->data);
            QueuePop(&(obj->queue2));
        }
        ret = QueueFront(&(obj->queue2));    //记录要弹出的数据
        QueuePop(&(obj->queue2));            //弹出数据
    }
    return ret;                              //返回被弹出的数据
}
  • 通过两个队列的交互我们便完成了数据的先进后出

题解代码:

用于题解的队列:

typedef int QDataType;

typedef struct QueueNode      //队列链表节点结构体
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue          //维护队列的结构体
{
	QNode* head;
	QNode* tail;
}Queue;


void QueueInit(Queue* queue);               //队列初始化
void QueueDestroy(Queue* queue);            //销毁链表(队列)
void QueuePush(Queue* queue,QDataType val); //链表尾插数据(数据从队列尾入队)
void QueuePop(Queue* queue);                //删去链表头节点(数据从队列头部出队)
QDataType QueueFront(Queue* queue);         //返回队列头部数据
QDataType QueueBawck(Queue* queue);         //返回队列尾部数据
int QueueSize(Queue* queue);                //返回队列节点个数
bool QueueEmpty(Queue* queue);              //判断队列是否为空


void QueueInit(Queue* queue)       //队列初始化   
{
	assert(queue);
	queue->head = NULL;
	queue->tail = NULL;
}
void QueueDestroy(Queue* queue)    //销毁链表(队列)
{
	assert(queue);
	QNode* tem = queue->head;
	while (tem)                    //逐个节点释放链表
	{
		QNode* Next = tem->next;
		free(tem);
		tem = Next;
	}
	queue->head = NULL;
	queue->head = NULL;
}


void QueuePush(Queue* queue,QDataType val)  //链表尾插数据(数据从队列尾入队)
{
	assert(queue);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (NULL == NewNode)                    //在堆区申请节点空间
	{
		perror("malloc failed:");
		exit(-1);
	}
	NewNode->data = val;
	NewNode->next = NULL;

	if (NULL == queue->tail)                //注意判断队列是否为空队列并分类讨论完成元素插入
	{
		assert(NULL == queue->head);
		queue->tail = NewNode;              //队列为空时tail和head指向同一个节点
		queue->head = NewNode;
	}
	else
	{
		queue->tail->next = NewNode;        //队列非空时令tail指向队尾数据
		queue->tail = NewNode;
	}
}


void QueuePop(Queue* queue)		    //删去链表头节点(数据从队列头部出队)
{
	assert(queue);
	assert(queue->head && queue->tail);
	QNode* Next = queue->head->next;
	free(queue->head);
	queue->head = Next;
	if (NULL == queue->head)        //注意判断完成删除元素后队列是否变为空队列
	{
		queue->tail = NULL;
	}
}


QDataType QueueFront(Queue* queue) //返回队列头部数据
{
	assert(queue);
	assert(queue->head);
	return queue->head->data;
}
QDataType QueueBawck(Queue* queue) //返回队列尾部数据
{
	assert(queue);
	assert(queue->tail);
	return queue->tail->data;
}


int QueueSize(Queue* queue)        //返回队列节点个数
{
	assert(queue);
	int count = 0;
	QNode* tem = queue->head;
	while(tem)                      //计算节点个数
	{
		count++;
		tem = tem->next;
	}
	return count;
}

bool QueueEmpty(Queue* queue)      //判断队列是否为空
{
	assert(queue);
	return (NULL == queue->head);
}

题解的栈:

typedef struct 
{
    Queue queue1;
    Queue queue2;
} MyStack;


//创建MyStack结构体
MyStack* myStackCreate() 
{
    MyStack * tem = (MyStack *)malloc(sizeof(MyStack));
    if(NULL == tem)
    {
        perror("malloc failed:");
        exit(-1);
    }
    QueueInit(&(tem->queue1));
    QueueInit(&(tem->queue2));
    return tem;
}


//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        QueuePush(&(obj->queue1),x);
    }
    else
    {
        QueuePush(&(obj->queue2),x);
    }
}


//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{
    int ret = 0;
    //将非空队列里面的(除了队尾的元素)移到另外一个空队列中
    if(!QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2)))
    {
        while(obj->queue1.head != obj->queue1.tail) //转移数据
        {
            QueuePush(&(obj->queue2),(obj->queue1).head->data);
            QueuePop(&(obj->queue1));               
        }
        ret = QueueFront(&(obj->queue1));   //记录要弹出的数据
        QueuePop(&(obj->queue1));           //弹出数据
    }
    else
    {
        while(obj->queue2.head != obj->queue2.tail) //转移数据
        {
            QueuePush(&(obj->queue1),(obj->queue2).head->data);
            QueuePop(&(obj->queue2));
        }
        ret = QueueFront(&(obj->queue2));    //记录要弹出的数据
        QueuePop(&(obj->queue2));            //弹出数据
    }
    return ret;                              //返回被弹出的数据
}



//栈顶的数据其实就是非空队列尾的数据
int myStackTop(MyStack* obj) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        return obj->queue1.tail->data;
    }
    else
    {
        return obj->queue2.tail->data;
    }
}


//返回栈顶的数据
bool myStackEmpty(MyStack* obj) 
{
    return QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2));
}


//销毁栈
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&(obj->queue1));
    QueueDestroy(&(obj->queue2));
    free(obj);
    obj = NULL;
}

五.用两个栈实现队列(232. 用栈实现队列 - 力扣(Leetcode))

思路图解:

 题解代码:

typedef struct 
{
    ST PushStack;
    ST PopStack;   
} MyQueue;


MyQueue* myQueueCreate()    //队列初始化接口
{
    MyQueue* tem = (MyQueue *)malloc(sizeof(MyQueue));
    if(NULL == tem)
    {
        perror("malloc failed:");
        exit(-1);
    }
    StackInit(&(tem->PushStack));
    StackInit(&(tem->PopStack));
    return tem;
}

void myQueuePush(MyQueue* obj, int x) 
{
    StackPush(&(obj->PushStack),x);
    //数据入队
}

int myQueuePop(MyQueue* obj) 
{
    assert(!StackEmpty(&(obj->PushStack)) || !StackEmpty(&(obj->PopStack)));
    //数据出队前保证队列不为空
    if(StackEmpty(&(obj->PopStack)))  //PopStack为空则要将PushStack数据转移进来
    {
        while(!StackEmpty(&(obj->PushStack)))
        {
            StackPush(&(obj->PopStack),StackTop(&(obj->PushStack)));
            //将PushStack栈顶数据压入PopStack栈中
            StackPop(&(obj->PushStack));
            //完成数据转移后弹出PushStack的栈顶数据
        }
    }
    int ret = StackTop(&(obj->PopStack)); //记录队头元素
    StackPop(&(obj->PopStack));           //元素出队
    return ret;                           //返回队头元素
}

int myQueuePeek(MyQueue* obj) 
{
    assert(!StackEmpty(&(obj->PushStack)) || !StackEmpty(&(obj->PopStack)));
    //保证队列不为空
    if(StackEmpty(&(obj->PopStack))) //PopStack为空则要将PushStack数据转移进来
    {
        while(!StackEmpty(&(obj->PushStack)))
        {
            StackPush(&(obj->PopStack),StackTop(&(obj->PushStack)));
            //将PushStack栈顶数据压入PopStack栈中
            StackPop(&(obj->PushStack));
            //完成数据转移后弹出PushStack的栈顶数据
        }
    }
    return StackTop(&(obj->PopStack));
    //返回PopStack栈顶元素作为队列队头元素
}

bool myQueueEmpty(MyQueue* obj) 
{
    return StackEmpty(&(obj->PopStack))  && StackEmpty(&(obj->PushStack));
    //判断队列是否为空
}

void myQueueFree(MyQueue* obj) 
{
    StackDestroy(&(obj->PopStack));
    StackDestroy(&(obj->PushStack));
    free(obj);
    obj = NULL;
}
  • 题解中我们运用的是本期第一节中实现好的栈

 

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

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

相关文章

Talk | 清华大学交叉信息研究院助理教授杜韬:利用计算方法探究流固耦合

本期为TechBeat人工智能社区第474期线上Talk&#xff01; 北京时间2月15日(周三)20:00&#xff0c;清华大学交叉信息研究院助理教授——杜韬的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “利用计算方法探究流固耦合”&#xff0c;届时将介绍流固…

Windows10使用-处理IE自动跳转至Edge

文章目录 前言一、调整Edge二、调整Internet选项三、搜索栏的恢复总结前言 微软官方宣布,自2023年2月14日永久停止支持Internet Explorer 11浏览器。后期点击IE 图标将会自动跳转到Edge界面。对于一些网站,可能需要使用IE模式才能正常使用,这时候就需要做相应的调整,才能够…

做外贸怎么找客户

现在国内贸易内卷非常严重&#xff0c;很多商家都转向海外市场了&#xff0c;总结而言&#xff0c;目前所有做外贸的人&#xff0c;核心的点就是要找到重点意向客户&#xff0c;今天就和大家分享一下目前市面上外贸找客户的几种方法。主动出击式开发外贸客户1、参加展会找外贸客…

使用拦截器实现登录状态检测(以及在注册拦截器类时要使用ioc中的拦截器类)

拦截器 preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行&#xff0c;用来进行一些前置初始化操作或是对当前请求做预处理&#xff0c;也可以进行一些判断来决定请求是否…

C++类和对象-继承多态

继承 继承是面向对象三大特性之一 定义类时&#xff0c;下级别的成员除了拥有上一级的共性&#xff0c;还有自己的特性&#xff0c;就可以考虑使用继承的技术&#xff0c;减少代码的重复 继承的基本语法 语法&#xff1a;class 子类 : 继承方式 父类 子类也被成为派生类父类…

FreeRTOS队列

队列简介队列是一种任务到任务&#xff0c;任务到中断&#xff0c;中断到任务数据交流得一种机制。在队列中可以存储数量有限&#xff0c;大小固定得多个数据&#xff0c;队列中的每一个数据叫做队列项目&#xff0c;队列能够存储队列项目的最大数量称为队列的长度&#xff0c;…

生活中常见标识

一维码 一维条码即指条码条和空的排列规则,常用的一维码的码制包括:EAN码、39码、交叉25码、UPC码、128码、93码,ISBN码,及Codabar(库德巴码)等。 常见场景: - 快销品- 常用五金- 通讯设备其中蕴含的信息就是条码的****一串数字 二维码 常见场景

性能测试中,我遇到的8个常见问题总结

性能压测中我们需要明白以下几点&#xff1a; 1、好的开始是成功的一半&#xff0c;前期的准备非常重要&#xff1b; 2、过程中&#xff0c;关注每个细节&#xff0c;多个维度监控&#xff1b; 3、在调优中多积累经验&#xff1b; 4、对结果负责&#xff0c;测试报告要清晰…

Redis实战案例

文章目录1、SpringBoot整合Redis1.1、新建项目1.2、接口编写1.3、集成Redis1.3、测试1.4、序列化问题2、Redis实现分布式缓存2.1、背景介绍2.2、代码编写2.3、缓存改造2.4、小结3、RedisAOP自定义注解&#xff0c;优雅实现分布式缓存3.1、自定义注解3.2、AOP切面类3.3、测试3.4…

跳跃游戏II-力扣45-java 动态规划

一、题目描述给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处:0 < j < nums[i] i j < n返回到达 nums[n - …

手撸一个Table组件(Table组件不过如此)

一、前言 手写Table组件这个文章我一直都想写&#xff0c;今天终于得空来写它了。小编认为Table组件是组件库里"较为复杂"的一个组件&#xff0c;因为它的扩展性非常强&#xff0c;并且它的基础样式如何去写都非常考究&#xff0c;那么今天我就带大家来实现一个基础…

SpringBoot整合Spring Security过滤器链加载执行流程源码分析

文章目录1.引言2.Spring Security过滤器链加载1.2.注册名为 springSecurityFilterChain的过滤器2、查看 DelegatingFilterProxy类3.查看 FilterChainProxy类3.1 查看 doFilterInternal方法。3.2 查看 getFilters方法。4 查看 SecurityFilterChain接口5 查看 SpringBootWebSecur…

JDK8增加的特性

Java知识点总结&#xff1a;想看的可以从这里进入 目录13、JDK8增加的特性13.1、Lambda表达式13.2、方法的引用13.3、时间处理类13.4、接口增加方法13.5、注解新增13.6、Optional类13.7、Stream13、JDK8增加的特性 13.1、Lambda表达式 Lambda表达式和方法的引用 13.2、方法的…

Java8的Optional类的使用 和 Stream流式操作

Java知识点总结&#xff1a;想看的可以从这里进入 目录13.6、Optional类13.7、Stream13.7.1、Stream创建13.7.2、中间操作1、筛选2、切片3、映射4、排序13.7.3、终止操作1、遍历2、聚合3、匹配查找4、归约5、收集归集统计分组字符串拼接归约13.6、Optional类 Optional类是一个…

【已解决】异常断电文件损坏clickhouse启动不了:filesystem error Structure needs cleaning

问题 办公室有一台二手服务器&#xff0c;作为平时开发测试使用。由于机器没放在机房&#xff0c;会偶发断电异常断电后&#xff0c;文件系统是有出问题的可能的&#xff0c;尤其是一些不断在读写合并的文件春节后&#xff0c;发现clickhouse启动不了&#xff0c;使用systemct…

【Nginx】【一】Nginx简介

Nginx简介 背景介绍 Nginx&#xff08;“engine x”&#xff09;一个具有高性能的【HTTP】和【反向代理】的【WEB服务器】&#xff0c;同时也是一个【POP3/SMTP/IMAP代理服务器】&#xff0c;是由伊戈尔赛索耶夫(俄罗斯人)使用C语言编写的&#xff0c;Nginx的第一个版本是200…

nginx的平滑升级、反向代理负载均衡

文章目录一、负载均衡介绍二、nginx的平滑升级和版本回滚1.平滑升级2.版本回滚3.本实验纯代码过程三、反向代理负载均衡总结一、负载均衡介绍 四层负载均衡 所谓四层负载均衡是指OSI七层模型中的传输层, 那么传输层Nginx已经支持TCP/IP的控制, 所以只需要对客户端的请求进行TCP…

RPC编程:RPC概述和架构演变

RPC编程系列文章第一篇一&#xff1a;引言1&#xff1a;本系列文章的目标2&#xff1a;RPC的概念二&#xff1a;架构的演变过程1&#xff1a;单体架构1)&#xff1a;概念2)&#xff1a;特点3)&#xff1a;优缺点2&#xff1a;单体架构水平扩展1)&#xff1a;水平拓展的含义2)&a…

提到数字化,你想到哪些关键词

我们的生活中已经充满了数据&#xff0c;各种岗位例如运营、市场、营销上也都喜欢在职位要求加上一条利用数据、亦或是懂得数据分析。事实上&#xff0c;数据已经成为了构建现代社会的基本生产要素&#xff0c;并且因为不受自然环境的限制&#xff0c;已经成为了人们对未来社会…

【论文笔记】Manhattan-SDF==ZJU==CVPR‘2022 Oral

Neural 3D Scene Reconstruction with the Manhattan-world Assumption 本文工作&#xff1a;基于曼哈顿世界假设&#xff0c;重建室内场景三维模型。 1.1 曼哈顿世界假设 参考阅读文献&#xff1a;Structure-SLAM: Low-Drift Monocular SLAM in Indoor EnvironmentsIEEE IR…