【数据结构】栈和队列

在这里插入图片描述

欢迎来到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);
}

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值