数据结构初阶--栈和队列

目录

一.栈

1.栈的定义

2.顺序栈的功能实现

2.1.顺序栈的定义

2.2.顺序栈的初始化

2.3.顺序栈的判空

2.4.顺序栈的入栈

2.5.顺序栈的出栈

2.6.顺序栈的取栈顶元素

2.7.顺序栈的求栈的大小

2.8.顺序栈的销毁

2.9.完整程序

Stack.h

Stack.c

test.c

二.队列

1.队列的定义

2.链式队列的功能实现

2.1.链式队列的定义

2.2.链式队列的初始化

2.3.链式队列的判空

2.4.链式队列的入队

2.5.链式队列的出队

2.6.链式队列的取队头元素

2.7.链式队列的取队尾元素

2.8.链式队列的求队列的大小

2.9.链式队列的销毁

2.10.完整程序

Queue.h

Queue.c

test.c

三.栈与队列刷题

题一:有效的括号

题二:用队列实现栈

题三:用栈实现队列

题四:设计循环队列


一.栈

1.栈的定义

栈是只允许在一端进行插入或删除操作的线性表

逻辑结构:与普通线性表相同

数据的运算:插入,删除操作有区别

特点:后进先出,即Last In First Out(LIFO)

几个重要术语:

  1. 空栈;
  2. 栈顶:允许插入和删除的一端
  3. 栈底:不允许插入和删除的一端
  4. 栈顶元素
  5. 栈底元素

2.顺序栈的功能实现

2.1.顺序栈的定义

typedef int STDataType;typedef struct Stack
{STDataType* a;//动态开辟数组int top;//标识栈顶位置int capacity;//栈可以容纳的数据个数
}ST;

首先,定义动态数组a,采用动态数组的方式主要是便于后期容量的扩充;然后,定义一个变量top用于标识栈顶位置;最后,定义一个变量capacity用于统计栈可以容纳的数据个数。

2.2.顺序栈的初始化

void StackInit(ST* ps)
{//判空assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}

首先,将指向栈中数据内存空间的指针a初始化为NULL;然后,将指向栈顶元素的变量top初始化为0;最后,再将标识栈当前容量的capacity初始化为0即可。

调试分析:

2.3.顺序栈的判空

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

判断一个栈是否为空,若为空则返回true,否则返回false。这里需要特别说明的一点是,我们将top指向栈顶元素的下一个位置,而非指向栈顶元素本身。

注意:

  1. 当top初始化为:top=0;此时top指向栈顶元素的下一个位置
  2. 当top初始化为:top=-1;此时top指向栈顶元素

调试分析:

2.4.顺序栈的入栈

void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//为空就开辟四个空间,不为空,就扩容至二倍STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}

在入栈之前,首先需要对顺序栈的空间容量进行检查,这里使用三目运算符进行判断:若当前容量capacity为空,则开辟4个数据的内存空间;若当前容量非空但已满,则将capacity的大小扩容至2*capacity。然后调用realloc函数开辟新的内存空间,并返回新的内存空间的起始地址tmp。接着将新开辟的内存空间的起始地址tmp赋值给a,让a指向这片新开辟的空间。最后,将x插入栈顶位置,并让top继续指向栈顶元素的下一个位置。

调试分析:

2.5.顺序栈的出栈

void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}

在出栈之前,首先需要调用StackEmpty(ps)函数来判断栈是否为空,若不为空,则可以出栈。出栈操作就是将top减一,需要注意的是,出栈的数据还残留在内存中,只是逻辑上被删除了。

调试分析:

2.6.顺序栈的取栈顶元素

STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}

在取栈顶元素之前,首先需要调用函数StackEmpty(ps)判断栈是否为空,若栈不为空则可以取栈顶元素。因为top指向栈顶元素的下一个位置,所以top需要先进行减一,再取栈顶元素。

调试分析:

2.7.顺序栈的求栈的大小

int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}

因为top指向栈顶元素的下一个位置,而top的下标又是从0开始的,所以top所在位置的下标就是所求栈的大小,也就是栈中元素个数。

调试分析:

2.8.顺序栈的销毁

void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}

对于栈的销毁,首先需要释放由realloc动态申请开辟的内存空间,这里主要搭配free函数进行释放。然后将top和capacity初始化为0。

调试分析:

2.9.完整程序

