初阶数据结构3 栈和队列

1. 栈

1.1 概念与结构

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

栈的底层结构
理论来说栈的底层结构可以是链表可以是数组,但是数组更好实现,因为栈只在栈底插入数组,链表尾插要找尾结,时间复杂度为O(N),而数组尾插时间复杂度为O(1)

1.2 栈的实现

typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
初始化栈
void STInit(ST* ps);
销毁栈
void STDestroy(ST* ps);
⼊栈
void STPush(ST* ps, STDataType x);
出栈
void STPop(ST* ps);
取栈顶元素
STDataType STTop(ST* ps);
获取栈中有效元素个数
int STSize(ST* ps);
栈是否为空
bool STEmpty(ST* ps);


编写代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void STInitialise(ST* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

void STDestroy(ST* ps)
{
	assert(ps);
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//判断空间是否充足
	if (ps->top == ps->capacity)
	{
		//增容//0*2 = 0
		//若capacity为0,给个默认值,否则×2倍
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, NewCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = NewCapacity;
	}
	ps->arr[ps->top++] = x;
}

bool StackEmpty(ST* ps)
{
	if (ps->top)
	{
		return true;
	}
	return false;
}

void StackPop(ST* ps)
{
	assert(ps);
	assert(StackEmpty(ps));
	ps->top--;
}

STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(StackEmpty(ps));
	return ps->arr[ps->top - 1];
}

int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}


代码和顺序表的内容基本大差不差,非常好实现

2. 队列

2.1 概念与结构

概念:只允许在⼀端进⾏插⼊数据操作,在另⼀端进⾏删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
⼊队列:进⾏插⼊操作的⼀端称为队尾
出队列:进⾏删除操作的⼀端称为队头
在这里插入图片描述

如同我们上学的时候如同,后来的人排队尾,只有队头的人才能走。禁止插队的说


2.2 队列的实现

队列的底层结构

队列的底层结构当然也是数组和链表都行,但是我们更倾向链表。队列队头出数据,用数组去实现的话所有数据都左移一位。链表直接头删就行了。队列队尾插入数据用数组直接插入,而链表要去找尾结点。
当然这里就很巧妙,我直接标识一下尾结点就像头结点一样不就行了吗?但是你队头出数据时用数组有什么巧妙的方法吗,博主的话是想不到。


typedef int QDataType;
队列结点结构
typedef struct QueueNode
{
int val;
struct QueueNode* next;
}QNode;
队列结构
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}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);
队列判空
bool QueueEmpty(Queue* pq);
队列有效元素个数
int QueueSize(Queue* pq);


编写代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void QueueInitialise(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	if (pq->phead == NULL && pq->ptail == NULL)
	{
		return false;
	}
	return true;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* NewNode = (QueueNode*)malloc(sizeof(QueueNode));
	if (NewNode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	NewNode->data = x;
	NewNode->next = NULL;
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = NewNode;
	}
	else
	{
		pq->ptail->next = NewNode;
		pq->ptail = NewNode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(QueueEmpty(pq));
	QueueNode* Next = pq->phead->next;
	if (pq->phead == pq->ptail)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	free(pq->phead);
	pq->phead = NULL;
	pq->phead = Next;
	pq->size--;
}

当队列只有一个结点的情况下我们free队头,此时队头和队尾指向相同,那队头就变成野指针了,所以要判断只有一个结点的情况


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 QueueSize(Queue* pq)
{
	assert(pq);
	/*size_t size = 0;
	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}*/
	return pq->size;
}

如果不用实现队列有效元素个数这个接口的话就不用在队列中定义size了,如果是链表的话我们当然可以去遍历然后拿计数器++求元素个数。但在队列这里就不规范了,因为队列不能遍历(while(pcur)),好伐我感觉这里还是比较灵活。我觉得主要原因是因为有个size我们就可以直接返回了。遍历复杂度为O(N)不够高效


void QueueDestroy(Queue* pq)
{
	assert(pq);
	assert(QueueEmpty(pq));

	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		QueueNode* Next = pcur->next;
		free(pcur);
		pcur = Next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

虽然说队列不能遍历,但是不遍历怎么去销毁每个结点感觉还是有点矛盾的


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值