手撕数据结构---栈和队列的概念以及实现

栈的概念:

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

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

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

 栈的基本框架

struct Stack
{int* arr;int capacity;int top;//栈顶
};

栈的实现

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"void STTest()
{ST st;//创建一个栈变量//初始化STInit(&st);//入栈SrackPush(&st, 1);SrackPush(&st, 2);SrackPush(&st, 3);SrackPush(&st, 4);SrackPush(&st, 5);//打印栈内的有效数据printf("%d\n", STSize(&st));//栈的出数据/*SrackPop(&st);*///循环出栈,直到栈为空// while (!StackEmpty(&st))//如果栈不为空的话,我们一直进行循环打印栈顶数据{//取出当前栈顶的数据STDataType data = StackTop(&st);printf("%d ", data);//打印返回的栈顶数据//数据出栈SrackPop(&st);//入栈的顺序是1 2 3 4 5//出栈的顺序是5 4 3 2 1//栈是不能被遍历的,也不能被随机访问}//打印栈内的有效数据printf("%d\n", STSize(&st));//销毁STDestory(&st);
}int main()
{STTest();return 0;
}
//栈这样的结构只能在一端入栈,一端出栈

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"//初始化
void STInit(ST* ps)
{assert(ps);//判断传的ps是不是空的ps->arr = NULL;ps->capacity = ps->top = 0;//一开始的栈顶等于我们的栈底(栈为空的话)
}//销毁
void STDestory(ST* ps)
{assert(ps);//参数不能传空if (ps->arr)//arr不为空的话我们直接将数组释放掉{free(ps->arr);}ps->arr = NULL;ps->capacity = ps->top = 0;
}//栈的入数据操作
void SrackPush(ST* ps, STDataType x)
{assert(ps);//ps不能传空//如果空间足够的话我们直接进行插入//判断空间是否足够,如果top等于capacity的话,那么就说明我们这个栈就是满的if (ps->capacity == ps->top)//,满了,我们需要进行增容操作{//二倍的增加//初始情况下我们的capacity是定义为0的,所以我们不能直接进行乘二的操作//我们需要创建一个变量int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//如果容量为0的话,我们给newCapacity一个初始值4,如果不为0我们就进行乘二的操作STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}//申请成功,我们将tmp申请的空间给pa->arrps->arr = tmp;//申请空间成功了,那么我们就要将capacity进行改变了ps->capacity = newCapacity;}//到这里空间一定是够的ps->arr[ps->top] = x;//我们往栈顶的位置进行添加数据//添加完数据之后,top要加加ps->top++;
}//判断栈是否为空
bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;//为空就返回true
}//栈的出数据操作
void SrackPop(ST* ps)
{assert(ps);//如果栈为空的话,我们是不能出数据的(top==0)assert(!StackEmpty(ps));//如果栈为空就会报错了//走到这里就说明栈不为空--ps->top;//我们只需要将top进行--操作就能进行栈的出数据操作了
}//取栈顶元素---循环打印栈顶的数据
STDataType StackTop(ST* ps)//返回值是栈顶的元素
{assert(ps);assert(!StackEmpty(ps));//判断当前栈是不是空的,如果是空的话,我们没有什么能取的return ps->arr[ps->top - 1];//ps->top - 1是这个栈的最后一个数据的下标}//获取栈中有效个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}

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 SrackPush(ST* ps, STDataType x);//第二个参数是要插入的数据//栈的出数据操作
void SrackPop(ST* ps);//取栈顶元素---循环打印栈顶的数据
STDataType StackTop(ST* ps);//返回值是栈顶的元素//判断栈是否为空
bool StackEmpty(ST* ps);//获取栈中有效个数
int STSize(ST* ps);

队列的概念

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

⼊队列:进⾏插⼊操作的⼀端称为队尾

出队列:进⾏删除操作的⼀端称为队头

队头:用来删除数据

对头:用来插入数据

队列的底层是链表,链表是由一个一个的节点组成

//定义队列节点的结构
struct QueueNode
{int data;struct QueueNode* next;//指向下个节点的指针
};struct Queue
{struct QueueNode* phead;//指向的是队头--删除数据struct QueueNode* ptail;//指向的是队尾--插入数据
};

为什么是定义的是两个结构体类型呢?

队列中的每一个数据是通过一个节点保存的,节点和节点之间是通过指针链接的,

其实就是维护了一个链表,给这个链表加上先进先出的限制,其实就是队列了

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 Queuempty(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;
}//判断队列是否为空
bool Queuempty(Queue* pq)
{assert(pq);return pq->phead == NULL && pq->ptail == NULL;//如果后面的表达式成立,那么就是真,返回的是true//就是说如果这里的是空队列的话,那么就返回的是true
}//入队列,队尾   插入数据
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//申请新节点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//申请一个节点大小的空间if (newnode == NULL){perror("malloc dail!");exit(1);}//对newnode进行初始化操作newnode->data = x;newnode->next = NULL;if (pq->phead == NULL)//说明队列为空{pq->phead = pq->ptail = newnode;//那么此时的newnode不仅是头结点也是尾节点}else//队列不为空{pq->ptail->next = newnode;//那么此时的newnode 就是新的ptailpq->ptail = newnode;}pq->size++;
}//出队列,队头    删除数据    从头结点开始删除数据
void QueuePop(Queue* pq)
{assert(pq);//队列为空(不可删除数据,因为没有数据)//队列不为空(可删除数据)assert(!Queuempty(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;//那么之前存的next就是新的头结点了}pq->size--;
}//取队头数据
QDataType QueueFront(Queue* pq)//返回队头数据
{assert(pq);assert(!Queuempty(pq));//队列不为空return pq->phead->data;//将队头里面的数据直接返回就行了
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!Queuempty(pq));//队列不为空return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);//下面这种遍历的话效率太低了//int size = 0;定义一个指针进行遍历//QueueNode* pcur = pq->phead;//指向队列的头结点//while (pcur)//pcur不为空就往后走//{//  size++;//  pcur = pcur->next;//}//return size;return pq->size;
}//队列的销毁
void QueueDestroy(Queue* pq)
{assert(pq);assert(!Queuempty(pq));//队列不为空//遍历QueueNode* pcur = pq->phead;while (pcur){//销毁之前先把下个节点进行保存QueueNode* next = pcur -> next;free(pcur);//将Pcur销毁之后,那么之前保存的next就是新的头结点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);//取队头数据printf("head:%d\n", QueueFront(&q));//取队尾数据printf("tail:%d\n", QueueBack(&q));//删除QueuePop(&q);//队列有效个数printf("size:%d\n", QueueSize(&q));//队列的销毁QueueDestroy(&q);}
int main()  
{QueueTest01();
}

栈和队列相关的OJ题

题目一:有效的括号

 

//借助数据结构---栈来解决这道题
/*
思路:我们先创建一个字符串指针ps
我们再创建一个栈空间
我们通过ps进行字符串的遍历
如果是做左括号的话,那么我们就进行入栈操作
如果我们遇到了右括号的话,那么我们就与栈顶的元素进行匹配如果是一对括号的话,那么我们就进行出栈操作,然后ps++,top-- 进行下一对括号的匹配如果ps++指向的是大括号,但是栈顶的是小括号,那么现在就是不匹配的那么我们就直接返回false*/
typedef char STDataType;
typedef struct Stack
{STDataType* arr;int capacity;int top;//栈顶
}ST;//初始化
void STInit(ST* ps)
{assert(ps);//判断传的ps是不是空的ps->arr = NULL;ps->capacity = ps->top = 0;//一开始的栈顶等于我们的栈底(栈为空的话)
}//销毁
void STDestory(ST* ps)
{assert(ps);//参数不能传空if (ps->arr)//arr不为空的话我们直接将数组释放掉{free(ps->arr);}ps->arr = NULL;ps->capacity = ps->top = 0;
}//栈的入数据操作
void SrackPush(ST* ps, STDataType x)
{assert(ps);//ps不能传空//如果空间足够的话我们直接进行插入//判断空间是否足够,如果top等于capacity的话,那么就说明我们这个栈就是满的if (ps->capacity == ps->top)//,满了,我们需要进行增容操作{//二倍的增加//初始情况下我们的capacity是定义为0的,所以我们不能直接进行乘二的操作//我们需要创建一个变量int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//如果容量为0的话,我们给newCapacity一个初始值4,如果不为0我们就进行乘二的操作STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}//申请成功,我们将tmp申请的空间给pa->arrps->arr = tmp;//申请空间成功了,那么我们就要将capacity进行改变了ps->capacity = newCapacity;}//到这里空间一定是够的ps->arr[ps->top] = x;//我们往栈顶的位置进行添加数据//添加完数据之后,top要加加ps->top++;
}//判断栈是否为空
bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;//为空就返回true
}//栈的出数据操作
void SrackPop(ST* ps)
{assert(ps);//如果栈为空的话,我们是不能出数据的(top==0)assert(!StackEmpty(ps));//如果栈为空就会报错了//走到这里就说明栈不为空--ps->top;//我们只需要将top进行--操作就能进行栈的出数据操作了
}//取栈顶元素---循环打印栈顶的数据
STDataType StackTop(ST* ps)//返回值是栈顶的元素
{assert(ps);assert(!StackEmpty(ps));//判断当前栈是不是空的,如果是空的话,我们没有什么能取的return ps->arr[ps->top - 1];//ps->top - 1是这个栈的最后一个数据的下标}//获取栈中有效个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}bool isValid(char* s)
{ST st;//创建一个栈变量//初始化STInit(&st);//遍历字符串schar *ps=s;//指向字符串swhile(*ps!='\0')//我们需要遍历字符串'\0'之前的数据{//左括号入栈if(*ps=='('|| *ps=='[' || *ps=='{'){SrackPush(&st,*ps);}else//右括号,和栈顶元素进行匹配{//栈不为空才能取元素//判断栈是否为空,空的话直接返回falseif(StackEmpty(&st))//栈为空的话,这个函数返回的就是true{return false;}//取栈顶元素,与top进行比较char ch=StackTop(&st);//栈顶的元素,我们取出if((*ps==')' &&ch=='(')||(*ps==']' &&ch=='[')||(*ps=='}' &&ch=='{')){//匹配上了我们就进行出栈操作SrackPop(&st);}else{//不匹配的话,我们在返回之前我们同样需要进行销毁操作STDestory(&st);//那么就是括号不匹配了return false;}}//入栈之后我们进行ps++ps++;}bool ret=StackEmpty(&st)==true;//为空的话那么我们就返回truew//销毁STDestory(&st);return ret;
}
/*
假如我们的字符串里面只有一个左括号的话,那么这个代码就会直接入栈
然后跳出循环,并没有对栈内的空间进行检查 
并没有进行出栈的操作,所以栈内是有元素的
我们要判断栈内是否为空
如果是空的话,那么就说括号都配对完成了,左括号都出栈了,那么就返回true*/
/*
我们在取栈顶元素之前我们还要对栈的空间进行判断,看看栈是否为空,
栈不为空才能去栈顶元素
栈为空的话,之间返回false*/

题目二:用队列实现栈

 

/*
队列是先进先出
栈是先进后出
*/
/*
因为我们是要进行栈的实现
那么假如我们存进去1 2 3
那么拿出来的就是3 2 1我们用两个队列实现
Q1和Q2两个队列
假设现在Q1里面的是1 2 3,1在对头,3在队尾
我们Q1每次出size-1个数据入到Q2里面,那么此时的Q1就剩下一个3,那么我们直接将3出栈,那么得到的第一个数就是3
以此类推我们就能得到3 2 1
*//*
两个队列,那个队列不为空,我们就将这个队列里面的size-1个数据导入到另一个队列里面去,然后将剩下的元素导出了如果最后队列里面只有一个数据,那么我们就直接将这个数据导出
*//*思路:
出栈:找到不为空的队列,将size-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;
}//判断队列是否为空
bool Queuempty(Queue* pq)
{assert(pq);return pq->phead == NULL && pq->ptail == NULL;//如果后面的表达式成立,那么就是真,返回的是true//就是说如果这里的是空队列的话,那么就返回的是true
}//入队列,队尾   插入数据
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//申请新节点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//申请一个节点大小的空间if (newnode == NULL){perror("malloc dail!");exit(1);}//对newnode进行初始化操作newnode->data = x;newnode->next = NULL;if (pq->phead == NULL)//说明队列为空{pq->phead = pq->ptail = newnode;//那么此时的newnode不仅是头结点也是尾节点}else//队列不为空{pq->ptail->next = newnode;//那么此时的newnode 就是新的ptailpq->ptail = newnode;}pq->size++;
}//出队列,队头    删除数据    从头结点开始删除数据
void QueuePop(Queue* pq)
{assert(pq);//队列为空(不可删除数据,因为没有数据)//队列不为空(可删除数据)assert(!Queuempty(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;//那么之前存的next就是新的头结点了}pq->size--;
}//取队头数据
QDataType QueueFront(Queue* pq)//返回队头数据
{assert(pq);assert(!Queuempty(pq));//队列不为空return pq->phead->data;//将队头里面的数据直接返回就行了
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!Queuempty(pq));//队列不为空return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);//下面这种遍历的话效率太低了//int size = 0;定义一个指针进行遍历//QueueNode* pcur = pq->phead;//指向队列的头结点//while (pcur)//pcur不为空就往后走//{//  size++;//  pcur = pcur->next;//}//return size;return pq->size;
}//队列的销毁
void QueueDestroy(Queue* pq)
{assert(pq);//assert(!Queuempty(pq));//队列不为空//遍历QueueNode* pcur = pq->phead;while (pcur){//销毁之前先把下个节点进行保存QueueNode* next = pcur -> next;free(pcur);//将Pcur销毁之后,那么之前保存的next就是新的头结点pcur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//两个队列来实现栈
typedef struct 
{Queue q1;//队列1Queue q2;//队列2
} MyStack;//STInit  栈的初始化
MyStack* myStackCreate()
{MyStack*pst=(MyStack*)malloc(sizeof(MyStack));//创建一个栈大小的空间QueueInit(&pst->q1);//调用初始化函数对q1进行初始化QueueInit(&pst->q2);return pst;
}//那么到这里我们有一个空栈,栈里面有两个队列//入数据
void myStackPush(MyStack* obj, int x)
{//往不为空的队列插入数据//第一步判断那个队列是非空队列if(!Queuempty(&obj->q1))//如果这个队列不是空的话,我们就我那个这个队列里面入数据{//往队列内插入数据QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}}
//出数据
int myStackPop(MyStack* obj)
{//找到不为空的队列Queue*empQ=&obj->q1;//假设q1是空的,创建指针指向q1Queue*noneQ=&obj->q2;//q2不为空,指针指向q2if(!Queuempty(&obj->q1))//如果q1不为空{//创建两个指针,noneQ指向的是非空队列,empQ指向的是空队列noneQ=&obj->q1;//那么这个非空指针就指向了q1empQ=&obj->q2;//那么空指针就指向q2了}//将不为空内的size-1个数据导入到另一个队列里面while(QueueSize(noneQ)>1)//循环条件是非空队列里面只剩下一个有效的数据了{int front=QueueFront(noneQ);//获取这个非空队列里面的队头数据QueuePush(empQ,front);//往空队列里面循环插入队头数据QueuePop(noneQ);//因为我们这个非空队列的队头数据已经拿出去了 ,那么我们就将非空队列进行删除数据操作}//非空队列中只剩下一个数据----那么这个数据就是要出栈的数据int pop=QueueFront(noneQ);//获取剩下的这个元素QueuePop(noneQ);//进行出数据操作return pop;//返回我们要的值}//取栈顶元素  假设插入1 2 3,那么栈顶就是3   这里是2两个队列
int myStackTop(MyStack* obj)   
{//找到不为空的队列,取队尾元素if(!Queuempty(&obj->q1))//如果第一个队列不为空的话{return QueueBack(&obj->q1);//直接将取到的队尾元素进行返回就行了}else{return QueueBack(&obj->q2);}
}
//判读栈是否为空
bool myStackEmpty(MyStack* obj)
{//两个队列如果都为空的话,那么这个栈就是空的return Queuempty(&obj->q1)  &&  Queuempty(&obj->q2);
}
//销毁
void myStackFree(MyStack* obj)
{//就是栈内的连个队列的销毁QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);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);
*/

题目三:用栈实现队列

 

//定义栈的结构
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int capacity;//栈的空间大小int top;//栈顶(插入数据和删除数据的位置)
}ST;//初始化
void STInit(ST* ps)
{assert(ps);//判断传的ps是不是空的ps->arr = NULL;ps->capacity = ps->top = 0;//一开始的栈顶等于我们的栈底(栈为空的话)
}//销毁
void STDestory(ST* ps)
{assert(ps);//参数不能传空if (ps->arr)//arr不为空的话我们直接将数组释放掉{free(ps->arr);}ps->arr = NULL;ps->capacity = ps->top = 0;
}//栈的入数据操作
void SrackPush(ST* ps, STDataType x)
{assert(ps);//ps不能传空//如果空间足够的话我们直接进行插入//判断空间是否足够,如果top等于capacity的话,那么就说明我们这个栈就是满的if (ps->capacity == ps->top)//,满了,我们需要进行增容操作{//二倍的增加//初始情况下我们的capacity是定义为0的,所以我们不能直接进行乘二的操作//我们需要创建一个变量int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//如果容量为0的话,我们给newCapacity一个初始值4,如果不为0我们就进行乘二的操作STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}//申请成功,我们将tmp申请的空间给pa->arrps->arr = tmp;//申请空间成功了,那么我们就要将capacity进行改变了ps->capacity = newCapacity;}//到这里空间一定是够的ps->arr[ps->top] = x;//我们往栈顶的位置进行添加数据//添加完数据之后,top要加加ps->top++;
}//判断栈是否为空
bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;//为空就返回true
}//栈的出数据操作
void SrackPop(ST* ps)
{assert(ps);//如果栈为空的话,我们是不能出数据的(top==0)assert(!StackEmpty(ps));//如果栈为空就会报错了//走到这里就说明栈不为空--ps->top;//我们只需要将top进行--操作就能进行栈的出数据操作了
}//取栈顶元素---循环打印栈顶的数据
STDataType StackTop(ST* ps)//返回值是栈顶的元素
{assert(ps);assert(!StackEmpty(ps));//判断当前栈是不是空的,如果是空的话,我们没有什么能取的return ps->arr[ps->top - 1];//ps->top - 1是这个栈的最后一个数据的下标}//获取栈中有效个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}////*
因为我们是用两个栈来实现队列
那么假如我们插入1 2 3 
那么导出的也是1 2 3我们创建两个栈,分别用来入数据和出数据第一个栈接入我们放的是1 2 3  1在栈底,3在栈顶那么我们将这三个数据依次放到另一个栈内那么另一个栈就是3 2 1  3在栈第,1在栈顶,那么我们依次将这个栈的数据依次导出就能达到队列的效果了
*//*逻辑:pop是出数据
入队:往pushST中插入数据出队:判断popST是否为空,不为空直接pop,为空的话将pushST导入到popST中取队头:跟出队一样的,但是这里不pop数据
*/
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)
{//调用栈的插入方法SrackPush(&obj->pushST,x);//往pushST中插入数据
}//删除数据
int myQueuePop(MyQueue* obj)
{//1.检查popST是否为空//1)不为空直接 出//2)为空,pushST导入到popST,在出数据if(StackEmpty(&obj->popST))//如果为空的话,我们就进项导数据操作{//导数据while(!StackEmpty(&obj->pushST))//只要不为空,我们就将这个栈内的数据导出{//循环取栈顶数据//StackTop(&obj->pushST)将这个栈内的栈顶数据取出SrackPush(&obj->popST,StackTop(&obj->pushST));//将pushST这个栈的栈顶数据导入到popST中//取完数据,插入完数据之后我们再将进行pop操作,换下个数SrackPop(&obj->pushST);//下次我们取到的就是新的栈顶元素}}//取栈顶,删除栈顶元素并返回栈顶数据int top=StackTop(&obj->popST);//将这个栈的栈顶元素保存出来SrackPop(&obj->popST);//将栈顶元素删除,下次就是新的栈顶元素return top;
}
//取队头元素
int myQueuePeek(MyQueue* obj)
{//1.检查popST是否为空//1)不为空直接 出//2)为空,pushST导入到popST,在出数据if(StackEmpty(&obj->popST))//如果为空的话,我们就进项导数据操作{//导数据while(!StackEmpty(&obj->pushST))//只要不为空,我们就将这个栈内的数据导出{//循环取栈顶数据//StackTop(&obj->pushST)将这个栈内的栈顶数据取出SrackPush(&obj->popST,StackTop(&obj->pushST));//将pushST这个栈的栈顶数据导入到popST中//取完数据,插入完数据之后我们再将进行pop操作,换下个数SrackPop(&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);
*/

题目四:设计循环队列

//推荐循环队列底层队列为数组/*
插入数据:循环队列满了,就不能插入数据*//*
一开始的front指向的是数组的头元素
rear也是指向数组的头元素
每次插入一个元素,rear就进行++操作
我们在下面申请了k+1个整型的空间,最后一个空间仅仅只是占位置的,不是存储数据的,实际存储数据的只有k个
假设这里是5个空间
0 1 2 3 4  这是对应的下标
一开始的front和rear都指向的是0,每次增加一个元素,rear++
等rear指向4的时候这个数组就存满了因为是循环,最后rear++会回到front的位置
那么我们可以通过(rear+1)%(k+1)==front来判断队列是否满了
rear==front可以判断队列是否为空
*/
//定义循环队列的结构
typedef struct
{int *arr;int rear;int front;int capacity;//保存数组的空间的大小k
} MyCircularQueue;//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k)//我们根据这个K进行动态的申请内存,这里的返回值是指向循环队列的指针
{MyCircularQueue*pst=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//先开辟队列的空间//因为队列的底层结构是数组,那么我们再为数组开辟空间pst->arr=(int*)malloc(sizeof(int)*(k+1));//我们给数组申请K+1个整型大小的空间pst->front=pst->rear=0;pst->capacity=k;//循环队列的容量大小是kreturn pst;
}
//判断队列是否满了
bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear+1)%(obj->capacity+1)==obj->front;//就说明满了//capacity+1是数组的容量大小,多出的1是用来占位置的
}//向循环队列里面插入数据,如果成功插入就返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{//队列如果满了的话就不能进行插入数据的操作了if(myCircularQueueIsFull(obj))//如果满了的话,就不能插入数据了{return false;}//走到这里说明队列还没有满,我们就进行插数据操作obj->arr[obj->rear++]=value;//插入完数据之后rear要进行++的操作//为了保证循环的效果/*假设我们的rear此时在占位置的那个位置,就是多出来的1的那个位置为了保证循环,我们要让rear回到数组的第一个位置 */obj->rear%=obj->capacity+1;//obj->rear=obj->rear % (obj->capacity+1)//我们这里进行求余的操作,将结果给rear//插入完成我们就返回truereturn true;
}
//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->rear==obj->front;
}//从循环队列中删除一个元素,成功删除就返回true
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{//既然要删除数据,那么队列就不能为空if(myCircularQueueIsEmpty(obj))//说明这个数组为空,我们就不进行删除数据的操作了{return false;}//走到这里说明队列不为空,那么我们就进行删除操作obj->front++;obj->front%=obj->capacity+1;//取余//原先front位置的数据还在,但是我们现在front已经换位置了,那么原先位置的数据就能就行插入数据了return true;
}
//取对首元素,返回对应值
int myCircularQueueFront(MyCircularQueue* obj)
{//队列为空就没啥数据能取//判断队列是否为空if(myCircularQueueIsEmpty(obj))//队列为空的话我们直接返回-1{return -1;}return obj->arr[obj->front];
}
//取对尾元素,返回对应值
int myCircularQueueRear(MyCircularQueue* obj)
{//队列为空就没啥数据能取//判断队列是否为空if(myCircularQueueIsEmpty(obj))//队列为空的话我们直接返回-1{return -1;}//return obj->arr[obj->rear-1];//rear指向的是最后一个数据的下一个位置//假如我们的rear指向的是0下标的数,那么rear-1不就是-1吗?这么写代码就是错的,,存在越界情况int prev=obj->rear-1;//定义一个指针指向rear前一个数据if(obj->rear==0){prev=obj->capacity;//下标为4,那么就是这个数组的第5个位置,就是最后一个位置}return obj->arr[prev];//队尾元素就是rear经历过++操作之前的位置的元素
}//循环队列的销毁
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);
*/

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

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

