栈与队列的故事

​​​​​​​


目录

​编辑​​​​​​​

一、栈

1.1栈的概念及结构

1.2栈的实现

1、实现支持动态增长的栈

2、初始化栈

3、入栈

4、出栈

5、获取栈顶元素

6、检测栈是否为空

7、获取栈中有效元素个数

8、销毁栈

9、测试

1.3源码

二、队列

2.1队列的概念及结构

2.2队列的实现

1、链式结构:表示队列

2、队列的结构

3、初始化队列

4、队尾入队列

5、队头出队列

6、获取队列队头元素

7、获取队列队尾元素

8、获取队列中有效元素个数

9、检测队列是否为空

10、销毁队列

11、测试

2.3源码


一、栈

1.1栈的概念及结构

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

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
  • 出栈:栈的删除操作叫做出栈,出数据也在栈顶。

1.2栈的实现

栈的实现一般可以使用数组或者链表实现:

  • 对于链表而言又有双向链表和单链表,如果用双向链表实现,栈顶既可以是尾,也可以是头。如果用单链表实现,栈顶只能是头,因为单链表尾插容易,尾删却比较麻烦;而头插头删都很容易。
  • 相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

1、实现支持动态增长的栈

typedef int STDataType;

typedef struct stack
{
	STDataType* arr;
	int top;      //栈顶
	int capacity; //容量
}ST;

2、初始化栈

因为这个结构体肯定不可能为空,所以我们直接先断言它。然后,这里还有一个要特别注意的点,如果我们初始化时top给0,那top指向的就是栈顶元素的下一个位置;如果top给-1,那top就指向栈顶元素

void STInit(ST* pst)
{
	assert(pst);

	pst->arr = NULL;
	pst->capacity = 0;

	//表示top指向栈顶元素的下一个位置
	pst->top = 0;
	表示top指向栈顶元素
	//pst->top = -1;
}

3、入栈

入栈时如果空间不够,先扩容;因为top此时是指向栈顶元素的下一个位置,所以我们直接将要入栈的元素x放在top位置,然后让top指向下一个位置就行。

void STPush(ST* pst, STDataType x)
{
	assert(pst);

	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->arr, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->arr = tmp;
		pst->capacity = newcapacity;
	}

	pst->arr[pst->top] = x;
	pst->top++;

    //如果top初始化时为-1,入栈时就要top先++,再将元素放进去
	//pst->top++;
	//pst->arr[pst->top] = x;
}

4、出栈

在出栈时要注意如果top为0就不能在删除了,所以要断言一下。

void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	pst->top--;
}

5、获取栈顶元素

因为top是指向栈顶元素的下一个位置,所以要获取栈顶元素只需要top-1就行。

STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

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

6、检测栈是否为空

如果栈为空,那top就等于0,表达式的结果就为真;如果表达式的结果为假,就代表栈不为空。

bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

7、获取栈中有效元素个数

int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

8、销毁栈

void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->arr);
	pst->arr = NULL;

	pst->top = pst->capacity = 0;
}

9、测试

int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	STPush(&s, 4);
	STPush(&s, 5);
	while (!STEmpty(&s))
	{
		printf("%d ", STTop(&s));
		STPop(&s);
	}
	printf("\n");
	STDestroy(&s);

	return 0;
}

1.3源码

🌻Stack.h

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


typedef int STDataType;

typedef struct stack
{
	STDataType* arr;
	int top;      //栈顶
	int capacity; //容量
}ST;

//初始化
void STInit(ST* pst);
//销毁栈
void STDestroy(ST* pst);
//入栈
void STPush(ST* pst, STDataType x);
//出栈
void STPop(ST* pst);
//获取栈顶元素
STDataType STTop(ST* pst);
//检测栈是否为空,如果为空,返回真;如果不为空,返回假
bool STEmpty(ST* pst);
//获取栈中有效元素个数
int STSize(ST* pst);

🌻Stack.c

#include "stack.h"

//初始化
void STInit(ST* pst)
{
	assert(pst);

	pst->arr = NULL;
	pst->capacity = 0;

	//表示top指向栈顶元素的下一个位置
	pst->top = 0;

    //表示top指向栈顶元素
	//pst->top = -1;
}

//销毁栈
void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->arr);
	pst->arr = NULL;

	pst->top = pst->capacity = 0;
}

//入栈
void STPush(ST* pst, STDataType x)
{
	assert(pst);

	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->arr, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->arr = tmp;
		pst->capacity = newcapacity;
	}
    
    //如果top初始化时为0,入栈时就要先将元素放进去,再将top++
	pst->arr[pst->top] = x;
	pst->top++;

	//如果top初始化时为-1,入栈时就要top先++,再将元素放进去
	//pst->top++;
	//pst->arr[pst->top] = x;
}

//出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	pst->top--;
}

//获取栈顶元素
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

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

//检测栈是否为空,如果为空,返回真;如果不为空,返回假
bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

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

	return pst->top;
}

二、队列

2.1队列的概念及结构

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

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

2.2队列的实现

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

1、链式结构:表示队列

typedef int QDataType;

//链式结构:表示队列
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

2、队列的结构

因为队列需要队头和队尾两个指针,所以为了方便管理,我们可以把它俩放在一个结构体里边。

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
    int size;
}Queue;

3、初始化队列

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

4、队尾入队列

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

5、队头出队列

出队时得考虑两种情况,一种是队列为空,另一种是队列中只有一个节点;如果队列为空,我们只需断言一下就行。如果队列中只有一个节点,我们先保存当前节点,然后让头指针指向下一个节点,接着free掉保存的节点,这时如果头指针是指向空的,我们就把尾指针也置空。

void QueuePop(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	if (pq->phead == NULL)
	{
		pq->ptail = NULL;
	}

    pq->size--;
}

6、获取队列队头元素

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	return pq->phead->val;
}

7、获取队列队尾元素

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->ptail);

	return pq->ptail->val;
}

8、获取队列中有效元素个数

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

9、检测队列是否为空

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

10、销毁队列

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->size = 0;
}

11、测试

int main()
{
	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");
	QueueDestroy(&q);
	return 0;
}

2.3源码

🌻Queue.h

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

typedef int QDataType;

//链式结构:表示队列
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

//队列的结构
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* pq);
//队尾入队列
void QueuePush(Queue* pq, QDataType x);
//队头出队列
void QueuePop(Queue* pq);
//获取队列队头元素
QDataType QueueFront(Queue* pq);
//获取队列队尾元素
QDataType QueueBack(Queue* pq);
//获取队列中有效元素个数
int QueueSize(Queue* pq);
//检测队列是否为空
bool QueueEmpty(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);

🌻Queue.c

#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);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

//队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	if (pq->phead == NULL)
	{
		pq->ptail = NULL;
	}

	pq->size--;
}

//获取队列队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	return pq->phead->val;
}

//获取队列队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->ptail);

	return pq->ptail->val;
}

//获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

//销毁队列
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->size = 0;
}
  • 101
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 78
    评论
评论 78
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南风与鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值