数据结构学习之路--玩转队列的内核知识(附C源码)

news2024/12/28 6:16:41

   嗨嗨大家~我又来啦!今天为大家带来的是与队列相关的知识。我们马上进入知识的海洋~


目录

前言

一、队列 

1 队列的概念 

2 队列的实现 

2.1 队列的定义 

2.2 队列的初始化 

2.3 队列的判空 

2.4 入队 

2.5 出队 

2.6 取队头元素 

2.7 取队尾元素 

2.8 取有效元素个数(队列大小)

2.9 队列的销毁 

二、源代码 

三、栈与队列的经典题 

 1 有效的括号

2 用队列实现栈 

3 用栈实现队列 

4 设计循环队列 


前言

   说起队列,不妨来想象一下,在我们的生活中总是存在一种现象:排队。它就可以看作是队列,比如:我们在排队打饭的时候,先排队的人先打饭,打完饭后便出队了,也就是说最先排队的人最先出队,最后排队的人最后出队。有了此依据,我们来对队列作出详细讲解:

一、队列 

首先根据下图直观的了解队列的相关内容:

1 队列的概念 

   队列是一种特殊的线性表,特性是先进先出,即First In First Out(FIFO)。最先加入的元素最先取出,最后加入的元素最后取出。队列有头部和尾部,队列头部称为队头(首),队列尾部称为队尾,队列内的元素从队头到队尾的顺序符合加入队列的顺序。它只允许在一端进行插入(入队),在另一端删除的线性表(出队)。文字描述未免过于死板,为了更好的帮助大家理解,附以下图解:

左边为队头,右边为队尾。将数字 1  到 7 依次入队之后,此时队首元素是 1 ,队尾元素是 7 ,第一个出队的元素是 1 。 

2 队列的实现 

队列一般需要这样几个功能:

  • 初始化队列
  • 判断队列是否为空
  • 入队
  • 出队
  • 取队头元素
  • 取队尾元素
  • 取有效元素个数(大小)
  • 队列的销毁

2.1 队列的定义 

//定义
typedef int QDataType;
 
//链式队列结点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;
 
//链式队列
typedef struct Queue
{
	QNode* head;//队列的头
	QNode* tail;//队列的尾
}Queue;

2.2 队列的初始化 

//初始化
void QueueInit(Queue* pq)
{
	//判空
	assert(pq);
 
	//不带哨兵位(即不带头结点)
	pq->head = pq->tail = NULL;
}

这里实现的是不带头结点的链式队列,初始时 front 和 rear 都指向NULL。 

2.3 队列的判空 

//判空
bool QueueEmpty(Queue* pq)
{
	//判空
	assert(pq);
 
	//看队头元素是否为NULL
	return pq->head == NULL;
}

判断队列是否为空,仅需要看队头元素是否为NULL。 

2.4 入队 

//入队
void QueuePush(Queue* pq, QDataType x)
{
	//判空
	assert(pq);
 
	//创建新结点newnode
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
 
	//判空
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		exit(-1);
	}
 
	newnode->data = x;
	newnode->next = NULL;
 
	//链表为空
	if (pq->tail == NULL)
	{
		//在空队列中插入第一个元素
		//修改队头队尾指针
		pq->head = pq->tail = newnode;
	}
	else
	{
		//链表不为空
		pq->tail->next = newnode;//新结点插入到tail结点之后
		pq->tail = newnode;//修改tail指针
	}
}

在入队之前,需要调用 malloc 函数开辟一个新结点 newnode。对于不带头结点的情况,第一个元素入队时要特殊处理。由于一开始这两个指针都是指向NULL的,因此插入第一个元素时对这两个指针都要进行修改。 

2.5 出队 

//出队
void QueuePop(Queue* pq)
{
	//判空
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//只含一个结点
	if (pq->head->next == NULL)
	{
		free(pq->head);//释放最后一个结点
		pq->head = pq->tail = NULL;//将队头与队尾指针都置空
	}
	else//含多个结点
	{
		QNode* next = pq->head->next;//next为队头结点的下一个结点
		free(pq->head);//释放队头结点
		pq->head = next;//修改头指针
	}
}