相关文章

Kafka、RabbitMQ、RocketMQ:消息队列技术深度对比

文章目录 摘要1. 消息队列概述1.1 消息队列的作用 2. Kafka2.1 简介2.2 核心特性2.3 使用场景 3. RabbitMQ3.1 简介3.2 核心特性3.3 使用场景 4. RocketMQ4.1 简介4.2 核心特性4.3 使用场景 5. 技术对比5.1 吞吐量5.2 可靠性5.3 消息延迟5.4 消息顺序 6. 结语 摘要 消息队列是…

《框架》

《框架》 引言 在当今快速发展的技术世界中,框架(Framework)已成为软件开发中不可或缺的一部分。框架为开发者提供了一种结构化的方法来构建应用程序,从而提高开发效率、确保代码质量和维护性。本文将深入探讨框架的概念、类型、优势以及在现代软件开发中的应用。 什么是…

opencascade AIS_RubberBand AIS_RotationMode源码学习

//!相机旋转类型 Camera rotation mode. enum AIS_RotationMode { AIS_RotationMode_BndBoxActive, //!< default OCCT rotation AIS_RotationMode_PickLast, //!< rotate around last picked point AIS_RotationMode_PickCenter, //!< rotate around point at the ce…

广东省道路工程检测练习试题(单选406题)真题

