【初阶数据结构】5.栈和队列

news2024/11/15 8:41:43

文章目录

  • 1.栈
    • 1.1 概念与结构
    • 1.2 栈的实现
    • 2.队列
    • 2.1 概念与结构
    • 2.2 队列的实现
    • 3.栈和队列算法题
      • 3.1 有效的括号
      • 3.2 用队列实现栈
      • 3.3 用栈实现队列
      • 3.4 设计循环队列


1.栈

1.1 概念与结构

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFOLast In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述

栈底层结构选型

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。


1.2 栈的实现

Stack.h

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

//定义栈的结构
typedef int STDataType;
typedef struct Stack {
	STDataType* arr;
	int capacity;//栈的空间大小
	int top;//栈顶
}ST;

//栈的初始化
void STInit(ST* ps);

//栈的销毁
void STDestory(ST* ps);

//栈顶---入数据,出数据
//入数据
void StackPush(ST* ps, STDataType x);
//出数据
void StackPop(ST* ps);

//取栈顶元素
STDataType StackTop(ST* ps);

//获取栈中有效元素个数
int STSize(ST* ps);

//判断栈是否为空
bool StackEmpty(ST* ps);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"

//栈的初始化
void STInit(ST* ps) {
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

//栈的销毁
void STDestory(ST* ps) {
	assert(ps);
	if (ps->arr) {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

//栈顶---入数据,出数据
//入数据
void StackPush(ST* ps, STDataType x) {
	assert(ps);
	//1.判断空间是否足够
	if (ps->capacity == ps->top) {
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//增容
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("relloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	//空间足够
	ps->arr[ps->top++] = x;
}

//出数据
void StackPop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));//栈为空报错
	--ps->top;
}

//判断栈是否为空
bool StackEmpty(ST* ps) {
	assert(ps);
	return ps->top == 0;
}

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

//取栈顶元素
STDataType StackTop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->arr[ps->top - 1];
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"

void STTest() {
	ST st;
	STInit(&st);
	//
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	printf("size: %d\n", STSize(&st));

	//循环出栈,直到栈为空
	while (!StackEmpty(&st)) {
		STDataType data = StackTop(&st);
		printf("%d ", data);
		//出栈
		StackPop(&st);
	}
	printf("size: %d\n", STSize(&st));

	//
	//STDestory(&st);
}

int main() {
	STTest();
	return 0;
}

2.队列

2.1 概念与结构

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

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头

在这里插入图片描述

队列底层结构选型

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


2.2 队列的实现

Queue.h

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

//定义队列结构
typedef int QDataType;

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

typedef struct Queue {
	QueueNode* phead;//队头:删
	QueueNode* ptail;//队尾:插
	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.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"

//初始化队列
void QueueInit(Queue* pq) {
	assert(pq);
	pq->phead = pq->ptail = 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->ptail = newnode;
	}
	else {
		//队列不为空
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;//入一次,size++ 一次
}

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

// 出队列,队头
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));

	//只有一个结点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead) {
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else {
		//删除队头元素
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	--pq->size;//出一次,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->ptail->data;
}

//队列有效元素个数
int QueueSize(Queue* pq) {
	assert(pq);
	/*int size = 0;
	QueueNode* pcur = pq->phead;
	while (pcur) {
		size++;
		pcur = pcur->next;//复杂度O(n)
	}*/
	return pq->size;//复杂度O(1)
}

//销毁队列
void QueueDestroy(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));//判断队列不为空,空队列不需要销毁

	QueueNode* pcur = pq->phead;
	while (pcur) {
		QueueNode* Next = pcur->next;
		free(pcur);
		pcur = Next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"


void QueueTest01() {
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	//QueuePop(&q);
	printf("head:%d\n", QueueFront(&q));
	printf("tail:%d\n", QueueBack(&q));
	printf("size:%d\n", QueueSize(&q));

	QueueDestroy(&q);

	//QueuePop(&q);
	//QueuePop(&q);
	//QueuePop(&q);
	//QueuePop(&q);
	QueuePop(&q);
}

int main() {
	QueueTest01();
	return 0;
}

3.栈和队列算法题

3.1 有效的括号

点击链接答题

在这里插入图片描述

思路:

定义一个指针ps遍历字符串

ps遍历到的字符为左括号,入栈

ps遍历到的字符为右括号,取栈顶元素与ps进行比较,

  1. 栈顶元素 匹配 *ps,出栈,ps++
  2. 栈顶元素 不匹配 *ps,返回false

代码:

//定义栈的结构
typedef char STDataType;
typedef struct Stack {
	STDataType* arr;
	int capacity;//栈的空间大小
	int top;//栈顶
}ST;

//栈的初始化
void STInit(ST* ps) {
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

//栈的销毁
void STDestory(ST* ps) {
	assert(ps);
	if (ps->arr) {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

//栈顶---入数据,出数据
//入数据
void StackPush(ST* ps, STDataType x) {
	assert(ps);
	//1.判断空间是否足够
	if (ps->capacity == ps->top) {
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//增容
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("relloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		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;
}

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

//取栈顶元素
STDataType StackTop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->arr[ps->top - 1];
}

bool isValid(char* s) {
    ST st;
    //初始化
    STInit(&st);
    //遍历字符串s
    char* ps = s;
    while(*ps != '\0'){
        //左括号,入栈
        if(*ps == '(' || *ps == '[' || *ps == '{'){
            StackPush(&st, *ps);
        }
        else{//右括号,和栈顶元素比较是否匹配
        //栈为空,直接返回false
        if(StackEmpty(&st)){
            return false;
        }
        //栈不为空才能取栈顶元素
            char ch = StackTop(&st);
            if((*ps == ')' && ch == '(')
            || (*ps == ']' && ch == '[')
            || (*ps == '}' && ch == '{') ){
                StackPop(&st);
            } 
            else{
                //不匹配
                STDestory(&st);
                return false;
            }
        }
        ps++;

    }
    bool ret = StackEmpty(&st) == true;//如果为空,返回true
    //销毁
    STDestory(&st);
    return ret;
}

3.2 用队列实现栈

点击链接答题

在这里插入图片描述

思路:

出栈:找不为空的队列,将size-1个数据导入到另一个队列中。

入栈:往不为空队列里面插入数据

取栈顶元素:

例如:

两个队列:

  1. Q1:1 2 3
  2. Q2:NULL

如果是栈的话:

  1. 插入1 2 3
  2. 出栈一次
  3. 插入4
  4. 全部出栈

得到:3 4 2 1


Q1里面的1 2出栈到Q2,把3取出来,此时Q1NULL

取出了3

Q2里面插入4,此时Q21 2 4

Q2里面的1 2出栈到Q1,此时Q24,把4取出来,此时Q2NULL

取出了3 4

Q1里面的1出栈到Q2,把2取出来 ,此时Q1NULL

取出了3 4 2

Q2里面的1取出来

取出了3 4 2 1

代码:

//定义队列结构
typedef int QDataType;

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

typedef struct Queue {
	QueueNode* phead;//队头:删
	QueueNode* ptail;//队尾:插
	int size;//保存队列有效数据个数
}Queue;

//初始化队列
void QueueInit(Queue* pq) {
	assert(pq);
	pq->phead = pq->ptail = 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->ptail = newnode;
	}
	else {
		//队列不为空
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;//入一次,size++ 一次
}

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

// 出队列,队头
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));

	//只有一个结点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead) {
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else {
		//删除队头元素
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	--pq->size;//出一次,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->ptail->data;
}

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

//销毁队列
void QueueDestroy(Queue* pq) {
	assert(pq);
    //这里允许pq为空
	// assert(!QueueEmpty(pq));//判断队列不为空,空队列不需要销毁

	QueueNode* pcur = pq->phead;
	while (pcur) {
		QueueNode* Next = pcur->next;
		free(pcur);
		pcur = Next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}



//两个队列来实现栈
typedef struct {
    Queue q1;//队列1
    Queue q2;//队列2
} MyStack;

//STInit
MyStack* myStackCreate() {
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);

    return pst;
}

//Push入栈
void myStackPush(MyStack* obj, int x) {
    //往不为空的队列中插入数据
    if(!QueueEmpty(&obj->q1)){//如果q1不为空,执行这个
        QueuePush(&obj->q1,x);//往队列q1里面插入x
    }
    else{//如果q2不为空,执行这个
        QueuePush(&obj->q2,x);//往队列q2里面插入x
    }
}

//Pop出栈
int myStackPop(MyStack* obj) {
    //找不为空的队列
    Queue* empQ = &obj->q1;//定义q1为空
    Queue* noneQ = &obj->q2;//定义q2为非空
    if(!QueueEmpty(&obj->q1)){//如果q1不为空,执行这个
        noneQ = &obj->q1;//q1是非空队列
        empQ = &obj->q2;//q2是空队列
    }
    //将不为空队列中size-1个数据导入到空队列中
    while(QueueSize(noneQ) > 1){//循环结束的判断,是只剩下一个数据
        int front = QueueFront(noneQ);//取队头数据
        QueuePush(empQ,front);//往空队列q2里面插入队头元素
        QueuePop(noneQ);//把非空队列q1里面的队头元素拿出来,这样下次循环出来的队头元素就不会重复了
    }
    //非空队列中只剩下一个数据------要出栈的数据
    int pop = QueueFront(noneQ);//把要取出的数据存起来
    QueuePop(noneQ);//将数据出队列,让q1变成空队列
    return pop;//返回要取出的数据
}

//取栈顶元素:找不为空的队列,取队尾元素
//不出栈
int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->q1)){
        //q1不为空
        return QueueBack(&obj->q1);//取队尾数据
    }
    else{
        //q2不为空
        return QueueBack(&obj->q2);//取队尾数据
    }
}