Stack.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int 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);//取栈顶元素
STDataType StackTop(ST* ps);//判空
bool StackEmpty(ST* ps);//栈大小
int StackSize(ST* ps);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1#include"Stack.h"//初始化
void StackInit(ST* ps)
{//判空assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}//销毁
void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}//入栈
void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//为空就开辟四个空间,不为空,就扩容至二倍STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}//出栈
void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}//取栈顶元素
STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}//判空
bool StackEmpty(ST* ps)
{//判空assert(ps);return ps->top == 0;
}//栈大小
int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"Stack.h"void TestStack()
{ST st;//初始化StackInit(&st);//入栈StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);//出栈while (!StackEmpty(&st)){//读取栈顶元素printf("%d ",StackTop(&st));//出栈StackPop(&st);}printf("\n");//销毁StackDestory(&st);
}int main()
{TestStack();return 0;
}

二.队列

1.队列的定义

队列是只允许在一端进行插入(入队),在另一端删除的线性表(出队)。

队列的特点:先进先出,即First In First Out(FIFO)。

几个重要术语:

  1. 空队列
  2. 队头:允许删除的一端
  3. 队尾:允许插入的一端
  4. 队头元素
  5. 队尾元素

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);//看队头元素是否为NULLreturn pq->head == NULL;
}

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

调试分析:

2.4.链式队列的入队

void QueuePush(Queue* pq, QDataType x)
{//判空assert(pq);//创建新结点newnodeQNode* 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。

调试分析:

2.10.完整程序

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);//销毁
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);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"//初始化
void QueueInit(Queue* pq)
{//判空assert(pq);//不带哨兵位pq->head = pq->tail = NULL;
}//销毁
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;
}//入队
void QueuePush(Queue* pq, QDataType x)
{//判空assert(pq);//创建新结点newnodeQNode* 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;
}//判空
bool QueueEmpty(Queue* pq)
{//判空assert(pq);//看队头元素是否为NULLreturn pq->head == NULL;
}//总计元素个数
int QueueSize(Queue* pq)
{//判空assert(pq);//设置指针变量cur,用于遍历队列QNode* cur = pq->head;int size = 0;//遍历队列while (cur){++size;cur = cur->next;}return size;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"void TestQueue()
{Queue q;//初始化QueueInit(&q);//入队QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePush(&q, 5);//出队while (!QueueEmpty(&q)){//取对头元素printf("%d ",QueueFront(&q));//出队QueuePop(&q);}printf("\n");//销毁QueueDestory(&q);
}int main()
{test();return 0;
}

三.栈与队列刷题

题一:有效的括号

题目描述:

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

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

分析:

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

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

实现:

bool isValid(char* s)
{ST st;//初始化StackInit(&st);while (*s){if (*s == '(' || *s == '[' || *s == '{')//是左括号{//入栈StackPush(&st, *s);++s;}else//是右括号{//判断栈是否为空,可能出现字符串只含右括号的情况if (StackEmpty(&st)){//销毁StackDestory(&st);return false;}//若栈不为空,则读取栈顶元素并出栈STDataType top = StackTop(&st);// 读取栈顶元素StackPop(&st);//出栈//看栈顶元素与当前字符是否匹配if ((top == '{' && *s == '}') || (top == '(' && *s == ')') || (top == '[' && *s == ']')){//匹配++s;}else{//不匹配则销毁StackDestory(&st);return false;}}}//匹配完之后,判断栈是否为空,可能出现字符串只含左括号的情况bool ret = StackEmpty(&st);//销毁StackDestory(&st);return ret;
}

题二:用队列实现栈

题目描述:

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push,top,pop和empty)。
实现MyStack类:
void push(int x):将元素x压入栈顶
void pop():移除并返回栈顶元素
int top():返回栈顶元素
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和q2QueueDestory(&obj->q1);QueueDestory(&obj->q2);//释放栈free(obj);
}

题三:用栈实现队列

题目描述:

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push,pop,peek,empty)
实现MyQueue类:
void push(int x):将元素x推到队列的末尾
int pop():从队列的开头移除并返回元素
int peek():返回队列开头的元素
boolean empty():如果队列为空,返回true;否则,返回false

分析:

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