在出队之前,首先需要判断队列是否为空,若队列为空,则无法进行出队操作。当只剩下最后一个元素未出队时需特殊处理。首先需要调用 free函数释放首元素,然后将队头指针与队尾指针都置为 NULL。当还剩多个元素时,首先找到队头结点的下一个结点,然后调用 free函数释放掉队头结点,最后将 队头指针head 指向 next,更新队头元素。 

2.6 取队头元素 

//取队头元素
QDataType QueueFront(Queue* pq)
{
	//判空
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//取队头元素
	return pq->head->data;
}

在取队头元素之前,首选需要调用函数 QueueEmpty(pq) 判断队列是否为空,若为空,则无法取队头元素。若队列不为空,则取队头元素。 

2.7 取队尾元素 

//取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//取队尾元素
	return pq->tail->data;
}

在取队尾元素之前,首选需要调用函数 QueueEmpty(pq) 判断队列是否为空,若为空,则无法取队尾元素。若队列不为空,则取队尾元素。 

2.8 取有效元素个数(队列大小)

//取有效元素个数(队列大小)
int QueueSize(Queue* pq)
{
	//判空
	assert(pq);
 
	//设置指针变量cur,用于遍历队列
	QNode* cur = pq->head;
	int size = 0;
 
	//遍历队列
	while (cur)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

在求队列的元素个数时,首先设置指针变量 cur,并使其指向队头元素,然后设置一个变量 size,用于统计元素个数。让指针变量 cur进入 while循环遍历整个队列,每遍历一个元素, size就自增1,直到 cur走到队尾,则跳出循环,并返回 size大小。 

2.9 队列的销毁 

//销毁
void QueueDestory(Queue* pq)
{
	//判空
	assert(pq);
 
	//设置指针变量cur,用于遍历队列
	QNode* cur = pq->head;
 
	//遍历队列
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
 
	//将队头指针,队尾指针均置为空
	pq->head = pq->tail = NULL;
}

首先设置指针变量 cur,并使其指向队头元素,然后让指针变量 cur进入 while循环遍历整个队列,每遍历一个元素,就将其前一个元素释放掉,直到 cur走到队尾,则跳出循环。在销毁整个链表之后要将队头指针与队尾指针均置为 NULL。 

二、源代码 

Queue.h

#pragma once
 
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
 
typedef int QDataType;
 
//链式队列结点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;
 
//链式队列
typedef struct Queue
{
	QNode* head;//队列的头
	QNode* tail;//队列的尾
}Queue;
 
 
//初始化
void QueueInit(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);
 
//入队
void QueuePush(Queue* pq, QDataType x);
 
//出队
void QueuePop(Queue* pq);
 
//取队头元素
QDataType QueueFront(Queue* pq);
 
//取队尾元素
QDataType QueueBack(Queue* pq);
 
//元素个数(队列大小)
int QueueSize(Queue* pq);

//销毁
void QueueDestory(Queue* pq);
Queue.c

#include"Queue.h"

​​//初始化
void QueueInit(Queue* pq)
{
	//判空
	assert(pq);
 
	//不带哨兵位(即不带头结点)
	pq->head = pq->tail = NULL;
}

​​//判空
bool QueueEmpty(Queue* pq)
{
	//判空
	assert(pq);
 
	//看队头元素是否为NULL
	return pq->head == NULL;
}

​​//入队
void QueuePush(Queue* pq, QDataType x)
{
	//判空
	assert(pq);
 
	//创建新结点newnode
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
 
	//判空
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		exit(-1);
	}
 
	newnode->data = x;
	newnode->next = NULL;
 
	//链表为空
	if (pq->tail == NULL)
	{
		//在空队列中插入第一个元素
		//修改队头队尾指针
		pq->head = pq->tail = newnode;
	}
	else
	{
		//链表不为空
		pq->tail->next = newnode;//新结点插入到tail结点之后
		pq->tail = newnode;//修改tail指针
	}
}

​​//出队
void QueuePop(Queue* pq)
{
	//判空
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//只含一个结点
	if (pq->head->next == NULL)
	{
		free(pq->head);//释放最后一个结点
		pq->head = pq->tail = NULL;//将队头与队尾指针都置空
	}
	else//含多个结点
	{
		QNode* next = pq->head->next;//next为队头结点的下一个结点
		free(pq->head);//释放队头结点
		pq->head = next;//修改头指针
	}
}