//判断栈是否为空
//也就是判断两个队列是否为空
bool myStackEmpty(MyStack* obj) {//栈是空的,返回 true ;否则,返回 false 。
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

//栈的销毁
//也就是判断两个队列的销毁
void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);//销毁q1
    QueueDestroy(&obj->q2);//销毁q2
    //因为MyStack* myStackCreate()里面有个指针pst,所以我们还要销毁指针obj
    free(obj);//销毁指向栈的指针
    obj = NULL;
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

3.3 用栈实现队列

点击链接答题

在这里插入图片描述

思路:

定义两个栈:pushST(入数据)和popST(出数据)

假设我们要在队列里放123,出队列123

我们先在pushST里面放进去1 2 3

然后把pushST里面的数据拿到popST里面,依次是3 2 1

然后把1提取出来,把2提取出来,把3提取出来.


如果我们先在pushST里面放进去1 2 3

然后把pushST里面的数据拿到popST里面,依次是3 2 1

然后在pushST里面放进去4

然后把popST里面的1提取出来,把2提取出来,把3提取出来.

然后把pushST里面的数据4拿到popST里面

然后把4提取出来


入队:往pushST中插入数据

出队:判断popST是否为空,不为空直接pop,为空的话将pushST导入到popST中再pop

取队头:跟出队一样,但这里只取数据,不pop数据

