实现简单的栈与队列
创始人
2024-05-20 18:43:31
0

前言:前面已经详细地介绍了基本的顺序表和链表,这次要介绍的是数据结构中的栈与队列。从本质上来说,二者是特殊的线性表,是依赖于顺序表或链表来实现的,所以只要能够很好地掌握顺序表和链表,再了解清楚栈与队列的概念及基本结构,就可以很好地将二者实现出来。

注:由上言以及下文可以知道栈与队列的实现与顺序表,链表的实现大同小异(甚至更简单),一些内容不会详细说明,不清楚的可以再看看以下两篇文章:

(1)顺序表的实现

(2)单链表的实现

目录:

一:栈

1. 栈的概念

2. 栈的结构(图示)

3. 栈的重要接口函数

4. 栈实现相关代码总览

二:队列

1. 队列的概念

2. 队列的结构(图示)

3. 队列的重要接口函数

4. 队列实现相关代码总览


一:栈

1. 栈的概念

(1) 栈: 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。 (2) 压栈:栈的插入操作叫做压栈/进栈/入栈,即入数据在栈顶(3) 出栈:栈的删除操作叫做出栈。即出数据也在栈顶

2. 栈的结构(图示)

注:栈一定要遵守先进后出的原则。


3. 栈的重要接口函数

我们可以使用顺序表来实现栈,也可以用链表实现栈,但是链表实现栈有两种方式,一种是头插头删,一种是尾插尾删。而单链表进行尾插尾删时要进行的找尾操作较为复杂(要遍历链表),所以我们选择顺序表(数组)来实现栈,其结构相对链表而言较为优势。

栈相关的七个重要接口函数:

void StackInit(ST* st);//初始化
void StackPush(ST* st, STDataType x);//入栈
void StackPop(ST* st);//出栈
STDataType StackTop(ST* st);//获取栈顶元素
int StackSize(ST* st);//获取栈中的有效元素个数
bool StackEmpty(ST* st);//判断栈是否为空
void StackDestroy(ST* st);//销毁栈

4. 栈实现相关代码总览

(1)TestStack.c

#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"void TestStack1()
{ST st;//定义一个栈StackInit(&st);//将栈初始化,要传递结构体指针才能在接口函数内对其进行改变StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);//压栈printf("%d\n", StackSize(&st));//打印此时的栈内元素个数while (!StackEmpty(&st))//打印出栈内的所有数据{printf("%d ", StackTop(&st));StackPop(&st);}//一个小疑问:Pop执行后的终点是top=0,但是此时st->a[0]不是等于1吗,这样不是没有删干净吗?printf("\n%d\n", StackSize(&st));StackDestroy(&st);
}void TestStack2()
{ST st;//定义一个栈StackInit(&st);//将栈初始化,要传递结构体指针才能在接口函数内对其进行改变StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);printf("%d\n", StackSize(&st));StackPop(&st);StackPop(&st);printf("%d\n", StackSize(&st));//Pop两次后栈内的元素个数while (!StackEmpty(&st)){printf("%d ", StackTop(&st));StackPop(&st);}printf("\n%d\n", StackSize(&st));StackDestroy(&st);
}int main()
{TestStack1();//TestStack2();return 0;
}

(2)Stack.h

#pragma once
#include 
#include 
#include 
#include typedef int STDataType;//定义一个动态增长的数组栈
typedef struct Stack
{STDataType* a;//指针a指向动态开辟的数组int top;//表示栈内的有效元素个数int capacity;//表示栈的空间容量
} ST;//相关接口函数的定义
void StackInit(ST* st);//初始化
void StackPush(ST* st, STDataType x);//入栈
void StackPop(ST* st);//出栈
STDataType StackTop(ST* st);//获取栈顶元素
int StackSize(ST* st);//获取栈中的有效元素个数
bool StackEmpty(ST* st);//判断栈是否为空
void StackDestroy(ST* st);//销毁栈

(3)Stack.c

#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"void StackInit(ST* st)
{assert(st);st->a = NULL;//top初始化为0时,top指向的是栈顶的下一个数据,因为压栈是先插入数据后top再加1st->top = st->capacity = 0;
}void StackPush(ST* st, STDataType x)//入栈,向栈内插入数据
{assert(st);if (st->top == st->capacity)//判断容量是否足够,不够就扩容{int newCapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDataType* tmp = (STDataType*)realloc(st->a, sizeof(ST) * newCapacity);if (tmp == NULL){printf("realloc fail\n");exit(-1);}st->a = tmp;st->capacity = newCapacity;}st->a[st->top] = x;st->top++;//先插入数据,top再++,即top此时指向的是栈顶的下一个数据
}void StackPop(ST* st)//出栈
{assert(st);//assert(st->top > 0);assert(!StackEmpty(st));//栈不能为空st->top--;
}bool StackEmpty(ST* st)//判断栈是否为空
{assert(st);return st->top == 0;
}STDataType StackTop(ST* st)//获取栈顶元素
{assert(st);return st->a[st->top - 1];//top下标指向的是栈顶的后一个数据
}int StackSize(ST* st)//获取栈内有效数据的个数
{assert(st);return st->top;
}void StackDestroy(ST* st)//销毁栈
{assert(st);while (!StackEmpty(st)){StackPop(st);}st->a = NULL;st->top = st->capacity = 0;
}

二:队列

1. 队列的概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性。 入队列:进行插入操作的一端称为队尾。 出队列:进行删除操作的一端称为队头。

2. 队列的结构(图示)

 注:队列一定要遵守先进先出的原则。


3. 队列的重要接口函数的实现

与栈相同,队列既可以通过顺序表来实现,也可以通过链表实现。但与栈实现起来不同的是,对于队列而言,链表的结构更适合它,因为队列主要涉及到的是头部尾部的插入与删除,而顺序表(数组)实现起来的效率会更低一些。

这里需要特别说明的是:选用的是单链表,并且我们需要给这个单链表定义好头节点 (head)和尾节点(tail),将它们作为队列的基本框架,以形成一个较好头插尾删的单链表。

图示:

代码说明:

队列相关接口函数:

void QueueInit(Queue* pq);//初始化队列
void QueuePush(Queue* pq, QDataType x);//入队列
void QueuePop(Queue* pq);//出队列
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//返回队列中有效数个数
QDataType QueueFront(Queue* pq);//获取队头数据
QDataType QueueBack(Queue* pq);//获取队尾数据
void QueuePrint(Queue* pq);//打印队列(同时会清空队列的元素)
void QueueDestroy(Queue* pq);//销毁队列中动态开辟节点的链表

4. 队列实现相关代码总览

(1) TestQueue.c

#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"void TestQueue1()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);printf("%d\n", QueueSize(&q));//打印此时队列内的元素个数QueuePop(&q);QueuePop(&q);printf("%d\n", QueueSize(&q));//打印此时队列内的元素个数QueuePrint(&q);//QueuePrint函数调用的同时会清空队列printf("\n%d\n", QueueSize(&q));QueueDestroy(&q);
}void TestQueue2()
{Queue q;QueueInit(&q);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);//打印此时队列元素个数,队头数据,对尾数据printf("%d %d %d\n", QueueSize(&q), QueueFront(&q), QueueBack(&q));QueuePrint(&q);//QueuePrint函数调用的同时会清空队列printf("\n%d\n", QueueSize(&q));QueueDestroy(&q);
}int main()//各个接口函数功能的测试
{TestQueue1();//TestQueue2();return 0;
}

(2) Queue.h

#pragma once
#include 
#include 
#include 
#include typedef int QDataType;//用单链表实现队列(定义队列中的链表结构)
typedef struct QueueListNode
{struct QueueListNode* next;//指针域QDataType data;//数据域
} QLNode;//为了更好地实现单链表的头删(出队列)与尾插(入队列),在队列的链表结构中在定义一个头节点与尾节点,可以表示队列的结构
typedef struct Queue
{QLNode* head;QLNode* tail;
} Queue;//队列相关接口函数的定义
void QueueInit(Queue* pq);//初始化队列
void QueuePush(Queue* pq, QDataType x);//入队列
void QueuePop(Queue* pq);//出队列
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//返回队列中有效数个数
QDataType QueueFront(Queue* pq);//获取队头数据
QDataType QueueBack(Queue* pq);//获取队尾数据
void QueuePrint(Queue* pq);//打印队列(同时会清空队列的元素)
void QueueDestroy(Queue* pq);//销毁队列中动态开辟节点的链表

(3) Queue.c

