数据结构——栈和队列

一、栈💬

1. 栈的概念和结构

栈(stack)又名堆栈,它是一种运算受限的线性表。
限定仅在表尾进行插入和删除操作的线性表(尾插和尾删),这一端被称为栈顶。相对地,把另一端称为栈底。
向一个栈插入新元素又称作进栈/入栈/压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;
从一个栈删除元素又称作出栈/退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

先进后出:FILO

2. 栈的实现

2.1 用什么结构来实现栈❓

栈是线性表,所以我们有许多种选择来实现它:静态顺序表、动态顺序表、单链表、双向链表。

在顺序表和链表中选择一个,我们选择顺序表,为什么呢?这涉及到一个知识点:缓存命中率
关于缓存命中率是什么,请参考博客:缓存命中率
简单来说,就是顺序表的调用效率高,链表的调用效率低一些。
而无疑我们抛弃静态,选择动态顺序表以方便管理。

实际上选择用哪种结构来实现栈和队列都是可以的,这取决于我们自己,以及适用的场景,在这里,我们仅讨论最简单的方式。

请添加图片描述

2.2 栈的实现代码⭐️
1️⃣2.2.1 头文件 Stack.h
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int capacity;
	int top;
}ST;

void STInit(ST* ps);

void STDestory(ST* ps);

void STPush(ST* ps, STDataType x);

void STPop(ST* ps);

int STSize(ST* ps);

bool STEmpty(ST* ps);

STDataType STTop(ST* ps);
2️⃣2.2.2 函数代码 Stack.c
#include"Stack.h"

void STInit(ST* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	ps->capacity = 4;
	ps->top = 0;
}

void STDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void STPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->capacity *= 2;
		ps->a = tmp;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	ps->top--;
}

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

bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->a[ps->top - 1];
}
3️⃣2.2.3 测试文件 Test.c
#include"Stack.h"

void StackTest()
{
	ST st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	STPush(&st, 5);
	while (!STEmpty(&st))
	{
		STDataType ret = STTop(&st);
		printf("%d ", ret);
		STPop(&st);
	}
}

int main()
{
	StackTest();
	return 0;
}

二、队列💬

1. 队列的概念和结构❓

队列也是一种受限定的特殊线性表。
它只允许在表的前端进行删除操作,而在表的后端进行插入操作(尾插和头删)。
进行插入操作的端称为队尾,进行删除操作的端称为队头。
在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。

先进先出:FIFO

2. 队列的实现

2.1 用什么结构来实现队列❓

队列也是线性表,所以我们同样有许多种选择来实现它:静态顺序表、动态顺序表、单链表、双向链表。

但此时我们就要选择链表,而不是顺序表了。
理由:队列需要头删操作,如果使用顺序表,头删操作将挪动n个数据,效率很低。不如使用链表。

而链表只需单链表就足够了,无需双向、循环、带头。(当然,有些场景下加上双向循环带头更方便,这里我们只讨论一般情况下。)

我们在使用队列的时候一般会用到尾指针(一般的单链表只用头指针)。我们在单链表中各种接口的传参一般只有头指针,如果用尾指针就需要再传入一个参数尾指针,这样比较繁琐,我们不如把头指针和尾指针整合在一起,放到一个结构体内,这样我们传参的时候就可以只传入一个新的结构体指针就行了,因此我们创建队列的时候可以创建成如下的形式:

在这里插入图片描述

2.2 队列的实现代码⭐️
1️⃣2.2.1 头文件 Queue.h
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;


void QInit(Queue* ps);
void QDestory(Queue* ps);

void QPush(Queue* ps, QDataType x);
void QPop(Queue* ps);

bool QEmpty(Queue* ps);
int QSize(Queue* ps);

QDataType QFront(Queue* ps);
QDataType QBack(Queue* ps);
2️⃣2.2.2 函数代码 Queue.c
#include"Queue.h"

void QInit(Queue* ps)
{
	assert(ps);
	ps->head = ps->tail = NULL;
}
void QDestory(Queue* ps)
{
	assert(ps);
	for (QNode* cur = ps->head; cur <= ps->tail;)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	ps->head = ps->tail = NULL;
}

void QPush(Queue* ps, QDataType x)
{
	assert(ps);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (ps->head == NULL)
	{
		ps->head = ps->tail = newnode;
	}
	else
	{
		ps->tail->next = newnode;
		ps->tail = newnode;
	}
}
void QPop(Queue* ps)
{
	assert(ps);
	QNode* newhead = ps->head->next;
	free(ps->head);
	ps->head = newhead;
}

bool QEmpty(Queue* ps)
{
	assert(ps);
	return ps->head == NULL;
}
int QSize(Queue* ps)
{
	assert(ps);
	if (ps->head == NULL)
		return 0;
	if (ps->head == ps->tail)
		return 1;
	int count = 0;
	QNode* cur = ps->head;
	while (cur != ps->tail)
	{
		count++;
		cur = cur->next;
	}
	return count + 1;
}

QDataType QFront(Queue* ps)
{
	assert(ps);
	assert(!QEmpty(ps));
	return ps->head->data;
}
QDataType QBack(Queue* ps)
{
	assert(ps);
	assert(!QEmpty(ps));
	return ps->tail->data;
}
3️⃣2.2.3 测试文件 Test.c
#include"Queue.h"

void Test()
{
	Queue ps;
	QInit(&ps);
	QPush(&ps, 1);
	QPush(&ps, 2);
	QPush(&ps, 3);
	int sz = QSize(&ps);
	printf("%d ", sz);
	printf("%d ", QBack(&ps));
	QDestory(&ps);
}

int main()
{
	Test();
	return 0;
}

ssert(ps);
assert(!QEmpty(ps));
return ps->tail->data;
}




##### 3️⃣2.2.3 测试文件 Test.c

```c
#include"Queue.h"

void Test()
{
	Queue ps;
	QInit(&ps);
	QPush(&ps, 1);
	QPush(&ps, 2);
	QPush(&ps, 3);
	int sz = QSize(&ps);
	printf("%d ", sz);
	printf("%d ", QBack(&ps));
	QDestory(&ps);
}

int main()
{
	Test();
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值