代码:

//定义栈的结构
typedef char STDataType;
typedef struct Stack {
	STDataType* arr;
	int capacity;//栈的空间大小
	int top;//栈顶
}ST;

//栈的初始化
void STInit(ST* ps) {
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

//栈的销毁
void STDestory(ST* ps) {
	assert(ps);
	if (ps->arr) {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

//栈顶---入数据,出数据
//入数据
void StackPush(ST* ps, STDataType x) {
	assert(ps);
	//1.判断空间是否足够
	if (ps->capacity == ps->top) {
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//增容
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("relloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		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;
}

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

//取栈顶元素
STDataType StackTop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->arr[ps->top - 1];
}



//
typedef struct {
    ST pushST;
    ST popST;
} MyQueue;

//队列的初始化
MyQueue* myQueueCreate() {
    MyQueue* pst = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&pst->pushST);
    STInit(&pst->popST);

    return pst;
}

//往pushST中插入数据
void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->pushST,x);
}

//删除数据
//1.检查popST是否为空,不为空直接出,为空pushST导入到popST,再出数据
int myQueuePop(MyQueue* obj) {
    if(StackEmpty(&obj->popST)){
        //如果popST为空
        //导数据
        while(!StackEmpty(&obj->pushST)){
            //如果pushST非空
            StackPush(&obj->popST, StackTop(&obj->pushST));//把取到的栈顶元素导入进popST
            StackPop(&obj->pushST);
        }
    }
    //取栈顶,删除栈顶元素并返回栈顶数据
    int top = StackTop(&obj->popST);//储存删除的栈顶元素
    StackPop(&obj->popST);
    return top;//返回删除的栈顶元素
}

//取队头元素
int myQueuePeek(MyQueue* obj) {
    if(StackEmpty(&obj->popST)){
        //如果popST为空
        //导数据
        while(!StackEmpty(&obj->pushST)){
            //如果pushST非空
            StackPush(&obj->popST, StackTop(&obj->pushST));//把取到的栈顶元素导入进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;
}

/**
 * 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);
*/

3.4 设计循环队列

点击链接答题

在这里插入图片描述

思路:

循环队列,空间固定。

这里我们可以用数组来实现循环队列。


如何判断队列是否为满?

多申请一块空间

(rear+1)%(k+1) == front


如何判断队列是否为空?

rear == front

代码:

//定义循环队列的结构
typedef struct {
    int* arr;//定义数组
    int front;//定义头
    int rear;//定义尾
    int capacity;//定义一个变量保存数组空间大小
} MyCircularQueue;

//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k) {
    //定义一个指针,动态申请一块空间,指向循环队列
    MyCircularQueue* pst = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //底层数组申请k+1个整型空间
    pst->arr = (int*)malloc(sizeof(int)*(k+1));

    pst->front = pst->rear = 0;
    pst->capacity = k;//把k保存起来
    return pst;//返回指向循环队列的指针
}

//判断队列是否满了
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //(rear+1)%(k+1) == front  就满了
    return (obj->rear+1)%(obj->capacity+1) == obj->front;
}

//入队列
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //队列满了就不能插入数据
    if(myCircularQueueIsFull(obj)){
        return false;//说明插入失败
    }
    //队列没满,可以插入数据
    obj->arr[obj->rear++] = value;//插入一个数据rear++
    obj->rear %= obj->capacity + 1;//如果rear跑到队尾了,就通过取余回到队头

    return true;//说明插入成功
}