​//取队头元素
QDataType QueueFront(Queue* pq)
{
	//判空
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//取队头元素
	return pq->head->data;
}

​​//取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
 
	//判断队列是否为空
	assert(!QueueEmpty(pq));
 
	//取队尾元素
	return pq->tail->data;
}

​​//取有效元素个数(队列大小)
int QueueSize(Queue* pq)
{
	//判空
	assert(pq);
 
	//设置指针变量cur,用于遍历队列
	QNode* cur = pq->head;
	int size = 0;
 
	//遍历队列
	while (cur)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

​​//销毁
void QueueDestory(Queue* pq)
{
	//判空
	assert(pq);
 
	//设置指针变量cur,用于遍历队列
	QNode* cur = pq->head;
 
	//遍历队列
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
 
	//将队头指针,队尾指针均置为空
	pq->head = pq->tail = NULL;
}
​
test.c
 
#include"Queue.h"
 
void TestQueue()
{
	Queue q;
 
	//初始化
	QueueInit(&q);
 
	//入队
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
    QueuePush(&q, 6);
    QueuePush(&q, 7);
 
	//出队
	while (!QueueEmpty(&q))
	{
		//取对头元素
		printf("%d ",QueueFront(&q));
		//出队
		QueuePop(&q);
	}
	printf("\n");
 
	//销毁
	QueueDestory(&q);
}
 
int main()
{
	test();
 
	return 0;
}

三、栈与队列的经典题 

 1 有效的括号

题目描述:

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合
  2. 左括号必须以正确的顺序闭合

分析:

   我们可以把左括号依次压入栈中,越往后被压入的左括号越先被弹出栈进行匹配。每出现一个右括号,就”消耗”一个左括号进行匹配检查,这个过程对应出栈操作。扫描一连串括号的过程中若发现下列情况都说明括号序列不合法,终止操作。

注意:

  • 弹出栈的左括号与刚刚遇到要检查的右括号不匹配;
  • 扫描到右括号时发现栈空了(右括号单身);
  • 处理完所有括号后,栈非空(右括号单身)。

代码实现: 

bool isValid(char * s)
{
    Stack st;
    StackInit(&st);
    while(*s)
    {
        //左括号入栈,右括号找最近的左括号匹配
        if(*s == '[' || *s == '(' || *s == '{')
        {
            StackPush(&st, *s);
            s++;
        }
        else
        {
            if(StackEmpty(&st))//只有右括号的情况
            {
                StackDestroy(&st);//销毁
                return false;
            }
            char top = StackTop(&st);
            //不匹配的情况
            if ( (top == '[' && *s != ']') 
            || (top == '(' && *s != ')')
            || (top == '{' && *s != '}') ) 
            {
                StackDestroy(&st);
                return false;
            }
            else //匹配的情况
            {
                StackPop(&st);
                s++;
            }
        }
    }
 
    //如果最后栈内为空才说明是匹配的(防止最后栈内还剩下左括号的情况)
    bool ret = StackEmpty(&st);
    StackDestroy(&st);
 
    return ret;
 
    //特别注意:在return之前需要先把栈销毁掉
}

2 用队列实现栈 

题目描述:

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push,top,pop和empty)。

实现MyStack类:

  1. void push(int x):将元素x压入栈顶
  2. void pop():移除并返回栈顶元素
  3. int top():返回栈顶元素
  4. boolean empty():如果栈是空的,返回true;否则,返回false

分析:

   队列 queue1保存原始输入数据,队列 queue2作为临时队列缓存数据。当进行 stack_pop操作时,先将 queue1里除最后一个元素外全部出队,并将出队的数据保存在临时队列queue2里,然后保存 queue1的最后一个元素,最后再将 queue2里的全部元素出队,且出队的元素重新放进 queue1里,返回保存的 queue1最后的元素。

代码实现: 

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
 
typedef int QDataType;
 
//链式队列结点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;
 
//链式队列
typedef struct Queue
{
	QNode* head;//队列的头
	QNode* tail;//队列的尾
}Queue;
 
 
//初始化
void QueueInit(Queue* pq);
 
