【数据结构】栈和队列的实现(结尾附源码)

目录

1 栈

1.1 栈的概念及结构

1.2 栈的实现

1.2.1 动态栈的初始化和销毁

1.2.2 入栈和出栈

1.2.3 获取栈内有效元素个数

2 队列

2.1 队列的概念及结构

2.2 队列的实现

2.2.1 队列的初始化和销毁

2.2.2 队列的插入和删除

2.2.3 获取头尾元素以及队列有效元素个数

3 源码

3.1 栈

3.1.1 Stack.h

3.1.2 Stack.c 

3.2 队列

3.2.1 Quene.h

3.2.2 Quene.c


1 栈

1.1 栈的概念及结构

栈是一种特殊的数据结构。是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。

在栈中,进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(也可以叫先进后出)的原则。

此外,关于栈还有两个专业名词,压栈和出栈。

压栈:栈的插入操作叫做压栈/进栈/入栈,插入的数据在栈顶。

出栈:栈的删除操作叫做出栈,删除的数据在栈顶。

1.2 栈的实现

想要实现栈可以采用数组或者链表的方式。相对而言使用数组的方式更好,因为在数组中可以实现下标的随机访问,从尾部插入数据更方便。

我们初步设想的逻辑结构是:创建一个数组,数组的首元素就是栈底、最后一个元素是栈顶,通过操作这个数组来间接操作栈。

 

 在具体实现时,栈还分为静态和动态两种方式,以下是静态栈的结构

typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;

实际操作中,一般我们很难估计数据的数量,所以静态栈不是很常用。一般会采用动态栈的方式。

typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;

1.2.1 动态栈的初始化和销毁

void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;//top将会指向栈顶元素的下一个位置
	//pst->top=-1   //top将会指向栈顶元素的位置
	pst->capacity = 0;
}

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

初始化和销毁是比较常规的操作,但是这里有一个需要注意的地方,在初始化时,如果我们将top赋值为0,由于每一次插入数据之后,top都需要自增一次,这就会导致top的值比栈顶元素所在处的下标大1。这会在之后的操作中造成影响,所以这一点必须清楚才能在之后做出改变。

如果想要直截了当的让top的值就是栈顶元素的下标,那么可以将top置为-1。

当top取值不同时,下面的很多操作也会有区别。在这里为了规避这些区别,我们统一将top的值置为0。

1.2.2 入栈和出栈

由于我们采用的是数组实现栈,所以入栈操作在逻辑上还是比较简单的,就是在数组的尾部插入。但是这里需要注意一个问题,由于数组的空间是动态开辟的。因此在每一次插入之前,都需要判断一下空间是否还足够。如果不够就需要扩容。

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

	if (pst->top == pst->capacity)  //如果容量已满
	{
		int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
		STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

如果在上一步将top赋值为-1,那么这里的第一个判断条件就应该改为pst->top + 1 == pst->capacity。

出栈操作就省心的多,首先判断一下栈是否为空,不为空就直接让top--,使top指向删除元素的前一个元素,让这个元素成为新的栈顶。

要注意的是,这样做并不是真正的将原来的栈顶元素删除,但是这里不将其删除并不会造成影响。

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}

我们发现,在执行出栈操作时,栈有可能是空的状态。因此可以通过布尔值来判断栈是否为空。

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

	return pst->top == 0; 
}

如果我们想知道某个时刻栈顶元素的值,还可以编写一个获取栈顶元素的函数。

STDatatype STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

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

在栈的实际应用中,获取栈顶元素与出栈通常是配套使用的。

1.2.3 获取栈内有效元素个数

有时候我们想要知道栈内存放了多少个有效的数据,那就可以编写一个函数。

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

	return  pst->top;
}

当然,如果在之前top的初始值为-1,那么这里就需要返回top+1。

2 队列

2.1 队列的概念及结构

队列是一种与栈相对的数据结构。只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

进行插入操作的一端叫做队尾,进行删除操作的一端叫做队头。

2.2 队列的实现