//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->rear == obj->front;
}

//出队列
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //队列为空
    if(myCircularQueueIsEmpty(obj)){
        return false;//表示删除失败
    }
    //队列不为空
    obj->front++;
    obj->front %= obj->capacity + 1;//如果front跑到队尾了,就通过取余回到队头
    return true;//表示删除成功
}

//取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {
    //如果队列为空
    if(myCircularQueueIsEmpty(obj)){
        return -1;//如果队列为空,返回 -1 
    }
    //如果队列不为空
    return obj->arr[obj->front];
}

//取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    //如果队列为空
    if(myCircularQueueIsEmpty(obj)){
        return -1;//如果队列为空,返回 -1 
    }
    //如果队列不为空
    int prev = obj->rear-1;//prev是rear前一个位置
    if(obj->rear == 0){
        prev = obj->capacity;//如果rear是处在arr[0],那么prev在arr[k]
    }
    return obj->arr[prev];
}

//循环队列的销毁
//就是顺序表的销毁
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    free(obj);
    obj = NULL;
}

/**
 * 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);
*/

循环队列的概念与结构

实际中还有一种特殊的队列叫循环队列,环形队列首尾相连成环,环形队列可以使用数组实现,也可以使用循环链表实现

在这里插入图片描述


思考:队列满的情况下,为什么 Q.rear 不存储数据?

