栈和队列经典面试题

目录

一、括号匹配问题

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

题目

思路

完整代码

二、用队列实现栈

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

题目

思路

代码实现

构造一个栈

用队列实现栈的接口

第一个接口:创建并初始化栈

第二个接口:入栈

第三个接口:出栈

第四个接口:取栈顶元素

第五个接口:判断栈是否为空

第六个接口:销毁栈

总代码

三、用栈实现队列

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

题目

 思路

第一个接口:创建并初始化队列

第二个接口:入队

第三个接口:出队

第四个接口:取队头元素

 第五个接口:判断队列是否为空

第六个接口:销毁队列

总代码

四、设计循环队列

622. 设计循环队列 - 力扣(LeetCode)

题目

思路

第一个接口:定义结构体

第二个接口:构造/初始化循环队列

第三个接口:向循环队列插入一个元素

第四个接口:从循环队列中删除一个元素

第五个接口:从队首获取元素

第六个接口:获取队尾元素

第七个接口:检查循环队列是否为空

第八个接口:检查循环队列是否已满

第九个接口:释放空间

总代码


一、括号匹配问题

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

题目

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

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

注意空字符串可被认为是有效字符串。

思路

做题前,得先明确解题方案是啥,此题用栈的思想去解决是较为方便的,栈明确指出后进先出。我们可以这样设定:

  1. 遇到左括号“ ( ”、“ [ ”、“ { ”,入栈。
  2. 遇到右括号“ ) ”、“ ] ”、“ } ”,出栈,跟左括号进行匹配,不匹配就报错。

上两句话的意思就是说我去遍历字符串,如果遇到左括号,就入栈;遇到右括号,就出栈顶元素,并判断这个右括号是否与栈顶括号相匹配,若不匹配则返回false,匹配继续遍历字符串,直到遍历完全,遍历后,检查栈是否为空,若为空,则均匹配,返回true,反之false。

S1:遍历输入的括号序列,如果是左括号,进入S2,如果是右括号,进入S3
S2:如果当前遍历到左括号,则入栈
S3:如果当前遍历到右括号,则出栈一个元素,看其是否与当前的右括号组成一对,如果不是,则匹配失败。或者在出栈过程中发生异常(从空栈中出栈),也匹配失败
S4:若能顺利遍历完成,检查栈中是否还有剩余元素,如果有,则匹配失败;如果没有,则匹配成功。

1、首先将这个字符串转换成字符数组,并初始化一个空栈。

2、遍历到第0个元素,(,为左括号,入栈

3、后面以此类推,遍历完第3个元素[后,栈空间应该是这样的

4、遍历到第4个元素]时,发现为右括号,此时,从栈顶出栈一个左括号,即[,刚好[与],匹配成一对

5、以此类推,直到第6个元素),都是匹配的

6、此时,序列已经遍历完毕,但是栈不是空的,所以原序列匹配失败。

 

完整代码

//创建栈结构
typedef char STDataType;
typedef struct Stack
{
	STDataType* a; //存储数据
	int top; //栈顶的位置
	int capacity; //容量
}ST;
//初始化栈
void StackInit(ST* ps);
//销毁栈
void StackDestory(ST* ps);
//压栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//判空
bool StackEmpty(ST* ps);
//访问栈顶数据
STDataType StackTop(ST* ps);
//有效元素个数
int StackSize(ST* ps);
 
//定义:
//初始化栈
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}
//销毁栈
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
//压栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//如果栈满了,考虑扩容
	if (ps->top == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; //检测容量
		ps->a = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newcapacity; //更新容量
	}
	ps->a[ps->top] = x;//将数据压进去
	ps->top++;//栈顶上移
}
//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}
//判空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0; //如果top为0,那么就为真,即返回
}
//访问栈顶数据
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1]; //top-1的位置才为栈顶的元素
}
//有效元素个数
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
 
//创建好了栈开始实现
bool isValid(char* s) {
    ST st;//先创建一个栈
    StackInit(&st);//初始化栈
    while (*s)
    {
        if (*s == '[' || *s == '(' || *s == '{')
        {
            StackPush(&st, *s); //如果是左括号就入栈
            ++s;
        }
        else
        {
            if (StackEmpty(&st))
            {
                return false; //此处说明前面根本没有左括号,导致栈为空,直接返回false
            }
            char top = StackTop(&st); //获取栈顶元素
            StackPop(&st); //出栈顶元素,接下来进行匹配
            if ((*s == ']' && top != '[')
                || (*s == ')' && top != '(')
                || (*s == '}' && top != '{'))
            {
                StackDestory(&st); //返回前先销毁,防止内存泄漏
                return false; //如果不匹配,直接返回false
            }
            else
            {
                //此时匹配,继续比较,直到遍历结束
                ++s;
            }
        }
    }
    //栈为空,说明所有左括号都匹配
    bool ret = StackEmpty(&st);
    StackDestory(&st); //返回前先销毁,防止内存泄漏
    return ret;
}