//销毁
void QueueDestory(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);
 
 
/**********用队列实现栈**********/
 
 
//构造一个包含两个队列的栈
typedef struct
{
	Queue q1;
	Queue q2;
}MyStack;
 
//初始化栈
MyStack* myStackCreate()
{
	//调用malloc为栈开辟内存空间
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
 
	//初始化两个队列
	QueueInit(&obj->q1);
	QueueInit(&obj->q2);
 
	return obj;
}
 
//入栈
void myStackPush(MyStack* obj, int x)
{
	//判空
	assert(obj);
 
	//往不为空的队列插入元素,若两个队列均为空,插入其中一个即可
	if (!QueueEmpty(&obj->q1))
	{
		QueuePush(&obj->q1,x);
    }
    else
    {
		QueuePush(&obj->q2,x);
    }

//出栈
int myStackPop(MyStack* obj)
{
    //判空
    assert(obj);

    //假设q1队列为空,q2队列不为空
    Queue* emptyQ = &obj->q1;
    Queue* nonEmptyQ = &obj->q2;

    //若q1队列不为空,则将q2队列设为空,q1队列设为非空
    if (!QueueEmpty(&obj->q1))
    {
        emptyQ = &obj->q2;
        nonEmptyQ = &obj->q1;
    }

    //把非空队列的数据导入到空队列,也就是将非空队列的前n-1个数据导入至另一个空队列
    while (QueueSize(nonEmptyQ) > 1)
    {
        QueuePush(emptyQ,QueueFront(nonEmptyQ));//取非空队列对头的数据插入到空队列中去
        QueuePop(nonEmptyQ);//出队
    }

    int top = QueueFront(nonEmptyQ);//取非空队列的队头元素
    QueuePop(nonEmptyQ);//出队

    return top;
}

//取栈顶元素
int myStackTop(MyStack* obj)
{

    //判空
    assert(obj);

    //若q1队列不为空,则取q1队尾元素
    if (!QueueEmpty(&obj->q1))
    {
         return QueueBack(&obj->q1);
    }
    else
    {
         //若q2队列不为空,则取q2队尾元素
         return QueueBack(&obj->q2);
    }
}

//判断栈是否为空
bool myStackEmpty(MyStack* obj)
{

    //判空
    assert(obj);

    //只有当两个队列均为空时,才表示栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

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

    //判空
    assert(obj);

    //销毁队列q1和q2
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);

    //释放栈
    free(obj);
}
    
    
   

3 用栈实现队列 

题目描述:

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push,pop,peek,empty)。

实现MyQueue类:

  1. void push(int x):将元素x推到队列的末尾
  2. int pop():从队列的开头移除并返回元素
  3. int peek():返回队列开头的元素
  4. boolean empty():如果队列为空,返回true;否则,返回false

分析:

   用两个栈实现一个队列,设置其中一个栈 pushst专门入数据,另一个栈 popst专门出数据。若要入队,则进栈 pushst;若要出队,首先看 popst栈是否为空,如果为空,就先把栈 pushst的数据转移过来,然后出队,如果不为空,则直接出栈 popst的数据。

代码实现: 

typedef struct 
{
    Stack pushST;
    Stack popST;
} MyQueue;
 
MyQueue* myQueueCreate() 
{
    MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&q->pushST);
    StackInit(&q->popST);
 
    return q;
}
 
void myQueuePush(MyQueue* obj, int x)
{
    //不管栈内有没有数据,只要是入队操作就向Push栈入数据即可
    StackPush(&obj->pushST, x);
}
 
