欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析2
目录
👉🏻栈
栈概念
栈其实就是一种顺序表
,在函数栈帧的开辟和销毁那,我们其实对于栈已经有了一定的了解。
在开辟一个栈后,我们可以执行入栈、出栈,而在执行入栈、出栈时,我们需要严格遵循一个原则“后进先出”
即后入栈的元素先出栈
我们接下来要遵循这个原则去实现栈的以下这些功能:
1.栈的初始化
2.栈的销毁
3.入栈
4.出栈
5.返回栈的栈顶元素
6.判断栈是否为空
1.栈的初始化
一个栈中需要具备这些元素:
- 数组:作为线性表我们自然的创建一个数组作为栈的空间
- 栈顶位置:这个为了方便后面我们获取栈顶元素和出栈
- 栈容量:为了方便入栈和判栈是否为空
于是我们创建由这些元素组成的结构体👇🏻
typedef int TypeDataStack;
typedef struct Stack
{
TypeDataStack* a;
int top;
int capacity;
}ST;
栈的雏形已经有了,但是当我们创建一个栈的时候,我们需要对栈进行初始化
首先我们就得对数组进行初始化,因为此时数组未进行初始化,还只是个野指针,所以我们令其为NULL,而top和capacity为0;
初始化为👇🏻
void InitST(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;//top=0指的是栈顶后一位数据
}
2.栈的销毁
栈的销毁就很容易了,我们直接将开辟的动态数组销毁即可,而后记得将栈中的top和capacity统统重置为0;
void DestroyST(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
3.入栈
入栈也很容易,就是在栈顶位置,即top处进行入栈。
但是我们在入栈的时候还要考虑栈空间容量是否足够,
如果容量不足,即判断此时已满容量的时候,我们就要进行扩容
扩容我们即在原数组基础上进行realloc
void PushST(ST* pst, TypeDataStack x)
{
assert(pst);
//要考虑容量不足扩容情况
if (pst->top == pst->capacity)
{
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
TypeDataStack* AdAfter = (TypeDataStack*)realloc(pst->a,newCapacity * sizeof(TypeDataStack));
if (AdAfter == NULL)
{
perror("realloc fail");
return;
}
pst->a = AdAfter;
pst->capacity = newCapacity;
}
//插入数据
pst->a[pst->top] = x;
pst->top++;
}
4.出栈
出栈则更简单,我们只需要将top–,将栈顶位置下移即可。
但我们仍要考虑空栈情况,如果空栈的时候,我们可不能进行出栈了。
void PopST(ST* pst)
{
assert(pst);
//还得断言判断一下是否为空栈
assert(!STEmpty(pst));
pst->top--;
}
5.返回栈的栈顶元素
返回top位置的元素。
但如果为空栈,则无元素。
TypeDataStack TopST(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1];
}
6.判断栈是否为空
如果top为0,则意味着栈为空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;//为真返回1,假则返回0
}
Stack.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
typedef int TypeDataStack;
typedef struct Stack
{
TypeDataStack* a;
int top;
int capacity;
}ST;
void InitST(ST* pst);
void DestroyST(ST* pst);
void PushST(ST* pst, TypeDataStack x);
void PopST(ST* pst);
TypeDataStack TopST(ST* pst);//返回top元素
bool STEmpty(ST* pst);
int STSize(ST* pst);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void InitST(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;//top=0指的是栈顶后一位数据
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;//为真返回1,假则返回0
}
void DestroyST(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
void PushST(ST* pst, TypeDataStack x)
{
assert(pst);
//要考虑容量不足扩容情况
if (pst->top == pst->capacity)
{
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
TypeDataStack* AdAfter = (TypeDataStack*)realloc(pst->a,newCapacity * sizeof(TypeDataStack));
if (AdAfter == NULL)
{
perror("realloc fail");
return;
}
pst->a = AdAfter;
pst->capacity = newCapacity;
}
//插入数据
pst->a[pst->top] = x;
pst->top++;
}
void PopST(ST* pst)
{
assert(pst);
//还得断言判断一下是否为空栈
assert(!STEmpty(pst));
pst->top--;
}
TypeDataStack TopST(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1];
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
👉🏻队列
队列概念
队列实质上是一个单链表,记得我们在食堂吃饭排队的时候吗,我们必须等到前一个人点完餐后才能轮到下一个人,所谓先到先得,与栈所遵循的原则不同,队列所遵循的原则为先进先出
而接下来我们就要遵循这个原则实现队列的以下功能:
1.队列的初始化
2.队列的销毁
3.入列
4.出列
5.返回队列的头部
6.返回队列的尾部
7.判断队列是否为空
1.队列的初始化
队列的组成元素主要有:
1.下一个结点的地址
2.当前结点的数据
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
而我们还需要再多创建一个结构体来存放队列的首地址、尾地址和队列长度,这就方便了我们进行入列和出列,省去了找尾节点的功夫
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int sz;
}Queue;
我们要先对Queue进行初始化,将其中的phead和ptail置为NULL,sz置为0
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->sz = 0;
}
2.队列的销毁
队列的销毁我们就是用while循环一直遍历到尾结点,逐一将每个结点空间进行释放。
最后我们要将Queue中的phead和ptail重置为NULL,sz置为0
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->sz = 0;
}
3.入列
入列我们就不用考虑容量问题了,我们每入列一个,就开辟一个新空间,
将尾结点的next指向新空间的地址,而后新空间成为新的尾结点。
但我们还要考虑空队列情况,空队列的时候,我们创建的新空间,此时头结点是它,尾结点也是它。
入列后记得sz++
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->data = x;
if (pq->phead == NULL)
{
assert(pq->ptail==NULL);//如果pq->phead为空,pq->tail一定为空,则说明此时队列为空
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;//newnode成为新的尾
}
pq->sz++;
}
4.出列
出列先保存头结点下一结点的地址,而后将头结点的空间释放,随机新的头结点就是刚刚保存下来的地址。
这边我们还要再考虑一个细节部分:单结点时
单结点出列后,此时为空队列了,此时phead和ptail我们都要将其置为NULL,ptail不能漏掉
出列后记得sz–;
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
//1.一个节点时,要将pq->ptail也置空
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else//2.多个节点
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->sz--;
}
5.返回队列的头部
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
6.返回队列的尾部
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
7.判断是否为空队列
若phead和ptail都为NULL,即说明此时为空队列,或者此时sz==0
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->sz == 0;
}
Queue.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int sz;
}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);//返回尾部数据
int QueueSz(Queue* pq);//返回队列长度
bool QueueEmpty(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->sz == 0;
}
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->sz = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->sz = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->data = x;
if (pq->phead == NULL)
{
assert(pq->ptail==NULL);//如果pq->phead为空,pq->tail一定为空,则说明此时队列为空
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;//newnode成为新的尾
}
pq->sz++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
//1.一个节点时,要将pq->ptail也置空
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else//2.多个节点
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->sz--;
}
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 QueueSz(Queue* pq)
{
assert(pq);
return pq->sz;
}
👉🏻LeetCode题
用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
我们知道,队列和栈所遵循的原则是相反的,前者是先进先出,后者是后进先出
用两个队列实现栈的push、top、pop 和 empty可以有以下思路:
1.push:插入比较随意,我们可以随机选取一个空队列进行插入
2.pop:当我们将数据插入到一个队列中后,想要进行pop,这时我们注意,此时我们pop的数据,对于队列来说是尾部,但是对于栈来说是头部,可是队列遵循的是头进头出,怎么让尾出呢?
我们其实可以将该队列的n个数据倒入n-1个数据到另一个队列当中,此时剩下来的这个数据就是尾部数据了,此时将其pop即可。
3.top:与pop思路一样
4.如果两个队列都为空则为空栈
原题链接:用队列实现栈
代码实现👇🏻
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int sz;
}Queue;
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->sz == 0;
}
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->sz = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->sz = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->data = x;
if (pq->phead == NULL)
{
assert(pq->ptail==NULL);//如果pq->phead为空,pq->tail一定为空,则说明此时队列为空
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;//newnode成为新的尾
}
pq->sz++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
//1.一个节点时,要将pq->ptail也置空
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else//2.多个节点
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->sz--;
}
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 QueueSz(Queue* pq)
{
assert(pq);
return pq->sz;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
if(obj==NULL)
{
perror("malloc fail");
return;
}
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
void myStackPush(MyStack* obj, int x) {
assert(obj);
if(&obj->q1==NULL)
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj) {
assert(obj);
//由于我不知道到底两个队列哪个队列为空,所以我先假设
Queue* EmptyQue=&obj->q1;//先假设q1为空
Queue* NonEmptyQue=&obj->q2;
if(!QueueEmpty(&obj->q1))//如果不为空
{
EmptyQue=&obj->q2;
NonEmptyQue=&obj->q1;
}
//接下来将非空队列中的数据倒到空队列中
while(QueueSz(NonEmptyQue)>1)
{
QueuePush(EmptyQue,QueueFront(NonEmptyQue));
QueuePop(NonEmptyQue);
}
int top=QueueBack(NonEmptyQue);
QueuePop(NonEmptyQue);
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);
}
用C语言实现比较麻烦,队列的实现还要自己写
用栈实现队列
这道题与上题有异曲同工之妙,我们直接上思路
1.入列和出列:我们专门创建一个栈pushst用来入列,一个栈popst用来出列,入列好入,我们就只在pushst中插入数据就好了,出列的话,我们就是把所有pushst的所有数据放到popst中(在存入popst的时候要记得将pushst中的数据顺便pop掉),而放入后,此时要pop的头部已经在popst的栈顶了,我们就可以直接对其进行pop
2.两个栈若都为空,则为空队列
原题链接:用栈实现队列
代码实现👇🏻
typedef int TypeDataStack;
typedef struct Stack
{
TypeDataStack* a;
int top;
int capacity;
}ST;
void InitST(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;//top=0指的是栈顶后一位数据
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;//为真返回1,假则返回0
}
void DestroyST(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
void PushST(ST* pst, TypeDataStack x)
{
assert(pst);
//要考虑容量不足扩容情况
if (pst->top == pst->capacity)
{
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
TypeDataStack* AdAfter = (TypeDataStack*)realloc(pst->a,newCapacity * sizeof(TypeDataStack));
if (AdAfter == NULL)
{
perror("realloc fail");
return;
}
pst->a = AdAfter;
pst->capacity = newCapacity;
}
//插入数据
pst->a[pst->top] = x;
pst->top++;
}
void PopST(ST* pst)
{
assert(pst);
//还得断言判断一下是否为空栈
assert(!STEmpty(pst));
pst->top--;
}
TypeDataStack TopST(ST* pst)
{
assert(pst);
return pst->a[pst->top-1];
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
typedef struct {
ST pushst;
ST popst;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
if(obj==NULL)
{
perror("malloc fail");
return ;
}
InitST(&obj->pushst);
InitST(&obj->popst);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
assert(obj);
PushST(&obj->pushst,x);
}
int myQueuePop(MyQueue* obj) {
int top=myQueuePeek(obj);
PopST(&obj->popst);
return top;
}
int myQueuePeek(MyQueue* obj) {
assert(obj);
if(STEmpty(&obj->popst))
{
while(!STEmpty(&obj->pushst))
{
PushST(&obj->popst,TopST(&obj->pushst));
PopST(&obj->pushst);
}
}
return TopST(&obj->popst);
}
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}
void myQueueFree(MyQueue* obj) {
DestroyST(&obj->pushst);
DestroyST(&obj->popst);
free(obj);
}
设计循环队列(数组法)
思路如下👇🏻
原题链接:设计循环队列
代码如下👇🏻
typedef struct {
int front;
int rear;
int k;
int* a;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
int* arr=(int*)malloc(sizeof(int)*(k+1));
obj->front=0;
obj->rear=0;
obj->a=arr;
obj->k=k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if(obj->rear==obj->front)
return 1;
else
return 0;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
int rear=obj->rear,k=obj->k;
if((rear+1)%(k+1)==obj->front)
return 1;
else
return 0;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(!myCircularQueueIsFull(obj))
{
obj->a[obj->rear]=value;
obj->rear++;
obj->rear%=(obj->k+1);
return true;
}
return false;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(!myCircularQueueIsEmpty(obj))
{
obj->front++;
obj->front%=(obj->k+1);
return true;
}
return false;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
int rear=obj->rear,k=obj->k;
return obj->a[(rear+k)%(k+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长