二、用队列实现栈

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

题目

思路

我们这里使用两个队列 

 

入栈数据1、 2、 3

可以将数据入队列至队列一或者队列二。

 

如何出栈?

 但出栈要先出1,怎么办?

第一步

将队列一出队n-1个至队列二。

 

第二步

pop队列一的最后一个元素。

 

int myStackPop(MyStack* obj) 
{
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        nonempty=&obj->q1;
        empty=&obj->q2;
    }
    while(QueueSize(nonempty)>1)
    {
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }
    int top=QueueFront(nonempty);
    return top;
}

接下来怎么入栈呢?

将元素入队至不为空的队列。

怎么判断栈空

队列一和队列二都为空的情况下,栈就是空的。

如何取栈顶元素

取不为空的队列尾部元素。

总的来说就是,入栈时就将数据插入不为空的队列,出栈就将不为空的队列的前n-1个数据导入至另一个队列,然后pop最后一个元素。

代码实现

构造一个栈

这个栈要包含两个队列

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

在此之前我们要准备好队列的一般接口:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	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->head = pq->tail = NULL;
	pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail==NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
		pq->size++;
	}
}
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;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;	
}
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;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

用队列实现栈的接口

第一个接口:创建并初始化栈
MyStack* myStackCreate() 
{
    MyStack* pst=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}
第二个接口:入栈
void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

入栈简单:只要将数据插入到不为空的队列即可。

入栈之前我们需要判断队列满吗?

不需要,因为我的队列是用单链表实现的,可以无限链接下去。

如果两个队列都为空,该插入到哪个队列呢?

我们可以这样做,如果队列1为空,就插入到队列2,如果不为空,就插入到队列1.

第三个接口:出栈

首先我们有两个队列

初始状态它们都是空的

队列一:空

队列二:空

入栈1、2、3、4

执行后

队列一:空

队列二:1、2、3、4

出队列只能先出1,如何出4呢?

把1、2、3先出给队列一

执行后

队列一:1、2、3

队列二:4

然后将4用变量记录并出队,最后返回记录4的变量。

执行后

队列一:1、2、3

队列二:空。

出队三板斧

第一步:即将不为空的队列的前n-1个元素入至为空的队列。

第二步:将剩下的1个元素用变量记录,然后将最后一个元素出队。

第三步:返回用变量记录的元素。

int myStackPop(MyStack* obj) 
{
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        nonempty=&obj->q1;
        empty=&obj->q2;
    }
    while(QueueSize(nonempty)>1)
    {
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }
    int top=QueueFront(nonempty);
    return top;
}
第四个接口:取栈顶元素
int myStackTop(MyStack* obj) 
{
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

取栈顶元素之前我们需要保证栈不为空

这里会用到检查是否为空

如果栈为空,返回false。

取栈顶元素,即取不为空的队列的队尾元素。

第五个接口:判断栈是否为空
bool myStackEmpty(MyStack* obj) 
{
    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}

如果两个队列都是空的那么该栈就是空的。

这里多提一下,栈的元素个数怎么求?

栈的元素个数就是那个不为空队列的元素个数。

第六个接口:销毁栈
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

要将两个队列置空后再去free

用队列实现栈:

我们需要用两个队列实现栈

栈类是于尾插尾删

队列是尾插头删

第一次入栈:两个队列都为空,随便插入一个队列都可

第一次出栈:出栈要出的是尾部数据,队列只能出头部数据,这是队列不能直接实现的。

那么需要将不为空的队列前n-1个数据导入至为空的队列,再将最后一个元素pop掉。

队列一:1、2、3、4

队列二:空

那么导入后

队列一:4

队列二:1、2、3

最后pop最后一个元素

队列一:空

队列二:1、2、3、4

再次尾插:尾插至不为空的队列即可。

总代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	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->head = pq->tail = NULL;
	pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail==NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
		pq->size++;
	}
}
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;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;	
}
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;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == 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));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}