1.一般项目的合格点率应达到( ),且不合格点的最大偏差值不得大于规定允许偏差值的1.5倍。 A.不低于75% B.不低于80% C.不低于85% D.不低于90% 答案:B 2.进行土路基平整度检测时,当路宽小于9m,检测点数应为( ) A.1 B.2 C.3 D.4 答案:A 3.根据《城镇道路工程施工…

python之代码简化式(列表、字典生成式,递归函数,迭代器(iter)和生成器(yield)、匿名函数(lambda)的使用)(12)

文章目录 前言1、列表、字典生成式2、递归函数2.1 python中代码的递归深度&#xff08;扩展&#xff09; 3、拓展&#xff1a;迭代器和生成器3.1 迭代器&#xff08;iter&#xff09;3.2 生成器&#xff08;yield&#xff09; 4、匿名函数&#xff08;lambda&#xff09;4.1 ma…

宠物猫用空气净化器真的有用吗?值得买的猫用空气净化器牌子排名

作为一名6年资深铲屎官&#xff0c;每天铲猫砂盆的工作无疑是一项挑战。家中不仅弥漫着难以忍受的气味&#xff0c;而且家里的小孩和老人偶尔会因为过敏性鼻炎或结膜炎等问题感到不适。换毛季节尤其头疼&#xff0c;浮毛无处不在&#xff1a;沙发、外套、坐垫&#xff0c;甚至连…