//获取队头数据
int myQueuePeek(MyQueue* obj) 
{
    //如果pop栈为空,先把push栈数据导入pop栈
    if(StackEmpty(&obj->popST))
    {
        while(!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    
    return StackTop(&obj->popST);
}
 
//出队
int myQueuePop(MyQueue* obj) 
{
 
    //如果pop栈为空,先把push栈数据导入pop栈
    /*if(StackEmpty(&obj->popST))
    {
        while(!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    */
 
    //复用
    int top = myQueuePeek(obj);//易错点:不能写&obj->popST,因为该传入队列的指针
    StackPop(&obj->popST);
    return top;
}

bool myQueueEmpty(MyQueue* obj) 
{
    //push栈和pop栈同时为空,队列才为空
    return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj)
{
    StackDestroy(&obj->pushST);
    StackDestroy(&obj->popST);
    free(obj);
}

4 设计循环队列 

 题目描述:

设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为"环形缓冲器"。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作

  1. MyCircularQueue(k):构造器,设置队列长度为k
  2. Front:从队首获取元素。如果队列为空,返回-1
  3. Rear:获取队尾元素。如果队列为空,返回-1
  4. enQueue(value):向循环队列插入一个元素,如果成功插入则返回真
  5. deQueue():从循环队列中删除一个元素。如果成功删除则返回真
  6. isEmpty():检查循环队列是否为空
  7. isFull():检查循环队列是否已满

分析:

  • 采用数组或者链表都可以,但是数组缓存利用率更高,所以这里主要采用数组的方式。
  • 用模运算将存储空间在逻辑上变成“环状”。当发现rear指针要指向MaxSize时,不应该让它指向MaxSize而是应该让它指向数组下标为0的位置。
  • 队列判空:Q.rear==Q.front;队列判满:队尾指针的下一个位置是对头,即(Q.rear+1)%MaxSize==Q.front

代码实现: 

//循环队列的定义
typedef struct
{
	int* a;//动态开辟数组
	int k;//当前有效元素个数
	int head;//队头
	int tail;//队尾
}MyCircularQueue;
 
//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
	//为队列开辟一块动态内存空间
	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
 
	//为数组开辟一个包含(k+1)个元素的内存空间
	obj->a = (int*)malloc(sizeof(int) * (k + 1));
 
	//队头,队尾起始都指向数组下标为0的位置
	obj->head = obj->tail = 0;
 
	//当前有效元素个数设置为k个
	obj->k = k;
 
	return obj;
}
 
//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
	//判空
	assert(obj);
 
	return obj->head == obj->tail;
}
 
//判断队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
	//判空
	assert(obj);
 
	int next = obj->tail + 1;
 
	//当tail指向数组最后一个下标的下一个位置时,则将tail指向数组下标为0的位置
	if (next == obj->k + 1)
	{
		next = 0;
	}
 
	//队尾指针的下一个位置是对头,则表示队列已满
	return next == obj->head;
}
 
//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
	//判空
	assert(obj);
 
	//检查队列是否已满
	if (myCircularQueueIsFull(obj))
	{
		return false;
	}
 
	//未满则将value插入队尾
	obj->a[obj->tail] = value;
    obj->tail++;//队尾指针后移

    //当tail指向数组最后一个下标的下一个位置时,则将tail指向数组下标为0的位置
    if (obj->tail == obj->k + 1)
    {
        obj->tail = 0;
    }
    //obj->tail%=(k+1);
 
    return true;
}

//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    
    //判空
    assert(obj);

    //检查队列是否为空
    if (MyCircularQueueIsEmpty(obj))
    {
        return false;
    }

    //若不空,则队头指针后移
    ++obj->head;
  
    //当head指向数组最后一个下标的下一个位置时,则将head指向数组下标为0的位置
    if (obj->head == obj->k + 1)
    {
        obj->head = 0;
    }
    return true;
}

//取队头元素
int myCircularQueueFront(MyCircularQueue* obj)
{

     //判空
     assert(obj);

     //检查队列是否为空
     if (myCircularQueueIsEmpty(obj))
     {
         return -1;
     }

     //若不为空,则取队头元素
     return obj->a[obj->head]};
    
//取队尾元素
int myCircularQueueRear(MyCircularQueue* obj)
{
     //判空
     assert(obj);

     //检查队列是否为空
     if (myCircularQueueIsEmpty(obj))
     {
          return -1;
     }

     //因为tail指向队尾元素的下一个位置,所以要取tail前一位置的下标
     int pre = obj->tail - 1;

     //若tail在数组起始位置,则前一位置的下标为数组的末尾位置
     if (obj->tail == 0)
     {
          pre = obj->k;
     }
     //int pre = obj->tail - 1 + obj->k + 1;
     //pre %= (obj->k+1);

     //取队尾元素
     return obj->a[pre];
}