void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

int myStackPop(MyStack* obj) 
{
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        nonempty=&obj->q1;
        empty=&obj->q2;
    }
    while(QueueSize(nonempty)>1)
    {
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }
    int top=QueueFront(nonempty);
    return top;
}

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

 

三、用栈实现队列

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

题目

 思路

第一次入队

将数据1出队操作

将栈1的数据全导入栈2

 

然后栈2进行出栈操作 

此时第一个出栈的就是栈1的头

再次入队5、6、7

直接将5、6、7入栈至栈1

实际我们的对头和队尾是这样的 

 

总而言之:

用两个栈实现一个队列,就得把一个栈专门入队,另一个栈用作出队。这里实现的时候我们用栈1做入队栈,栈2做出队栈。

也就是说,只要将数据入队,直接放入栈1.

出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.

队列结构体

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

提前准备的栈

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


typedef char STDataType;


typedef struct Stack
{
	STDataType* a;
	int top;   //栈顶的位置
	int capacity; //容量
}ST;


void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}


void STPush(ST* ps, STDataType x)
{
	assert(ps);
	//容量不足,需要进行扩容
	if (ps->top == ps->capacity)
	{
		//增大容量
		int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//对a指向的数组空间进行增容
		ps->a = (STDataType*)realloc(ps->a, NewCapacity * sizeof(STDataType));
		//检查
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = NewCapacity;


	}
	//增容之后再入栈
	//top是从0位置开始所以是先使用再++
	ps->a[ps->top] = x;
	ps->top++;
}




void STPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}


bool STEmpty(ST* ps)
{
	assert(ps);
	if (ps->top > 0)
	{
		return false;
	}
	else
	{
		return true;
	}
	//return ps->top == 0;
}


int STsize(ST* ps)
{
	assert(ps);
	return ps->top;
}


STDataType STTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	//top是从0开始的
	return ps->a[ps->top - 1];
}

第一个接口:创建并初始化队列

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

我们把栈1做入队栈,栈2做出队栈。

也就是说,只要将数据入队,直接放入栈1.

出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.

第三个接口:出队

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

第四个接口:取队头元素
 

int myQueuePeek(MyQueue* obj) 
{
    if(STEmpty(&obj->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);
    free(obj);
}

用栈实现队列

我们有两个栈要求实现队列的一般接口

栈一:空

栈二:空

第一次入队:入栈至栈一或者栈二都可,这里选择栈一。

假设入队1、2、3、4

栈一:1、2、3、4

栈二:空

出队:要先出1

将栈一全部导入栈二

栈一:空

栈二:4、3、2、1

之后入队就插入至栈一

出队就pop栈二。

总代码

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


typedef char STDataType;


typedef struct Stack
{
	STDataType* a;
	int top;   //栈顶的位置
	int capacity; //容量
}ST;


void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}


void STPush(ST* ps, STDataType x)
{
	assert(ps);
	//容量不足,需要进行扩容
	if (ps->top == ps->capacity)
	{
		//增大容量
		int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//对a指向的数组空间进行增容
		ps->a = (STDataType*)realloc(ps->a, NewCapacity * sizeof(STDataType));
		//检查
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = NewCapacity;


	}
	//增容之后再入栈
	//top是从0位置开始所以是先使用再++
	ps->a[ps->top] = x;
	ps->top++;
}




void STPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}


bool STEmpty(ST* ps)
{
	assert(ps);
	if (ps->top > 0)
	{
		return false;
	}
	else
	{
		return true;
	}
	//return ps->top == 0;
}


int STsize(ST* ps)
{
	assert(ps);
	return ps->top;
}


STDataType STTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	//top是从0开始的
	return ps->a[ps->top - 1];
}

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))
    {
        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);
    free(obj);
}

四、设计循环队列

622. 设计循环队列 - 力扣(LeetCode)

题目

思路

思路:

1、因为队列是循环的,所以可以用循环链表实现,让链表尾指向链表头即可,但是会有一个问题就是,插入数据的时候,尾插找队列尾的时候不方便,记录队列尾的指针永远指向的都是队列尾的下一个位置(下面会说原因),就需要用循环来找尾

2、可以用数组来实现,数组在内存中是连续的空间,可以直接用两个数组的下标来控制队列的实现,数组实现的弊端就是在循环的时候数组边界需要特殊考虑,但是可以多加个判断条件来判断边界条件即可,相对于用链表实现更优,所以循环队列大多都用数组实现