C++中绝对值的用法

在C中&#xff0c;std::fabs函数可以用于四则运算中&#xff0c;特别是当你需要确保参与运算的浮点数是正值时。std::fabs函数返回其参数的绝对值&#xff0c;这对于处理可能包含负数的表达式很有用。 下面是一些示例&#xff0c;展示了如何在四则运算中使用std::fabs函数&…

Spring Boot 3 + Resilience4j 简单入门 + Redis Cache 整合

1. 项目结构 2. Maven依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.2</version><relativePath/> <!-- lookup parent from repository --&…

Kubernetes CRI工具集:crictl深度实践与应用

Kubernetes CRI工具集&#xff1a;crictl深度实践与应用 一、引言 在Kubernetes&#xff08;k8s&#xff09;环境中&#xff0c;crictl是一个重要的命令行工具&#xff0c;用于与容器运行时接口&#xff08;Container Runtime Interface, CRI&#xff09;兼容的容器运行时&am…

如何学习ClickHouse:糙快猛的大数据之路(技术要点概览)

这个系列文章用"粗快猛大模型问答讲故事"的创新学习方法&#xff0c;让你轻松理解复杂知识&#xff01;涵盖Hadoop、Spark、MySQL、Flink、Clickhouse、Hive、Presto等大数据所有热门技术栈&#xff0c;每篇万字长文。时间紧&#xff1f;只看开头20%就能有收获&#…