//销毁
void myCircularQueueFree(MyCircularQueue* obj)
{
     //判空
     assert(obj);

     //释放动态开辟的数组
     free(obj->a);

     //释放队列
     free(obj);
}

    本期的分享已经接近尾声,内容有些多,大家耐心些哈~重点还是要关注栈和队列的四道经典例题,它们能让我们更好的掌握核心知识。或许你在看的时候感觉有些难度,不要烦躁,更不要焦虑,没有谁能一蹴而就。你需要做的便是不断地重复、重复!!最能帮助你的那双手,就长在你的胳膊上。好啦,如果这篇文章对你们有帮助,记得留下三连加支持哈~你们的支持是我创作的最大动力!诸君加油,不负自己。那我们下期再会啦!

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

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

相关文章

CountDownLatch倒计时器源码解读与使用

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. CountDownLatch有什么用 3. CountDownLatch底层原理 3.1. count…

React Router 6 + Ant Design:构建基于角色的动态路由和菜单

要根据用户的角色生成不同的路由菜单并实现权限控制,你可以采取以下步骤: 定义路由配置 首先,你需要定义一个包含所有可能路由的配置文件,例如: const routes [{path: /dashboard,element: <DashboardPage />,roles: [admin, manager, user]},{path: /users,element:…

Unity场景光照数据Light data asset

首先描述一下遇到的问题&#xff0c;游戏运行过程中切换场景之后发现模型接收的光照不对。 Unity编辑模式下正常显示&#xff1a; 运行模式下从其他场景切入之后显示异常&#xff1a; 排查了灯光参数和环境光以及着色器都没发现异常。 根据ChatGPT的回答&#xff0c;问题可能…

15.Nacos服务分级存储模型

服务跨集群调用问题&#xff1a; 服务调用尽可能的选择本地集群的服务&#xff0c;跨集群调用延迟较高。 本地集群不可访问的情况下&#xff0c;再去访问其他集群。 如何配置集群的实例属性&#xff1a; spring: cloud:nacos:server-addr: localhost:8848 #nacos服务端地址d…

[lesson48]同名覆盖引发的问题

同名覆盖引发的问题 父子间的赋值兼容 子类对象可以当做父类对象使用(兼容性) 子类对象可以直接赋值给父类对象(<font color>兼容性)子类对象可以直接初始化父类对象父类指针可以直接指向子类对象父类引用可以直接引用子类对象 当使用父类指针(引用)指向子类对象时 子类…

Vitis HLS 学习笔记--优化指令-BIND_OP_STORAGE

目录 1. BIND_OP_STORAGE 概述 1.1 BIND_OP 1.2 BIND_STORAGE 2. 语法解析 2.1 BIND_OP 2.2 BIND_OP 用法示例 2.3 BIND_STORAGE 2.4 BIND_STORAGE 示例 3. 实例演示 4. 总结 1. BIND_OP_STORAGE 概述 BIND_OP_STORAGE 其实是两个优化指令的合称&#xff1a;BIND_OP…

数栈+AI:数栈V6.2创新发布,让数据开发更智能

近日&#xff0c;以“DataAI&#xff0c;构建新质生产力”为主题的袋鼠云春季发布会圆满落幕&#xff0c;大会带来了一系列“AI”的数字化产品与最新行业沉淀&#xff0c;旨在将数据与AI紧密结合&#xff0c;打破传统的生产力边界&#xff0c;赋能企业实现更高质量、更高效率的…

究竟该怎么寄快递才能安全无误的送到手中呢?

最近&#xff0c;小编上班了发现有同事在吐槽快递送到手中的时间很晚了&#xff0c;比预计的时间差了很多&#xff0c;并且产品也有不同程度的损坏。这就让我们很是恼火了&#xff0c;但是细细研究后才发现有一部分的原因竟然是我们的原因才导致的寄快递出现了很多纰漏。 首先…

echart实现排名列表

function createHorizontalBarChart(chartId, data) {if (typeof echarts undefined) {console.error(请先引入 ECharts 库);return;}// 初始化echarts实例var myChart echarts.init(document.getElementById(chartId));// 对数据按照 value 进行降序排序var sortedData dat…

hexo配置教程、主题使用及涉及的技术学习

一、背景 最近,一直想做一个属于自己的网站.可以从零开始搭建一个网站,顺便可以把日常中学到的技术用于实战,还可以顺便记录自己的所思所感,记录成长的过程. 方案 一开始的方案是从零开始,模仿常见个人博客的设计,基于vueSpringbootMySQL的去实现网站. 新建项目之后,发现vu…

Git 新手快速入门教程

一、什么是 Git 1. 何为版本控制 版本控制是一种记录文件变化的系统&#xff0c;可以跟踪文件的修改历史&#xff0c;并允许用户在不同版本之间进行比较、恢复或合并。它主要用于软件开发过程中管理代码的变更&#xff0c;但也可以应用于任何需要跟踪文件变更的场景。 版本控…

【问题】java查询MySQL时,mysql查询tinyint类型 的数据时,0会被转为false,1或以上会转为true

在做接口测试的数据库断言时&#xff0c;发现type字段断言总是失败&#xff0c;期望是0&#xff0c;打印数据库实际值是false 查看数据库格式&#xff1a; 解决&#xff1a; 在数据库url上&#xff0c;添加&#xff1a;&tinyInt1isBitfalse ——可解决java查询MySQL时&a…

密钥密码学(二)

原文&#xff1a;annas-archive.org/md5/b5abcf9a07e32fc6f42b907f001224a1 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;可变长度分数化 本章涵盖 基于摩尔斯电码的密码 混合字母和双字母 可变长度二进制码字 基于文本压缩的密码 本章涵盖…

【JavaWeb】Day52.Mybatis动态SQL(二)

动态SQL-foreach 案例&#xff1a;批量删除员工功能 SQL语句&#xff1a; delete from emp where id in (1,2,3); Mapper接口&#xff1a; ~~~java Mapper public interface EmpMapper {//批量删除public void deleteByIds(List<Integer> ids); } ~~~ XML映射文件&am…

C++学习进阶版(一):用C++写简单的状态机实现

目录 一、基础知识 1、状态机 2、四大要素 3、描述方式 4、设计步骤 5、实现过程中需注意 &#xff08;1&#xff09; 状态定义 &#xff08;2&#xff09; 状态转换规则 &#xff08;3&#xff09; 输入处理 &#xff08;4&#xff09; 状态机的封装 &#xff08;5…

串联超前及对应matlab实现

串联超前校正它的本质是利用相角超前的特性提高系统的相角裕度。传递函数为&#xff1a;下面将以一个实际的例子&#xff0c;使用matlab脚本&#xff0c;实现其校正后的相位裕度≥60。

mysql基础2——字段类型

整数类型 需要考虑存储空间和可靠性的平衡 浮点类型 浮点数类型不精准 将十进制数转换为二进制数存储 浮点数类型&#xff1a;float double real(默认是&#xff0c;double ) 如果需要将real设定为float &#xff0c;那么通过以下语句实现 set sql_mode "real_as…

go语言实现心跳机制样例

1、服务端代码&#xff1a; package mainimport ("fmt""net" )func handleClient(conn net.Conn) {defer conn.Close()fmt.Println("Client connected:", conn.RemoteAddr())// 读取客户端的数据buffer : make([]byte, 1024)for {n, err : conn…

AOC/AGON亮相2024上海国际酒店及商业空间博览会,共话电竞酒店产业新趋势!

摘要&#xff1a;行业头部品牌共聚上海&#xff0c;共话电竞酒店市场未来&#xff01; 春景熙熙&#xff0c;相逢自有时&#xff0c;3月26日-29日&#xff0c;2024上海国际酒店及商业空间博览会以及第二届全国电竞酒店投资交流论坛在上海新国际博览中心圆满帷幕。连续五年蝉联…

腾讯云轻量2核4G5M服务器优惠价格165元1年,2024年多配置报价单

腾讯云轻量2核4G5M服务器优惠价格165元1年。腾讯云服务器价格表2024年最新价格&#xff0c;轻量2核2G3M服务器61元一年、2核2G4M服务器99元1年&#xff0c;三年560元、2核4G5M服务器165元一年、3年900元、轻量4核8M12M服务器646元15个月、4核16G10M配置32元1个月、8核32G配置11…