第一个接口:定义结构体

定义两个数组下标,front指向队列的头,back指向队尾的下一个位置,原因:back最开始只能指向数组的第一个位置,也就是下标为0的位置,插入一个元素之后,back向后移一个位置,如果back指向的就是队尾的最后一个元素,那最开始队列为空的时候back指向哪里呢?所以就矛盾了(用链表实现也是同理)

typedef struct {
    int* a;
    int front;
    int back;
    int k;
 
} MyCircularQueue;

第二个接口:构造/初始化循环队列

首先需要动态开辟一个之前自己定义实现循环队列的结构体,再动态开辟一个数组,这里需要多开辟一个数组空间

多开一个数组空间的原因如下图所示:

多开一个数组空间就是为了判断队列满的时候避免与判断队列空的时候混淆,多开的一个数组空间可以相当于是整个数组中的任何位置,不一定是最后一个位置,随着多次的插入和删除数据,多出来的一个数组空间也会随着改变

MyCircularQueue* myCircularQueueCreate(int k) {
 
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(obj);
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    obj->front = 0;
    obj->back = 0;
    obj->k = k;
 
    return obj;
}

第三个接口:向循环队列插入一个元素

插入一个元素首先要判断队列是否满了(这里判断可以直接调用后面实现的检查队列是否已满的函数直接判断)。队列没有满再进行插入操作:将所要插入的元素放到数组下标为back的位置处,然后back++指向下一个位置,这里就要考虑边界问题了,因为每次插入一个元素之后,back都要向后走一个位置,若back在插入之前就指向的是数组的最后一个位置处(也就是下标为k指向的位置),则插入一个元素之后,back就要指向数组的第一个位置,这样就相当于循环起来了
 

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    assert(obj);
    if(myCircularQueueIsFull(&obj->a))
    {
        return false;
    }
    obj->a[obj->back] = value;
    if(obj->back == obj->k)
    {
        obj->back = 0;
    }
    else
    {
        obj->back++;
    }
    return true;
}

第四个接口:从循环队列中删除一个元素

删除队列的元素首先要判断队列是否为空(这里的判断使用后面实现的检查队列是否为空的函数直接进行判断)。队列不为空再进行删除队列元素操作:因为下标front永远都指向队列的头,删除元素也就是出队列是从队列的头出的,所以在数组实现中直接将下标front++后指向下一个位置,就相当于删除了队列的头元素,这里也就要多一个判断来考虑边界问题,当front指向最后一个位置时也就是下标为k位置时,front向后走一个位置就到了下标为0位置处
 

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);
 
    if(myCircularQueueIsEmpty(&obj->a))
    {
        return false;
    }
    if(obj->front == obj->k)
    {
        obj->front = 0;
    }
    else
    {
        obj->front++;
    }
    return true;
 
}

第五个接口:从队首获取元素

首先也需要判断队列是否为空(使用下面实现的检查队列是否为空的函数进行判断)。不为空:数组下标front永远指向队列的首元素,所以直接返回数组下标为front位置处的元素即可

int myCircularQueueFront(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(&obj->a))
    {
        return -1;
    }
    return obj->a[obj->front];
}

第六个接口:获取队尾元素

获取队列元素就需要判断队列是否为空(调用下面实现的检查队列是否为空的函数进行判断)。因为back永远指向队列尾的下一个位置处,所以返回队尾元素时,需要返回下标为back-1位置处的元素,边界情况就是back指向数组0位置时,就需要返回它的前一个位置,也就是数组的最后一个位置(下标为k位置)对应的元素

int myCircularQueueRear(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(&obj->a))
    {
        return -1;
    }
    if(obj->back == 0)
    {
        return obj->a[obj->k];
    }
    return obj->a[obj->back-1];
}

第七个接口:检查循环队列是否为空

front和back相等指向同一个位置时为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    return obj->front == obj->back;
}

第八个接口:检查循环队列是否已满

back的下一个位置是front时队列为满,特殊情况:同时满足front为0并且back指向数组最后一个位置(也就是k位置处)则队列为满

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    if(obj->front == 0 && obj->back == obj->k)
    {
        return true;
    }
    else
    {
        return obj->front == obj->back + 1;
    }
}

第九个接口:释放空间

free动态开辟的实现循环队列的数组,再free动态开辟的循环队列结构体