实现:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int 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);//取栈顶元素
STDataType StackTop(ST* ps);//判空
bool StackEmpty(ST* ps);//栈大小
int StackSize(ST* ps);/**********用栈实现队列**********///构造一个包含两个栈的队列
typedef struct
{ST pushst;ST popst;
}MyQueue;//初始化队列
MyQueue* myQueueCreate()
{//调用malloc为栈开辟内存空间MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));//初始化两个栈StackInit(&obj->pushst);StackInit(&obj->popst);return obj;
}//入队
void myQueuePush(MyQueue* obj, int x)
{//判空assert(obj);//入栈StackPush(&obj->pushst, x);
}//出队
int myQueuePop(MyQueue* obj)
{//判空assert(obj);//出队前首先要判断popst栈是否为空if (StackEmpty(&obj->popst)){//如果popst栈为空,则把pushst栈的数据拷贝过来while (!StackEmpty(&obj->pushst)){//将pushst栈的栈顶元素压入popst栈StackPush(&obj->popst, StackTop(&obj->pushst));//pushst栈的栈顶元素出栈StackPop(&obj->pushst);}}//读取popst栈的栈顶元素int front = StackTop(&obj->popst);//pushst栈的栈顶元素出栈StackPop(&obj->popst);return front;
}//取队头元素
int myQueuePeek(MyQueue* obj)
{//判空assert(obj);//当popst栈为空if (StackEmpty(&obj->popst)){//如果popst栈为空,则把pushst栈的数据拷贝过来while (!StackEmpty(&obj->pushst)){//将pushst栈的栈顶元素压入popst栈StackPush(&obj->popst, StackTop(&obj->pushst));//pushst栈的栈顶元素出栈StackPop(&obj->pushst);}}//读取popst栈的栈顶元素return StackTop(&obj->popst);
}//判空队列是否为空
bool myQueueEmpty(MyQueue* obj)
{//判空assert(obj);//只有当两个栈均为空时,才表示队列为空return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);
}//销毁队列
void myQueueFree(MyQueue* obj)
{//判空assert(obj);//销毁pushst栈和popst栈StackDestory(&obj->pushst);StackDestory(&obj->popst);//释放队列free(obj);
}

题四:设计循环队列

题目描述:

设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为"环形缓冲器"
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k):构造器,设置队列长度为k
Front:从队首获取元素。如果队列为空,返回-1
Rear:获取队尾元素。如果队列为空,返回-1
enQueue(value):向循环队列插入一个元素,如果成功插入则返回真
deQueue():从循环队列中删除一个元素。如果成功删除则返回真
isEmpty():检查循环队列是否为空
isFull():检查循环队列是否已满

分析:

1.采用数组或者链表都可以,但是数组缓存利用率更高,所以这里主要采用数组的方式。

2.用模运算将存储空间在逻辑上变成“环状”。当发现rear指针要指向MaxSize时,不应该让它指向MaxSize而是应该让它指向数组下标为0的位置。

3.队列判空: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.mzph.cn/news/18620.shtml

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

相关文章

使用 Docker Compose 部署 Redis Cluster 集群,轻松搭建高可用分布式缓存

Redis Cluster&#xff08;Redis 集群&#xff09;是 Redis 分布式解决方案的一部分&#xff0c;它旨在提供高可用性、高性能和横向扩展的功能。Redis Cluster 能够将多个 Redis 节点组合成一个分布式集群&#xff0c;实现数据分片和负载均衡&#xff0c;从而确保在大规模应用场…

session反序列化+SoapClientSSRF+CRLF

文章目录 session反序列化SoapClientSSRFCRLF前言bestphps revengecall_user_func()方法的特性SSRFCRLF组合拳session反序列化 解题步骤总结 session反序列化SoapClientSSRFCRLF 前言 从一道题分析通过session反序列化出发SoapClientSSRF利用CRLF解题 bestphp’s revenge 首…

基于方向编码的模板匹配算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ........................................................................... %选择移动个…

自适应巡航控制系统研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 据统计, 我国交通事故造成的伤亡人数每年超过10万人, 其中驾驶员人为原因 (疲劳、酒驾、误操作等) 所致事故逐渐升高.汽车交通…

pycharm——制作k线图

