栈和队列 --- C语言实现

本篇文章来详细介绍一下栈和队列,并且通过数组或链表来实现。

目录

1.栈

1.1栈的概念及结构

1.2栈的实现

2.队列

2.1队列的概念及结构

2.2队列的实现

3.栈和队列面试题

4.相关概念选择题


1.栈

1.1栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底 。栈中的数据元素遵守后进先出LIFO (Last ln First Out)的原则。

**压栈:**栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

**出栈:**栈的删除操作叫做出栈。出数据也在栈顶。

1.2栈的实现

栈的实现一般可以使用数组或者链表实现 ,相对而言,数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小

数组实现栈:

单链表实现栈:

数组代码实现:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;

typedef struct Stack
{
	STDataType* data;
	int top;
	int capacity;
}Stack;

//初始化
void StackInit(Stack* pst);
//销毁
void StackDestroy(Stack* pst);
//入栈
void StackPush(Stack* pst, STDataType x);
//出栈
void StackPop(Stack* pst);
//获取栈顶元素
STDataType StackTop(Stack* pst);
//判空
bool StackEmpty(Stack* pst);
//获取个数
int StackSize(Stack* pst);

接口实现:

cpp 复制代码
//初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->data = NULL;
	pst->top = 0;
	pst->capacity = 0;
}
//销毁
void StackDestroy(Stack* pst)
{
	assert(pst);
	free(pst->data);
	pst->data = NULL;
	pst->top = pst->capacity = 0;
}
//入栈 
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* ptr = (STDataType*)realloc(pst->data, newcapacity * sizeof(STDataType));
		if (ptr == NULL)
		{
			perror("malloc fail");
			return;
		}
		pst->data = ptr;
		pst->capacity = newcapacity;
	}
	pst->data[pst->top] = x;
	pst->top++;
}
//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	pst->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	return pst->data[pst->top - 1];
}
//判空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return pst->top == 0;
}
//获取个数
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}

2.队列

2.1队列的概念及结构

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

**入队列:**进行插入操作的一端称为队尾

出队列: 进行删除操作的一端称为队头

2.2队列的实现

队列也可以数组和链表的结构实现 ,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,数组头删效率会比较低

链表实现队列:

入队出队示意图:

链表代码实现队列:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QueDataType;
typedef struct QueueNode
{
	QueDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//释放
void QueueDestroy(Queue* pq);
//入队
void QueuePush(Queue* pq, QueDataType x);
//出队
void QueuePop(Queue* pq);
//取队头
QueDataType QueueFront(Queue* pq);
//取队尾
QueDataType QueueBack(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);

接口实现:

cpp 复制代码
//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
//释放
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
//入队
void QueuePush(Queue* pq, QueDataType x)
{
	assert(pq);
	//创建新节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	//入队
	if (pq->phead == NULL)
	{
		assert(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(!QueueEmpty(pq));

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

//取队头
QueDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}
//取队尾
QueDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}
//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL && pq->ptail == NULL;
}

另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

Q.rear指向的是有效数据的下一个位置,在这时我们不能知道 Q.rear = Q.front 时,是队列满还是空,所以这里有两种解决办法来判断队满,图中采用的是第二种方法。

1.定义一个size来记录元素个数

2.让队列中少存储一个元素,数组实现让**(rear+1) % MaxSize = front** ,rear 和 front 都是下标,环形链表实现让rear->next = front

3.栈和队列面试题

1.括号匹配问题。OJ链接

2.用队列实现栈。OJ链接

3.用栈实现队列。OJ链接

4.设计循环队列。OJ链接

4.相关概念选择题

答案在最后

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是()。

  • A 12345ABCDE

  • B EDCBA54321

  • C ABCDE12345

  • D 54321EDCBA
    2.若进栈序列为1,2,3,4,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

  • A 1,4,3,2

  • B 2,3,4,1

  • C 3,1,4,2

  • D 3,4,2,1
    3.循环队列的存储空间为 Q(1:100),初始状态为front=rear=100。经过一系列正常的入队与退队操作后,front=rear=99 ,则循环队列中的元素个数为()

  • A 1

  • B 2

  • C 99

  • D 0或者100
    4.以下()不是队列的基本运算?

  • A 从队尾插入一个新元素

  • B 从队列中删除第i个元素

  • C 判断一个队列是否为空

  • D 读取队头元素的值
    5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设队头不存放数据)

  • A ( rear - front + N ) % N + 1

  • B ( rear - front + N ) % N

  • C ( rear - front ) % ( N +1 )

  • D ( rear - front + N ) % ( N - 1 )

答案

  1. B
  2. C
  3. D
  4. B
  5. B

本篇结束

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜12 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa12 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio