栈与队列相关知识点

栈与队列是比较常用的两种数据结构,其中他们分别有特别的使用方法。今天,我们先来简单了解一下栈与队列的实现

栈是一种特殊的线性表,只在固定的一段进行插入和删除元素操作。同时进行压栈和出栈的地方被称为栈顶。栈的运转方式称为后进先出。接下来是栈的结构体内容

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

此次是用数组实现栈,栈的结构体包括一个指定类型的数组,栈顶(指向栈顶的下标),栈的容量。用链表也能实现栈,此处不赘叙。

栈的初始化

void StackInit(Stack* phead)
{
	phead->_a = NULL;
	phead->_top = -1;
	phead->_capacity = 0;
}

首先将栈中数组调整为空指针,不指向任何地址。接着将栈顶调整为-1,因为此时并没有数据,当进来第一个数据,存在0下标处时,这里便是0.接着将容量调整为0。

压栈

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->_capacity-1 == ps->_top)
	{
		int newcapacity = ps->_capacity == 0? 4:ps->_capacity * 2;
		STDataType* b = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
		if (b == NULL)
		{
			exit(1);
		}
		ps->_a = b;
		ps->_capacity = newcapacity;
	}
	ps->_a[++(ps->_top)] = data;
}

在压栈函数中,首先断言一下传过来的栈结构体是否为空指针。接着判断此时栈顶是否等于栈的容量-1。若是,则设一个int类型newcapacity,若此时栈容量依旧为0,则将他改为4;若此时栈容量不为0,则将他翻倍。接着realloc出一处地址,大小为newcapacity。若此处地址创建成功,则将目标栈的数组变为此处地址,并将栈的大小变为newcapacity。在最后,++栈顶并赋予栈顶目标数据。

出栈

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_top >= 0);
	ps->_top--;
}

出栈较为简单,首先将输入的ps和ps中top给断言一下,防止出现top小于0的情况发生。接着直接将top--即可,暂不需要free掉无用的数据。

取栈顶数据

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top >= 0);
	return ps->_a[ps->_top];
}

栈顶数据的提取也颇为简单,首先也是先断言一下保证ps非空也不是空指针。接着直接返回栈顶数据即可。

取栈内大小

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top + 1;
}

直接返回栈顶+1即可。

栈判空

int StackEmpty(Stack* ps)
{
	assert(ps);
	assert(ps->_top >= 0);
	return ps->_top == 0;
}

当top==0的话,既条件成立,返回1,;当不等于,既条件不成立,返回0.

栈销毁

void StackDestroy(Stack* ps)
{
	assert(ps);
	assert(ps->_top >= 0);
	free(ps->_a);
	ps->_top = -1;
	ps->_a = NULL;
	ps->_capacity = 0;
}

首先将ps中的数组a free掉并置为空指针,然后将top调整为最初的-1再把capacity调整为0.

队列

与栈类似,队列也只能在前后端进行插入和删除,但是队列属于先进先出,既删除数据只能从头部删除,插入数据只能从后面删除。队列我们用链表实现。用数组在队头删除数据会很麻烦。

typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* _next;
	QDataType _data;
}QNode;

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

队列基础结构如上图所示。一个队列应该包括队头节点与队尾节点,为了方便,包括一个int类型的size来记录队列中数据多少。而每个节点包括一个同类型的next指针和一个数据data。

队列初始化

void QueueInit(Queue* q)
{

	q->_front = q->_tail = NULL;
	q->size = 0;
}

队列的初始化很简单,只要把头尾节点全部置为NULL即可,接着把size置为0。

队列插入

void QueuePush(Queue* q, QDataType data)
{
	QNode* a = (QNode*)malloc(sizeof(QNode));
	a->_data = data;
	a->_next = NULL;
	if (q->_front == NULL)
	{
		q->_front = q->_tail = a;
	}
	else
	{	
		q->_tail->_next = a;
		q->_tail = a;
	}
	q->size++;
}

首先先malloc出一处地址,该处地址大小与节点一样,接着将该节点的数据调为目标数据,把他的next指针调为NULL。如果那时q的头节点依然为空指针,那么就将头尾节点都设为开辟出来的a节点。如果非空了(有内容了),就将q的尾指针设置为a。然后将size自加。

队列删除

void QueuePop(Queue* q)
{
	assert(q->_front);

	if (q->_front->_next == NULL)
	{
		free(q->_front);
		q->_front = q->_tail = NULL;
	}
	else
	{
		QNode* Next = q->_front->_next;
		free(q->_front);
		q->_front = Next;
	}

	q->size--;
}

如果头指针的下一个节点已经是空指针,既队列只有一个节点,那么free掉头节头后,直接将头节点和尾节点都置为NULL。如果不是,则设置一个QNode类型的Next指针来接受头指针的下一个节点。然后free掉头指针,然后将头指针调整为Next。最后将size自减即可。

返回队头/尾数据与队列大小

QDataType QueueFront(Queue* q) 
{
	assert(q->_front);
	return q->_front->_data;
}

QDataType QueueBack(Queue* q)
{
	assert(q->_front);
	return q->_tail->_data;
}

int QueueSize(Queue* q)
{
	return q->size;
}

此三个函数类型相似,先断言保证指针非空,接着返回对应的数据即可。

队列判空

int QueueEmpty(Queue* q)
{
		return q->size == 0;
}

当size确实为0时,返回1,当不为0时返回0.

队列销毁

void QueueDestroy(Queue* q)
{
	QNode* tmp = q->_front;
	while (tmp)
	{
		QNode* Next = tmp->_next;
		free(tmp);
		tmp = Next;
	}
	free(q);
}

首先设定一个QNode的类型来保存队头的指针,然后利用while一个一个将tmp给销毁掉。当所有节点都被销毁后,销毁掉队列指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值