K 线图 Candlestick Candlestick - Kline_itemstyle from pyecharts import options as opts from pyecharts.charts import Klinedata [[2320.26, 2320.26, 2287.3, 2362.94],[2300, 2291.3, 2288.26, 2308.38],[2295.35, 2346.5, 2295.35, 2345.92],[2347.22, 2358.98, 23…

安装skywalking并集成到微服务项目

文章目录 一、前言二、介绍1. 架构 三、安装skywalking服务端四、启动skywalking服务端五、微服务项目开发注册中心网关服务商品服务订单服务支付服务测试 六、下载java客户端七、微服务集成skywalking客户端1. idea启动2. 命令行启动3. 集成效果 八、skywalking客户端配置1. 配…

Python高阶技巧 正则表达式

正则表达式&#xff0c;又称规则表达式&#xff08;Regular Expression&#xff09;&#xff0c;是使用单个字符串来描述、匹配某个句法规则的字符串&#xff0c;常被用来检索、替换那些符合某个模式&#xff08;规则&#xff09;的文本。 简单来说&#xff0c;正则表达式就是使…

小程序学习(六):全局配置

1.全局配置文件及常用的配置项 全局配置-window 2.小程序窗口的组成部分 3.了解window节点常用的配置项 4.设置导航栏的标题 设置步骤:app.json->window->navigationBarTitleText 5.设置导航栏的背景色 背景颜色不支持red这种文字 6.设置导航栏的标题颜色 注意:navigat…

SpringBoot笔记:SpringBoot集成Dataway

文章目录 1、什么是 Dataway?2、主打场景3、技术架构4、整合SpringBoot4.1、maven 依赖4.2、初始化脚本4.3、整合 SpringBoot 5、Dataway 接口管理6、Mybatis 语法支持7、小结 1、什么是 Dataway? 官网地址&#xff1a;https://www.hasor.net/docs/guides/quickstart Da…

k8s kubeadm命令升级集群 从1.17升级到1.18

k8s kubeadm命令升级集群 从1.17升级到1.18 大纲 注意事项master节点执行升级命令master节点和node节点执行命令 注意事项 目标当前线上k8s集群版本是k8s1.17 想把k8s升级到1.18。注意k8s不能跨版本升级例如k8s1.17不能直接升级到k8s1.19&#xff0c;需要先升级到1.18才后向…

faac内存开销较大,为方便嵌入式设备使用进行优化(valgrind使用)

faac内存开销较大&#xff0c;为方便嵌入式设备使用进行优化&#xff0c;在github上提了issues但是没人理我&#xff0c;所以就搞一份代码自己玩吧。 基于faac_1_30版本&#xff0c;原工程https://github.com/knik0/faac faac内存优化: faac内存开销较大&#xff0c;为方便嵌入…

意外:WPS编程新工具,不用编程,excel用户:可以不用VBA啦

来来来&#xff0c;拓宽一下视野&#xff01; 别总以为excel和WPS只能用VBA编程&#xff0c;也别总是想着ACCESS这些老生常谈的工具。其实对于电子表格高级用户来讲&#xff0c;不会VBA&#xff0c;不用ACCESS&#xff0c;也一样可以解决复杂问题或者高级应用。 尤其是WPS用户…

【腾讯云 Cloud Studio 实战训练营】CloudStudio体验真正的现代化开发方式,双手插兜不知道什么叫对手!

CloudStudio体验真正的现代化开发方式&#xff0c;双手插兜不知道什么叫对手&#xff01; 文章目录 CloudStudio体验真正的现代化开发方式&#xff0c;双手插兜不知道什么叫对手&#xff01;前言出现的背景一、CloudStudio 是什么&#xff1f;二、CloudStudio 的特点三、CloudS…

PostgreSql 锁

一、概述 在 PostgreSQL 事务中提到&#xff0c;多个用户访问相同数据时可能出现脏读&#xff0c;不可重复度&#xff0c;幻读&#xff0c;更新丢失的问题&#xff0c;为解决这些问题&#xff0c;定义了不同的隔离级别&#xff0c;而隔离级别的具体实现&#xff0c;依靠的就是数…

钉钉群消息推送

1. 添加钉钉群机器人 PC端登录&#xff08;当前版本手机端无法进行推送关键词设置&#xff09;&#xff0c;群设置--> 机器人 --> webhook进行安全设置复制webhook对应的url 2. 群消息推送 钉钉群消息支持纯文本和markdown类型 2.1 调用示例源码 import com.alibaba.…

助你丝滑过度到 Vue3 组合式Api的优势新的组件 ②⑧

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; VUE3~TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f449;…

2023.08.01 驱动开发day8

驱动层 #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/gpio.h> #include <linux/of_gpio.h>#…

如何在免费版 pycharm 中使用 github copilot (chatGPT)?

起因 在 vscode 中使用了 github copilot 以后&#xff0c;感觉这个人工智能还不错。 但 vscode 对于 python 项目调试并不是特别方便&#xff0c;所以想在 Pycharm 中也能使用同一个 github 账号&#xff0c;用上 copilot 的功能。 不需要等待&#xff0c;安装即用&#xff…

【Web 表单】与用户数据打交道-1(mdn笔记)

0. Web 表单指南 我们将介绍 Web 表单的各个方面&#xff1a;HTML 结构、样式、验证表单数据&#xff0c;以及提交数据到服务器。 基本指南 你的第一个表单 第一次创建 HTML 表单的经验&#xff0c;包括设计一个简单表单、使用正确的 HTML 元素实现它、通过 CSS 添加一些非常简…

【Spring Cloud一】微服务基本知识

系列文章目录 微服务基本知识 系列文章目录前言一、系统架构的演变1.1单体架构1.2分层架构1.3分布式架构1.4微服务架构1.5分布式、SOA、微服务的异同点 二、CAP原则三、RESTfulRESTful的核心概念&#xff1a; 四、共识算法 前言 在实际项目开发过程中&#xff0c;目前负责开发…