数据结构-栈、队列-详解

1.前言

在数据结构中,队列都是一种线性表,但是,不同于顺序表和链表,栈和队列在对数据进行处理时,对数据的位置有特殊的规定。

2.

操作系统中,栈指的是内存的一块空间,用于存储函数、临时变量等。
这里的栈是一种数据结构
两个栈属于不同的学科,注意区分。

2.1是什么

stack)是一种运算受限的线性表。限定仅在一端进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。
在这里插入图片描述

简单来说,对栈的操作和下图类似,都是后进去的先出来。

  • LIFO(Last In First Out)
    在这里插入图片描述

2.2函数实现

struct Stack

想要实现栈,首先创造的还是一个结构体,用于存储不同类型的变量。
而,高中生物老师讲过,结构与功能相对应,这里也是相同的道理。
为了便于对栈顶的数据进行处理,创造变量记下栈顶的位置top
其他的类比动态顺序表就行。

typedef char StackDataType;
typedef struct Stack
{
	StackDataType* a;
	int top;
	int capacity;
}Stack;

StackInit

栈的初始化。

void StackInit(Stack* s)
{
	assert(s);
	s->a = (StackDataType*)malloc(sizeof(StackDataType) * 4);
	if (!s->a)
	{
		perror("InitStack::malloc");
		return;
	}
	s->top = 0;
	s->capacity = 4;
}

这里主要问题是top的初始值,我选择的是0,用于表示栈顶下一个元素的下标
也可以是-1,用于表示栈顶元素的下标
当然,也可以是任意值,只要不嫌麻烦。

StackDestroy

栈的销毁。

void StackDestroy(Stack* s)
{
	assert(s);
	free(s->a);
	s->a = NULL;
	s->top = 0;
	s->capacity = 0;
}

StackPush

插入新元素,即压栈。

void StackPush(Stack* s,StackDataType x)
{
	assert(s);
	if (s->top == s->capacity)
	{
		StackDataType* tmp = (StackDataType*)realloc(s->a, sizeof(StackDataType)*s->capacity * 2);
		if (!tmp)
		{
			perror("PushStack::realloc");
			return;
		}
		s->a = tmp;
		s->capacity *= 2;
	}
	s->a[s->top] = x;
	s->top++;
}

需注意,我的top为栈顶下一个元素的下标,因此s->a[s->top] = x;
初始化不同时,此处操作应该也不同。

StackSize

元素个数。

int StackSize(Stack* s)
{
	assert(s);
	return s->top;
}

StackEmpty

判空。

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

这个就比较有意思了,判空。
有人说,top == 0不就空了吗,为什么非要写个函数。
这里还是初始化的问题,如果top一开始是-1呢?
因此,让初始化top的人写个判空的函数,可减少意外发生。

StackTop

取栈顶元素,但不删除。

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

StackPop

删除栈顶元素,即出栈。
一般与StackTop配合使用。

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

2.3小结

嗯。


可以试试这道题:有效的括号
思路:遇到左括号,压入栈;遇到右括号,与栈顶元素匹配,然后出栈。
我写的:(注:上面栈的数据结构CV过去就行)
在这里插入图片描述

3.队列

3.1是什么

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。

入队
...
A
B
C
D
....
出队

与栈的后入先出不同,队列为先入先出:
在这里插入图片描述
???

3.2函数实现

struct Queue

结构与功能相对应:

  • 队列在一边删除,另一边插入,使用顺序表会涉及挪动,浪费时间。因此,选择链表。
  • 使用双链表固然可以,但在这里没必要,虽然,双链表提供了额外的功能,但,使用单链表已经足够满足需求。因此,使用单链表。
  • 队列涉及队首和队尾的操作,因此,存储这两个结点可避免遍历链表。

于是,可以写出:

typedef int QDatatype;

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

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

QueueInit

初始化。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

QueueDestroy

销毁。

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

QueueEmpty

判空。

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

QueuePush

入队:

void QueuePush(Queue* pq, QDatatype x)
{
	assert(pq);
	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (!newnode)
	{
		perror("QueuePush::malloc");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	
	if (!QueueEmpty(pq))
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	else
		pq->head = pq->tail = newnode;
		
	pq->size++;
}

QueuePop

出队。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
		pq->tail = NULL;
	pq->size--;
}

需注意,当变为空队列时,需把tail也置空。

QueueFront

获取队首元素。

QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

QueueBack

获取队尾元素。

QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

QueueSize

获取队伍长度。

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

3.3小结

最主要的问题集中于如何定义结构,当队列的结构体创建好后,其他函数实现并不复杂。


希望本篇文章对你有所帮助!并激发你进一步探索数据结构的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

相关文章:
数据结构-顺序表-详解
数据结构-单链表-详解-1
数据结构-单链表-详解-2

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值