英文润色工具 Quillbot

QuillBot 是一种基于人工智能&#xff08;AI&#xff09;的写作辅助工具&#xff0c;旨在帮助用户改进他们的写作质量和效率。它提供了多种功能&#xff0c;包括但不限于改写、总结、语法检查和词汇增强。以下是对 QuillBot 软件的一些详细介绍&#xff1a; ### 主要功能 1. …

如何智能便捷、自动化地进行文件数据采集?

文件数据采集是指从各种源头和渠道收集、整理、清洗、分析和挖掘数据的过程。它是大数据应用的基础&#xff0c;为企业提供全面的决策支持和业务价值。文件数据采集对于不同行业都至关重要&#xff0c;通过有效的文件数据采集&#xff0c;企业可以更好地了解市场动态、优化服务…

Elasticsearch(ES)版本升级

ES版本升级&#xff0c;索引不兼容&#xff0c;必须删除或重新索引它们才能升级到更高版本 1ES6升级到ES7&#xff0c;必须先升级到6.8 Elasticsearch 6.8.x ✔ 2ES7升级到ES8&#xff0c;必须先升级到7.17 Elasticsearch 7.17.x ✔ 3ES8升级到ES8无差异化升级 Elasticsearc…

数据驱动未来:构建下一代湖仓一体电商数据分析平台,引领实时商业智能革命