队列也可以用数组或者链表的方式实现。但是由于队列需要对头部进行删除操作,使用数组的话需要逐个挪动数据,时间代价太大。因此在实现队列时,采用链表的方式更加恰当。

队列中每个节点的结构如下:

typedef int QDatatype;
typedef struct QueneNode
{
	struct QueneNode* next;
	QDatatype data;
}QNode;

在这里,由于我们在操作队列时,总是需要知道队列的头部和尾部,因此就需要两个指针来记录头指针和尾指针,以及记录队列的数据个数。因此我们可以在创建一个结构体,来存放这些数据。

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

在这个结构中,链表的头结点就是队头,尾结点就是队尾。 

2.2.1 队列的初始化和销毁

初始化和销毁与单链表的非常类似,这里直接放源码。不清楚的可以查看这篇文章中的相关部分:【数据结构】无头+单向+非循环链表的增删查改(结尾附源码)

void QueneInit(Quene* pq)
{
	assert(pq);

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

void QueneDestroy(Quene* 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;
}

2.2.2 队列的插入和删除

插入删除操作也是同单链表的插入删除类似。这里有区别的是,因为在队列中有两个结构,一个是队列中结点的结构,一个是队列本身的结构。因此在执行插入删除操作时,需要考虑是否会影响到队列的本身结构。所以需要增加一些判断条件。

//插入(相当于尾插)
void QuenePush(Quene* pq, QDatatype x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

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

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

//删除(相当于头删)
void QuenePop(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = NULL;
	}
	else
	{
		QNode* cur = pq->phead->next;
		free(pq->phead);
		pq->phead = cur;
	}
	
	pq->size--;
}

2.2.3 获取头尾元素以及队列有效元素个数

这三个函数就比较直观了,直接上代码

//获取队头元素
QDatatype QueneFront(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->phead->data;
}

//获取队尾元素
QDatatype QueneBack(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->ptail->data;
}

//队列中有效元素个数
int QueneSize(Quene* pq)
{
	assert(pq);

	return pq->size;
}

3 源码

3.1 栈

3.1.1 Stack.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdbool.h>

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

void STInit(ST* pst);

void STDestory(ST* pst);

void STPush(ST* pst, STDatatype x);

void STPop(ST* pst);

STDatatype STTop(ST* pst);

bool STEmpty(ST* pst);

int STSize(ST* pst);

3.1.2 Stack.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;//top将会指向栈顶元素的下一个位置
	//pst->top=-1   //top将会指向栈顶元素的位置
	pst->capacity = 0;
}

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

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

	if (pst->top == pst->capacity)
	{
		int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
		STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}

STDatatype STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

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

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

	return pst->top == 0;
}

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

	return  pst->top;
}

3.2 队列

3.2.1 Quene.h

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

typedef int QDatatype;
typedef struct QueneNode
{
	struct QueneNode* next;
	QDatatype data;
}QNode;

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

void QueneInit(Quene* pq);
void QueneDestroy(Quene* pq);
void QuenePush(Quene* pq,QDatatype x);
void QuenePop(Quene* pq);
QDatatype QueneFront(Quene* pq);
QDatatype QueneBack(Quene* pq);
int QueneSize(Quene* pq);
bool QueneEmpty(Quene* pq);

3.2.2 Quene.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Quene.h"

bool QueneEmpty(Quene* pq)
{
	assert(pq);
	return pq->size == 0;
}

void QueneInit(Quene* pq)
{
	assert(pq);

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

void QueneDestroy(Quene* 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;
}

void QuenePush(Quene* pq, QDatatype x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

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

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

void QuenePop(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = NULL;
	}
	else
	{
		QNode* cur = pq->phead->next;
		free(pq->phead);
		pq->phead = cur;
	}
	
	pq->size--;
}

QDatatype QueneFront(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->phead->data;
}

QDatatype QueneBack(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->ptail->data;
}

int QueneSize(Quene* pq)
{
	assert(pq);

	return pq->size;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值