void myCircularQueueFree(MyCircularQueue* obj) {
    assert(obj);
    free(obj->a);
    free(obj);
}

总代码

typedef struct {
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=0;
    obj->rear=0;
    obj->k=k;

    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->front==(obj->rear+1)%(obj->k+1);
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
        return false;
    obj->a[obj->rear]=value;
    obj->rear++;
    obj->rear%=(obj->k+1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return false;
    ++obj->front;
    obj->front%=(obj->k+1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}


void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

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

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

相关文章

小白到运维工程师自学之路 第七十二集 (半自动yum安装k8s集群)

一、准备环境 修改主机名 hostnamectl set-hostname k8s-master hostnamectl set-hostname k8s-node1 hostnamectl set-hostname k8s-node2 bashvim /etc/hosts 192.168.77.14 k8s-master 192.168.77.15 k8s-node1 192.168.77.16 k8s-node2 下载阿里源 wget -O /etc/yum…

华为路由器:IPSec加密GRE通道(GRE over IPsec)

IPSec加密GRE通道 由于GRE隧道不提供安全性保障&#xff0c;使用ipsec加密gre隧道是现网中比较常用的VPN部署&#xff0c;它的加密方式分为两种&#xff1a; 可以使用IPsec来加密隧道进行传输&#xff0c;叫做IPsec over GRE&#xff1b; 加密数据流后从隧道传输&#xff0c;…

湘大 XTU OJ 1097 排序 题解:c++ 函数库的使用 快速排序 归并排序 冒泡排序

一、链接 1097 排序 二、题目 Description N个整数&#xff0c;将其排序输出。 输入 第一行是一个整数K&#xff08;1<K<20&#xff09;&#xff0c;表示有多少个样例&#xff0c;每个样例的第一行是一个整数N&#xff08;1<N<1,000&#xff09;和一个字符X&…

探索规律:Python地图数据可视化艺术

文章目录 一 基础地图使用二 国内疫情可视化图表2.1 实现步骤2.2 完整代码2.3 运行结果 一 基础地图使用 使用 Pyecharts 构建地图可视化也是很简单的。Pyecharts 支持多种地图类型&#xff0c;包括普通地图、热力图、散点地图等。以下是一个构建简单地图的示例&#xff0c;以…

1、Spark SQL 概述

1、Spark SQL 概述 Spark SQL概念 Spark SQL is Apache Spark’s module for working with structured data. 它是spark中用于处理结构化数据的一个模块 Spark SQL历史 Hive是目前大数据领域&#xff0c;事实上的数据仓库标准。 Shark&#xff1a;shark底层使用spark的基于…

竞赛项目 深度学习的口罩佩戴检测 - opencv 卷积神经网络 机器视觉 深度学习

文章目录 0 简介1 课题背景&#x1f6a9; 2 口罩佩戴算法实现2.1 YOLO 模型概览2.2 YOLOv32.3 YOLO 口罩佩戴检测实现数据集 2.4 实现代码2.5 检测效果 3 口罩佩戴检测算法评价指标3.1 准确率&#xff08;Accuracy&#xff09;3.2 精确率(Precision)和召回率(Recall)3.3 平均精…

【立创EDA】【0】基本概念

原理图库设计 符号设计 当在元件库中没有找到需要的元件原理图符号时&#xff0c;需要自己手动绘制点击文件-新建-符号进行新建符号 封装库设计 原理图符号对应焊盘 绘制封装时&#xff0c;可以在立创商城寻找元器件对应的数据手册进行参考 PCB绘制 晶振需要包地&#xf…

ip地址修改器软件哪个好 ip地址切换器有哪些

IP地址修改器是一种常用的网络工具&#xff0c;用于修改计算机或网络设备的IP地址。在网络连接中&#xff0c;IP地址被用于标识每个设备的唯一地址&#xff0c;通过它来实现设备之间的通信和数据传输。然而&#xff0c;有时候我们需要修改IP地址以解决一些网络问题或实现特定的…

如何让你的视频在 TikTok上变得火爆?

TikTok凭借巨大的用户量和商业价值&#xff0c;它从来不缺优质内容。如何在众多内容中脱颖而出获得关注&#xff0c;这并不简单。和泛流量账号不同&#xff0c;商业账号的目的更加明确&#xff0c;也就是说&#xff0c;商业账号并不一定要以高流量最为唯一的追求目标&#xff0…

41.利用matlab 平衡方程用于图像(matlab程序)

1.简述 白平衡 白平衡的英文为White Balance&#xff0c;其基本概念是“不管在任何光源下&#xff0c;都能将白色物体还原为白色”&#xff0c;对在特定光源下拍摄时出现的偏色现象&#xff0c;通过加强对应的补色来进行补偿。 所谓的白平衡是通过对白色被摄物的颜色还原&…

【12】Git工具 协同工作平台使用教程 Gitee使用指南 腾讯工蜂使用指南【Gitee】【腾讯工蜂】【Git】

tips&#xff1a;少量的git安装和使用教程&#xff0c;更多讲快速使用上手Gitee和工蜂平台 一、准备工作 1、下载git Git - Downloads (git-scm.com) 找到对应操作系统&#xff0c;对应版本&#xff0c;对应的位数 下载后根据需求自己安装&#xff0c;然后用git --version验…

代码质量检查工具SonarQube

Devops流水线之SonarQube 文章目录 Devops流水线之SonarQube1. 软件功能介绍及用途2. 软件环境搭建与使用2.1 使用方法2.2 SonarQube相关属性说明2.3 Sonar配置文件内容说明 3. 使用环节4. 检查方法 1. 软件功能介绍及用途 SonarQube是一个用于代码质量管理的开源平台&#xf…

蓝牙运动耳机哪个好、最好的运动蓝牙耳机品牌排行

在忙碌的都市生活中&#xff0c;人们往往容易迷失方向。音乐是一种良药&#xff0c;能够使心灵平静下来&#xff0c;找到正确的方向。生命需要运动&#xff0c;而有趣的运动更能让人们自由自在&#xff0c;释放身心。因此&#xff0c;运动和音乐天然地相辅相成。当我们佩戴一款…

Redis的RDB持久化

Redis是一个键值对数据库服务器&#xff0c;服务器中通常包含着任意个非空数据库&#xff0c;而每个非空数据库中又可以包含任意个键值对&#xff0c;为了方便起见&#xff0c;我们将服务器中的非空数据库以及它们的键值对统称为数据库状态。 举个例子&#xff0c;下图展示了一…

观察者模式

观察者模式 需求1. 定义2. 组成3. 观察者模式原型图4. 实现4.1 抽象观察者4.2 抽象主题4.3 实现观察者4.4 实现主题4.5 测试 需求 实现一种天气实时更新的程序&#xff08;天气推送&#xff09;&#xff0c;当气象站数据更新后&#xff0c;天气接口程序去获取最新天气数据&…

从零开始搭建个人博客网站(hexo框架)

1.工具及环境搭建 1&#xff09;注册GitHub并且新建一个repositories 2&#xff09;下载node.js以及Git 下载链接&#xff1a; 检验安装是否成功&#xff1a; 【注】&#xff1a;MacOS自带Git&#xff0c;可以直接在终端输入git --version进行检验 3&#xff09;新建一个…

生信豆芽菜-箱线图使用说明

网站&#xff1a;http://www.sxdyc.com/visualsBoxplot 一、使用方法 1.打开网址&#xff08;http://www.sxdyc.com/singleCollectionTool?href-diff&#xff09;&#xff0c;选择“箱线图”。 准备数据&#xff1a; 第一个文件为特征的表达文件&#xff0c;第一列为样本&am…

C#,数值计算——基于模拟退火的极小化问题单纯形(下山)算法的计算方法与C#源程序

1 模拟退火 模拟退火算法其实是一个类似于仿生学的算法&#xff0c;模仿的就是物理退火的过程。 我们炼钢的时候&#xff0c;如果我们急速冷凝&#xff0c;这时候的状态是不稳定的&#xff0c;原子间杂乱无章的排序&#xff0c;能量很高。而如果我们让钢水慢慢冷凝&#xff0c…

报错 | Spring报错详解

Spring报错详解 一、前言二、报错提示三、分层解读1.最下面一层Caused by2.上一层Caused by3.最上层Caused by 四、总结五、解决方案 一、前言 本文主要是记录在初次学习Spring时遇到报错后的解读以及解决方案 二、报错提示 三、分层解读 遇到报错的时候&#xff0c;我们需要…

vant van-tabs van-pull-refresh van-list 标签栏+上拉加载+下拉刷新

<template><div class"huibj"><div class"listtab"><!--顶部导航--><div class"topdh"><topnav topname"余额明细"></topnav></div><!--Tab 标签--><van-tabs v-model"…
最新文章