为了能使用 Q.rear = Q.front 来区别是队空还是队满,我们常常认为出现左图时的情况即为队满的情况

此时: rear+1=front

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

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

相关文章

从零开始实现大语言模型(八):Layer Normalization

1. 前言 Layer Normalization是深度学习实践中已经被证明非常有效的一种解决梯度消失或梯度爆炸问题,以提升神经网络训练效率及稳定性的方法。OpenAI的GPT系列大语言模型使用Layer Normalization对多头注意力模块,前馈神经网络模块以及最后的输出层的输入张量做变换,使shap…

android13 默认输入法配置分析rom默认配置修改分析

总纲 android13 rom 开发总纲说明 目录 1.前言 2.解决方法 3.方法分析 3.1方法1 3.2方法2 4.彩蛋 1.前言 Android13上需要预装中文输入法, 但是直接预装输入法的话,会出现默认使能的问题,点击TextEdit输入框, 弹出的是默认英文输入法LatinIME, 而不是谷歌拼音输入…

解决GoLand添加GOROOT提示The selected directory is not a valid home for Go Sdk的问题

现象 解决 在Go安装路径下找到zversion.go文件&#xff0c;我的在D:\Program Files\Go1.21.1\src\runtime\internal\sys下面 打开文件&#xff0c;添加如下内容&#xff1a; const TheVersion go1.21.1保存后再重新添加GOROOT即可

2024 杭电多校第一场

目录 目录 树 博弈 传送 树 给一棵根为 1 的有根树&#xff0c;点 i 具有一个权值 Ai 。 定义一个点对的值 f(u,v)max(Au,Av)|Au−Av| 。 你需要对于每个节点 i &#xff0c;计算 ansi∑u∈subtree(i),v∈subtree(i)f(u,v) &#xff0c;其中 subtree(i) 表示 i 的子树。 请…

如何让LabVIEW程序框图的图标简化,从而节省空间?

再点击选项 取消掉箭头所示的√即可。 这样就可以将生成的图标从下面所示&#xff1a; 变成简化的图标&#xff0c;如下所示&#xff1a;

UML的六大关系---泛化、实现、关联、聚合、组合、依赖

文章目录 前言1. 泛化关系(Generalization)2. 实现关系(Realization)3. ‌关联关系(Association)4. 聚合关系(Aggregation)5. 组合关系(Composition)6. 依赖关系(Dependency)总结 前言 讲到设计模式&#xff0c;就会有 U M L UML UML类图这个东西。 一开始就很难理解各种线啥意…

【Spring Boot】网页五子棋项目中遇到的困难及解决方法

目录 一、HikariPool-1 - Starting异常二、Invalid bound statement (not found)异常三、The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary异常四、The server time zone value时区报错异常五、补充知识点…

CSS技巧专栏:一日一例 6 - 纯CSS实现粉红色跳出来的立体按钮特效

纯CSS实现粉红色跳出来的立体按钮特效 今天要介绍的案例,是个相对简单的按钮效果,我们先看图: 案例分析 我说它简单,因为它实际上并没有使用什么特别的动画效果,只是几个简单的动画组合: 利用伪类before和after,制作按钮后面两个透明的粉色填充层,左右移动。给文字层…

代码随想录算法训练营第23天|39. 组合总和、40.组合总和II、131.分割回文串

打卡Day23 1.39. 组合总和2.40.组合总和II3.131.分割回文串 1.39. 组合总和 题目链接&#xff1a;39. 组合总和 文档讲解&#xff1a; 代码随想录 这道题和昨天做的组合之和由两个区别&#xff1a;被选的元素没有数量限制&#xff0c;同时被选的元素可以无限重复&#xff0c;…