1.1 项目背景 本项目是一个创新的湖仓一体实时电商数据分析平台&#xff0c;旨在为电商平台提供深度的数据洞察和业务分析。技术层面&#xff0c;项目涵盖了从基础架构搭建到大数据技术组件的集成&#xff0c;采用了湖仓一体的设计理念&#xff0c;实现了数据仓库与数据湖的有…

pytorch3d的安装

在这个网址中&#xff0c;下载对应的pytorch3d安装包 https://anaconda.org/pytorch3d/pytorch3d/files下载完成后使用下面命令进行安装 conda install ./pytorch3d-0.7.7-py39_cu118_pyt201.tar.bz2

【Golang 面试 - 进阶题】每日 3 题(三)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

web基础及http协议、

⼀、web基本概念和常识 Web&#xff1a;为⽤户提供的⼀种在互联⽹上浏览信息的服务&#xff0c;Web 服 务是动态的、可交 互的、跨平台的和图形化的。Web 服务为⽤户提供各种互联⽹服务&#xff0c;这些服务包括信息浏览 服务&#xff0c;以及各种交互式服务&#xff0c;包括聊…

芋道微服务全栈开发日记(商品sku数据归类为规格属性)

商品的每一条规格和属性在数据库里都是单一的一条数据&#xff0c;从数据库里查出来后&#xff0c;该怎么归类为对应的规格和属性值&#xff1f;如下图&#xff1a; 在商城模块&#xff0c;商品的单规格、多规格、单属性、多属性功能可以说是非常完整&#xff0c;如下图&#x…

web、http协议、apache服务、nginx服务

web基本概念和常识 概念 web&#xff1a;为用户提供的一种在互联网上浏览信息的服务&#xff0c;是动态的、可交互的、跨平台的和图形化的&#xff1b; 为用户提供各种互联网服务&#xff0c;这些服务包括浏览服务以及各种交互式服务&#xff0c;包括聊天、购物等&#xff1…

Arpg 游戏 对象实体 entity(二)

设计一个 entity class&#xff0c;用于创建和管理游戏世界中的实体对象。这个类包含了大量的函数&#xff0c;用于处理实体的各种行为和属性&#xff0c;例如创建、移动、播放动画、同步状态等。 CreateTitle 函数的作用是创建和初始化一个实体的标题显示。在游戏或模拟程序中…