#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"void QueueInit(Queue* pq)//队列初始化
{assert(pq);pq->head = NULL;pq->tail = NULL;
}void QueuePrint(Queue* pq)//打印整个队列的元素(同时会清空队列的元素)
{assert(pq);while (!QueueEmpty(pq)){printf("%d ", QueueFront(pq));QueuePop(pq);//只有清理了对头元素,才可以继续向后读取数据}
}void QueuePush(Queue* pq, QDataType x)//入队列(单链表的尾插)
{QLNode* newnode = (QLNode*)malloc(sizeof(QLNode));//开辟新节点if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->next = NULL;newnode->data = x;//节点入列的两种情况if (pq->head == NULL)//1.原队列为空{pq->head = pq->tail = newnode;newnode->next = NULL;}else//原队列不为空{pq->tail->next = newnode;pq->tail = newnode;pq->tail->next = NULL;}
}void QueuePop(Queue* pq)//出队列,单链表的头删
{assert(pq);assert(!QueueEmpty(pq));//删除时队列不能为空QLNode* next = pq->head->next;free(pq->head);pq->head = next;
}bool QueueEmpty(Queue* pq)//判断队列是否为空
{assert(pq);return pq->head == NULL;
}int QueueSize(Queue* pq)//返回队列中有效数个数
{assert(pq);QLNode* cur = pq->head;int num = 0;while (cur)//遍历队列中的单链表{num++;cur = cur->next;}return num;
}QDataType QueueFront(Queue* pq)//获取队头数据
{assert(pq);assert(!QueueEmpty(pq));//队列不能为空return pq->head->data;
}QDataType QueueBack(Queue* pq)//获取队尾数据
{assert(pq);assert(!QueueEmpty(pq));//队列不能为空return pq->tail->data;
}void QueueDestroy(Queue* pq)//销毁队列中动态开辟节点的链表
{assert(pq);while (pq->head){QLNode* next = pq->head->next;free(pq->head);pq->head = next;}
}

总结:

栈与队列的实现最重要的是结构以及对链表,顺序表的理解程度,这里再强调一次:数据结构的学习中结构的理解十分重要,所以我们可以多画画相关的图,再结合图理解这样一定可以事半功倍。栈与队列的介绍就到这里结束,谢谢大家的阅读,再见。

相关内容

热门资讯

名字大全网名 名字大全网名  高雅不俗的网名(精选700个)  网名也叫网络昵称,用于虚拟的网络世界,例如在微信、...
微信名字微信昵称简单大气 微信名字微信昵称简单大气  微信名字微信昵称简单大气(精选100个)  网名指在网上使用的名字。由于...
教师的经典座右铭 教师的经典座右铭(精选100句)  在平日的学习、工作和生活里,大家或多或少都接触过一些经典的句子吧...
小学数学《分数乘法》测试题 小学数学《分数乘法》测试题  一、对号入座。  1、小羊只数是大羊只数的 ,( )是单位1。  A、...
唯美的qq个性签名   很多时候,因为得不到,所以假装不想要。下面是大学生小编为大家分享有关唯美的qq个性签名,欢迎大家...
请假条表格 - 请假条表格 -范文请 假 条 ...
英语qq个性签名 英语qq个性签名  人生六品官:不知道要干点什么的是半成品,知道要干点什么的是成品,干出点名堂的是精...
吴泽邦:做梦想坚守者和实践者 吴泽邦:做梦想坚守者和实践者  吴泽邦  专业:清华大学法学院  吴泽邦,1997年2月18日出生,...
心烦的个性签名 心烦的个性签名  随着社交网络的发展,越来越多人习惯于在线上发布自己的个性签名,每个网友所写的个性签...
舞团名字大全霸气优雅300个 舞团名字大全霸气优雅300个  一、什么是名字  名字是指人或者产品、物体的名称,姓名有广义与狭义之...
女生经典个性签名 女生经典个性签名(精选620句)  随着社交网络的普遍使用,越来越多人喜欢在闲暇时设置个性签名,不同...
经典好听的好姐妹个性签名 经典好听的好姐妹个性签名  1、谁真谁假患难见真情  2、你是陪我到老的女人。  3、闺蜜永远比男友...
吸引人的网名两个字 吸引人的网名两个字  网名是你在网络上的个人代号,给人留下什么印象就会让人对你有什么样的看法,取一个...
表示运气好的网名 表示运气好的网名  网名是指在网上使用的名字,由于网络是虚拟的`世界,为了避免使用真实姓名带来的麻烦...
成熟男人的网名 成熟男人的网名  一、网名的特征  以下特征为多数情况,并不代表全部。  象征性,网名和自己的名字一...
教师座右铭 教师座右铭15篇教师座右铭1  一、能得到家长和孩子的尊敬和喜爱,这是教师的价值所在。  二、教师,...
好看的个性签名 好看的个性签名  随着社交网络平台的快速发展,越来越多人会在网上发布个性签名,个性签名没有什么局限,...
四个字网名大全 四个字网名大全  四个字网名大全(精选600个)  网名指在网上使用的名字。由于网络是一个虚拟的世界...
带猪字的网名 带猪字的网名  带猪字的网名(精选560个)  随着网络的普及,上网聊天人数的增多,网名千奇百怪。猪...
个性网名大全 个性网名大全  个性网名大全(精选500个)  在网络上有一个网名是不是也更能吸引网络好友呢?不试试...