区块链技术实现数字电网内数据可信共享 |《超话区块链》直播预告

随着全球电力市场朝着构建“SmartGrid”和“IntelliGrid”的目标发展&#xff0c;国内电力公司也提出了构建“数字电网”的愿景。清大科越推出新型电力系统区块链服务平台&#xff0c;通过便捷的建链、上链、用链及治链能力&#xff0c;有效解决数字电网各主体间数据共享的信任…

QT--网络篇

如果QT头文件找不到QTcpSocket、QTcpSocket、QTcpServer、QtNetwork ,那么可能是pro文件中缺少QT network这行代码 客户端QTcpSocket void QTcpSocket::connectToHost( QString servip, quint16 port ); connectToHost 函数会尝试与指定的服务器建立 TCP 连接。如果连接成…

代码随想录算法训练营第35天|LeetCode 01背包问题 二维、01背包问题 一维、416. 分割等和子集

1. LeetCode 01背包问题 二维 题目链接&#xff1a;https://kamacoder.com/problempage.php?pid1046 文章链接&#xff1a;https://programmercarl.com/背包理论基础01背包-1.html#算法公开课 视频链接&#xff1a;https://www.bilibili.com/video/BV1cg411g7Y6/ 思路&#xf…

【Vue】`v-if` 指令详解:条件渲染的高效实现

文章目录 一、v-if 指令概述二、v-if 的基本用法1. 基本用法2. 使用 v-else3. 使用 v-else-if 三、v-if 指令的高级用法1. 与 v-for 一起使用2. v-if 的性能优化 四、v-if 的常见应用场景1. 表单验证2. 弹窗控制 五、v-if 指令的注意事项 Vue.js 是一个用于构建用户界面的渐进式…

在 PostgreSQL 里如何实现数据的冷热数据分层存储的自动化策略调整?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何实现数据的冷热数据分层存储的自动化策略调整 在 PostgreSQL 里如何实现数据的冷…

最新可用度盘不限速后台系统源码_去授权开心版

某宝同款度盘不限速后台系统源码&#xff0c;验证已被我去除&#xff0c;两个后端系统&#xff0c;账号和卡密系统 第一步安装宝塔&#xff0c;部署卡密系统&#xff0c;需要环境php7.4 把源码丢进去&#xff0c;设置php7.4&#xff0c;和伪静态为thinkphp直接访问安装就行 …

bootstrap中文文档官网

Bootstrap v3 中文文档 Bootstrap 是最受欢迎的 HTML、CSS 和 JavaScript 框架&#xff0c;用于开发响应式布局、移动设备优先的 WEB 项目。 | Bootstrap 中文网

PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动一、理解索引抖动二、索引抖动的影响三…

C语言:温度转换

1.题目&#xff1a;实现摄氏度&#xff08;Celsius&#xff09;和华氏度&#xff08;Fahrenheit&#xff09;之间的转换。 输入一个华氏温度&#xff0c;输出摄氏温度&#xff0c;结果保留两位小数。 2.思路&#xff1a;&#xff08;这是固定公式&#xff0c;其中 F 是华氏度&a…

DL/T645-2007_Part1(协议帧解析)

帧结构 起始字符68H地址域起始字符68H控制码C数据域长度L数据域校验和CS结束字符16H1Byte6Byte1Byte1Byte1ByteN Byte1Byte1Byte 地址域 地址域为6个字节的BCD码构成&#xff0c;当使用的地址码长度不足6字节&#xff0c;高位用0补足&#xff1b;当通信地址为99999999999H时…

自学 阿里巴巴Java开发手册最新版(嵩山版)

&#x1f534; 阿里巴巴Java开发手册最新版&#xff08;嵩山版&#xff09; 一、编程规约(一) 命名风格(二) 常量定义(三) 代码格式(四) OOP 规约(五) 日期时间(六) 集合处理(七) 并发处理(八) 控制语句(九) 注释规约(十) 前后端规范 二、异常日志(一) 错误码(二) 异常处理(三)…