【数据结构】栈和队列

目录

一.栈

1.概念

2.实现

2.1栈的结构体定义

2.2栈的创建和销毁

2.3入栈和出栈

2.4取栈顶数据 

2.5判空、取元素个数

二.队列

1.概念

2.实现

2.1队列的结构体定义

2.2队列的初始化和销毁

2.3队列的插入和删除

2.4队列取头、尾值

 2.5判空、取元素个数


一.栈

1.概念

栈,作为一种特殊的线性表,只允许在其固定的一端进行插入和删除的操作,这一端称为栈顶,另一端则称为栈底

栈中的数据元素遵循后进先出原则(Last In First Out),即后进入的数据元素先出去。这一特点可以类比于现实中的弹夹、羽毛球桶(只有一个口的)等。

栈的插入操作叫做进栈/压栈/入栈,将数据插入到栈顶。栈的删除操作叫做出栈,将栈顶的数据删除。

2.实现

2.1栈的结构体定义

栈,作为一种特殊的线性表,对栈(Stack)的结构体的定义完全可以参考线性表的构成。链表和数组实现都可以,这里就使用了动态顺序表的构造方法。关于动态顺序表详解可以查看我的这篇博客:【C语言】顺序表(原理+实现)-CSDN博客

//定义存储数据的类型
typedef int STDataType;

//定义栈
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;

2.2栈的创建和销毁

创建栈时需要注意一点,top应该初始化为0吗?如果初始化为0,那么插入一个元素后,top变为1,指向的可不是栈顶元素,而是指向栈顶元素的下一个元素。如果你要想让top指向栈顶元素,那么top就必须初始化为-1.不过此刻为了与之前实现顺序表契合,选择了前者。

//创建和销毁
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}

2.3入栈和出栈

//入栈和出栈
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->capacity == pst->top)
	{
		int newcapacity = (pst->capacity == 0) ? 4 : (pst->capacity) * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		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(pst->top > 0);
	pst->top--;
}

2.4取栈顶数据 

这里必须注意要和前文初始化Stack时一致,前文初始化top为0,top则指向栈顶元素的下一个元素。因此在这里取栈顶元素时,需要取top-1下标对应的值。

//取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

2.5判空、取元素个数

说到底,理解到top所代表的意义,后面的这些问题都能迎刃而解。

//判空
bool STEmpty(ST* pst)
{
	assert(pst);
	return (pst->top == 0);
}

//获取数据个数
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

二.队列

1.概念

队列和栈一样,只允许在一端进行数据插入,但不同的是在删除数据上,栈只能从数据插入的这一端进行删除,而队列只能从另一端进行删除。如此,队列进行插入操作的一端称为队尾,进行删除操作的一端称为队头

队列遵循先进先出原则(First In First Out),就像排队一样,先排队的人先得到服务。

2.实现

2.1队列的结构体定义

队列即可以用数组实现也可以用链表实现。但使用链表的结构更优秀一些,原因在于如果使用数组,那么在对队头的处理就比较麻烦,删除下标为0的元素后,还需要整体前移,效率比较低。因此,使用链表来实现,如下定义:

//定义数据类型
typedef int QDatatype;

//定义链表的节点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype val;
}QNode;


//定义队列
//便于函数内修改指针phead,ptail,同时便于传多个参数
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

由于在构造后续的函数中,需要频繁修改phead和ptail,因此要在函数内修改指针变量,函数内改变参数指针的方式:1:传二级指针 2:传结构体指针,结构体中包含指针,通过结构体来改变里面的指针

因此,使用结构体传phead和ptail避免使用二级指针,也避免了传多个参数,直接传结构体指针即可。

2.2队列的初始化和销毁

//初始化和销毁
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	//assert(pq->phead);
	QNode* pcur = pq->phead;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

2.3队列的插入和删除

队列的插入和删除可以参考链表的尾插和头删。不过值得注意的是,在删除的过程中,必须多考虑一个只有一个节点的情况,如果按照多个节点的写法取处理一个节点,会把phead置为NULL,而ptail不进行操作,这样ptail就成为了一个野指针,在后续再进行插入操作中这是很危险的行为,因此一定要考虑这种特殊情况进行处理,在一个节点删除时,phead和ptail都置为NULL。

//插入
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);//报错不清晰
	assert(pq->size != 0);

	//一个节点
	if (pq->phead == pq->ptail)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个节点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	
	pq->size--;
}

2.4队列取头、尾值

//取头,取尾数值
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;
}

 2.5判空、取元素个数

在Queue的定义中加入size这个概念,在此就会很方便计算。

//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

//元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值