数据结构—栈和队列

栈的概念

  1. 栈是一种只允许在一端进行插入和删除操作的线性表。它是一种操作受限的线性表。在表中允许进行插入和删除的一端称为“栈顶”,另一端称为“栈底”。
  2. 栈的插入操作通常称为“入栈”或“进栈”(push),而栈的删除操作则称为“出栈”或“退栈”(pop)。当栈中无数据元素时称为“空栈”。
  3. 栈具有“后进先出”的特性。


栈的结构分析

栈只能在栈顶进行操作,最后进栈的元素第一个出栈 将栈转化成一个顺序表或链表,压栈即为对该线性表进行尾插,而入栈即为对该线性表进行尾删,而完成该操作最具优势的结构自然是顺序表。

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;	// 容量
}Stack;

栈的初始化

top可能会有两种取值:0和-1。 对于top取0,即top表示的是栈顶的下一个元素,其功能相当于size。 而若top取-1,则top表示的是栈顶的元素,当栈没有元素时,对a进行解引用a[top]则会报错。

void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

判断栈是否为空

由于top就是当前元素个数,所以判断栈是否为空,就是判断top是否为0

bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

栈顶插入数据

插入数据之前首先要判断空间容量的问题,如果空间不足,就需要进行扩容,top标记当前栈中有几个元素,capacity标记当前栈最多容纳几个元素。

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return ;
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

栈顶删除数据

删除栈顶元素即出栈,出栈前需要断言,判断当前是否有元素可以删除,调用StackEmpty函数。

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

获取栈顶元素

top所指向数组的下表总是比实际栈顶元素下标大1,所以栈顶元素的下标是top-1。

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

获取栈中有效元素个数 

因为数组元素下标从0开始,因此top就表示栈中有效元素个数。

int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

栈的销毁

销毁栈,无非就是将ps->a指向的空间释放掉。这块空间释放后,所有数据也就会跟着销毁。此时的top和capacity就要一起变为0。而ps->a由于被释放,变为了野指针,那么也是需要进行置空。

void StackDestroy(Stack* ps)
{
	assert(ps);

	free(ps->a);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

队列

队列的概念

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特性。


队列结构分析

我们需要定义的是链式结构的队列,即单链表为底层实现的。所以需要定义单链表结构来存储数据。然后,定义队列,队列里需要定义两个指向单链表的指针,一个是指向单链表头结点的指针,另一个则用来保存尾结点地址的指针。最后,还需定义一个记录当前队列元素个数的变量,用于遍历队列和判空。

//链式队列节点
typedef int QDataType;
typedef struct QListNode
{
	QDataType data;			// 当前节点存储的值
	struct QListNode* next; // 下一个节点的指针
}QNode;

// 队列的信息 
typedef struct Queue
{
	QNode* phead;	// 队头节点
	QNode* ptail;	// 队尾节点
	int size;		// 队列长度
}Queue;

队列初始化

首先需要断言判断一下指针合法性。然后,需要将队列内两个指针变量初始化。最后,将记录队列有效元素个数的变量初始化一下。

void QueueInit(Queue* q)
{
	assert(q);
	q->phead = NULL;
	q->ptail = NULL;
	q->size = 0;
}

队列判空

由于在定义队列时,增加了额外的记录当前有效数据个数的变量,所以这里直接返回该变量与0比较的值即可。

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

队列入数据

首先还是需要进行断言判断指针有效性。其次,就是动态开辟结点,并对新节点初始化。然后,进行头插操作,记得第一次插入时,需要单独判断两个指针同时指向新结点。最后,记得让ptail指针指向最后一个结点和size++。

void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (q->phead == NULL)
	{
		assert(q->ptail == NULL);
		q->phead = q->ptail = newnode;
	}
	else
	{
		q->ptail->next = newnode;
		q->ptail = newnode;
	}
	q->size++;
}

队列出数据

当队列为空时,不能进行删除操作,所以先断言判断一下是否为空队列。然后,就是头删链表的操作,需要注意的是当只剩一个结点时,对ptail进行特殊处理避免野指针。最后,就是让记录有效元素的size--一下。

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	if (q->phead->next == NULL)
	{
		free(q->phead);
		q->phead = q->ptail = NULL;
	}
	else
	{
		QNode* next = q->phead->next;
		free(q->phead);
		q->phead = next;
	}
	q->size--;
}

获取队头数据

由于在定义队列时,定义了一个记录当前有效元素个数的变量,所以直接返回该变量当前的值即可。

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->phead->data;
}

获取队尾数据

直接返回第一个结点的data即可。

QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->ptail->data;
}

获取队列中有效元素个数

在定义队列时,定义了一个记录当前有效元素个数的size变量,所以这里直接返回该变量当前的值即可。

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

队列销毁

首先,断言一下指针有效性。然后,依次释放每个结点。最后释放最后一个结点时,把ptail和phead置空,size置零。

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->phead = q->ptail = NULL;
	q->size = 0;
}

总结

  1. 栈是限定仅在表尾进行插入和删除的线性表,又称后进先出的线性表。有顺序栈和链栈。栈的主要操作有进栈和出栈,对于顺序栈的进栈和出栈要注意判断栈满和栈空。
  2. 队列是一种先进先出的线性表。只允许在一段进行插入,一段进行删除。有循环队列和链队。队列的主要操作就是进队和出队,对于顺序表示的循环队列的进队和出队操作要注意判断队空和队满。
  3. 队列和栈都不支持随机访问和随机插入。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值