栈和队列保姆式讲解

前言

        栈和队列都是特殊的线性表,二者的实现是基于前面学过的顺序表和链表的,所以在功能和实现方面不会有太大的问题,重点是要关注栈有什么的特点和队列又有什么区别,以及二者的应用场景是什么等等。


栈专题

栈的概念

        是一种特殊的线性表,栈只允许在固定的一端插入或删除数据。进行数据插入和删除的一端被称为栈顶,另一端称为栈底。此外,向栈插入数据的操作叫入栈/进栈/压栈,从栈中删除数据的操作叫出栈

栈的结构

        由于栈只允许在固定的一端插入或删除数据,因此栈数据的插入与删除遵从后进先出(Last in First out)原则。其结构必须满足该原则,所以可供实现栈的结构可以有顺序表、单链表和带头双向循环链表。即以下几种:

不过需要注意几种情况,就是当用单链表来实现时,为了方便数据的插入和删除操作,一般用头节点作为栈顶,尾节点作为栈底。事实上,与其用带头双向循环链表来实现栈,倒不如用单链表来实现更加方便,因此为了实现栈一般只采用两种结构,即顺序表和单链表。

栈的实现(顺序表实现)

        结构创建

         函数接口
(1)栈初始化(StackInit)

 此处需要注意,top初始化为什么值是自己定义的,如果想让top指向栈顶就需要把top初始化为-1,如果想让top指向栈顶的下一个元素,那就把top初始化为0。另外,top初始值不同可能会导致以下接口的实现都有细枝末节的变化,应当细心处理。

(2)栈销毁(StackDestroy)

(3)入栈函数(StackPush)

void StackPush(ST* ps, STDataType x)//入栈
{
	assert(ps);
	if (ps->capacity == ps->top)
	{
		//扩容
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ST* ptr = (ST*)realloc(ps->arr, sizeof(STDataType) * newCapacity);
		if (ptr == NULL)
		{
			perror("malloc fail");
			exit(-1);
		}
		ps->arr = ptr;
		ps->capacity = newCapacity;
	}
	ps->arr[ps->top++] = x;
}

 入栈函数主要的重点是扩容,扩容存在两种情况,一是原容量为零需要指定分配一块大小的空间;二是原容量不为零,扩容时以指定倍数增容。扩完容后记得别忘了要保存新的容量大小,在top处增加新的数据,最后别忘了top++。

(4)出栈函数(StackPop)

(5)栈判空(StackEmpty)

 当top为0时,栈为空,表达式结果为真,返回true

(6)获取栈顶元素(StackTop)

 (7)获取栈中有效数据个数(StackSize)

 

补充:栈的遍历打印

 栈只能从一端插入或删除数据,因此要想遍历打印栈,就必须一边出栈一边打印。

队列专题

队列概念

        与栈一样,队列也是一种特殊的线性表。相较于栈,队列规定在一端插入数据,另一端删除数据具有先进先出(First in first out)的特点。其中插入数据的操作叫入队列,入队列的一端称为队尾;删除数据的操作叫出队列, 出队列的一端称为队头。

队列结构

        队列要求元素先进先出,因此在实现时应当要求两端一端入队列,一端出队列。所以可用顺序表、单链表以及带头双向循环链表。此处同样排除最复杂的结构带头双向循环链表,本文使用单链表来实现队列,但并不是说顺序表不能实现,大家也可以尝试写写。

队列实现

        结构创建

 图上为节点类型的声明

为避免后续频繁使用二级指针,可以将头节点和尾节点放在结构体中统一管理,此后只需创建传结构体变量,接着传结构体变量的地址就可以在调用的函数中修改头指针和尾指针的值了。另外,由于队列的接口函数中有一个是求队列中元素的个数的,所以可以再定义一个成员size方便后续的函数实现。

队列接口函数
        (1)初始化队列(QueueInit)

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

需要注意,传递给QueueInit函数的参数为Queue类型的指针,以下接口函数亦然。

        (2)销毁队列(QueueDestroy)
void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;//cur记录当前位置的节点,用cur来遍历队列
	while (cur)
	{
		QNode* next = cur->next;//next记录下一个节点的地址
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;//最后记得将头尾指针置空,以免出现野指针
	pq->size = 0;
}

 销毁队列的思路是遍历队列,释放每个节点,最后记得将变量该置空的置空该赋0的赋0。

        (3)入队列(QueuePush)
void QueuePush(Queue* pq, QueueDataType x)//入队列
{
	assert(pq);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (NewNode == NULL)
	{
		perror("malloc fail");
		return;
	}
	NewNode->data = x;
	NewNode->next = NULL;
//以上均为创建新节点,下面才是插入数据的实现
	if (pq->phead == NULL)//队列为空
	{
		pq->phead = pq->ptail = NewNode;
	}
	else//队列不为空
	{
		pq->ptail->next = NewNode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}

 

 注意区分队列为空和队列不为空的情况,若队列为空则直接将新节点与头尾指针相连接,若不为空队列则在队列的尾部插入数据。每次插入之后都要记得更新一下ptail,让ptail指向尾节点,并且size++。

        (4)出队列(QueuePop)
void QueuePop(Queue* pq)//出队列
{
	assert(pq);
	assert(pq->phead);
	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--;
}

出队列在实现时采用单链表的头删,这是由队列的特性决定的。在头删时需要注意,当队列只有一个节点时,应单独处理,最后也别忘给size--。

        (5)获取队头元素(QueueFront)

        (6)获取队尾元素(QueueBack)

        (7)获取队列中元素个数(QueueSize)

前面在Queue中设计成员size就是为了方便实现这个函数。

        (8)队列判空(QueueEmpty)

此处判空与栈判空相同如果表达式为假返回false,为真返回true。

 

 

结语

以上就是栈和队列的所有内容了,这些都是些基础的玩意,本篇博客只是简单地总结了一下栈和队列的内容,并不能提高大家对这块知识的理解,后续会找时间写几篇数据结构相关的经典习题,希望对大家有帮助。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
栈和队列是两种常见的数据结构,它们都是线性结构,可以用于许多实际的应用场景。 首先,我们来讲解一下栈。栈是一种后进先出(Last In First Out,LIFO)的数据结构,它的主要操作包括入栈和出栈。入栈指将元素添加到栈顶,出栈指从栈顶弹出一个元素。栈可以用数组或链表来实现。 栈的应用场景非常广泛,例如: 1. 括号匹配:在编译器中,可以使用栈来判断代码中括号的匹配情况。 2. 系统调用:操作系统使用栈来存储进程调用时的参数和返回值。 3. 表达式求值:在计算机科学中,可以使用栈来实现中缀表达式到后缀表达式的转换,以及后缀表达式的计算。 接下来,我们来讲解一下队列。队列是一种先进先出(First In First Out,FIFO)的数据结构,它的主要操作包括入队和出队。入队指将元素添加到队尾,出队指从队头弹出一个元素。队列可以用数组或链表来实现。 队列的应用场景也非常广泛,例如: 1. 线程池:在多线程编程中,可以使用队列来实现任务的调度和执行,以避免线程频繁的创建和销毁。 2. 缓存:在网络编程中,可以使用队列来实现消息的缓存和转发,以提高系统的性能。 3. BFS算法:在图论中,可以使用队列来实现广度优先搜索(BFS)算法,以求解最短路径等问题。 总的来说,栈和队列是两种基本的数据结构,它们都有着广泛的应用场景。在实际开发中,根据需要选择合适的数据结构来实现功能,可以有效地提高程序的效率和可读性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值