boxmoe_header_banner_img

糖画99

文章导读

栈和队列的基本实现


avatar
mizuki 2025年12月25日 41

引言

栈和队列作为数据结构的基础,这篇文章将对栈和队列的基本实现进行具体讲解,并对其进行实现

栈的基本实现

  • 前言

在具体实现栈的函数前,我们要先创建出栈的结构体,所以我们要搞清楚结构体的类型,通过前面的学习,我们有链表和顺序表可以供我们选择,我们可以根据栈的特点来进行选择

  • 栈的特点

1.后进先出,如图(包括栈顶栈底的位置):

2.只允许在固定的一端进行删除和插入,也就是只能在栈顶进行这些操作

  • 结构体选择

因为只用对栈顶进行删除和插入,我们就可以通过最后一个元素来进行分析

首先是插入,无论是顺序表还是链表,插入都能够顺利的做到,并且相比之下,链表更节省空间,那我们就要选择链表吗?

再看删除,删除了栈顶的元素后,我们要返回上一个元素,但是对于单链表来说(不考虑双链表,因为有两个指向地址,也会浪费空间,不考虑),找到上一个元素是很困难的,而对于顺序表来说,只需要找到上一个下标就可以了,所以我们使用顺序表作为他的结构体

  • 结构体具体内容

首先一个指针数组,一个容量元素是必要的,又因为这里我们只用调用栈顶的函数,我们就创建一个与栈顶有关的元素,也就是尾结点

栈的结构体具体实现如下:

//给数据类型命名
typedef int DataType;

typedef struct Stack
{.
	DataType* data;
	//栈顶
	int top;
	//容量
	int capacity;
}Stack;
  • 栈的初始化

首先与顺序表一样,我们要先初始化存放数据的指针,也就是扩容并且赋值NULL,然后对容量赋值0

不同的是,我们要对栈顶(top)赋值多少呢,我们的目的是通过‘ ’top ‘’找到栈顶元素,此时我们有两个选择

1.对栈顶赋值-1,这样就可以直接指向栈顶元素,因为刚刚初始化时,是没有存放元素的,因为栈顶不会指向任何元素 ,例如当已经存放一个元素时,top = 0, 下标对应的就是栈顶的元素

2.对栈顶赋值0,此时我们要找到栈顶元素就要通过‘ top-1 ’来找到栈顶元素,但是相对方便的是,我们也可以把’ top ’作为有效元素,可以更好的判断容量是否足够

而在这里我选择的是将top赋值为0,如果你想用第一种方法实现的话,也可以自己尝试

栈的初始化具体实现如下:

// 初始化栈
void StackInit(Stack* ps)
{
	assert(ps);
	//初始化值
	ps->data = NULL;
	ps->capacity = 0;
	ps->top = 0;
}
  • 销毁栈

学会初始化栈后,我们先把销毁栈学习一下

其实与销毁顺序表的方式是一样的,先释放指针,再将两个值赋值为0,具体实现如下:

// 销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	ps->capacity = ps->top = 0;
	free(ps->data);
	ps->data = NULL;
}
  • 入栈(向栈中插入元素)

1.在执行插入元素前,我们要先判断容量是否足够,但是与之前的顺序表实现不同的是,这里我们不用再单独写一个判断是否需要扩容的函数,因为在栈的实现中只有一次与插入有关的函数,所以直接将判断是否需要扩容的函数写进去就可以了

2.判断扩容后,我们要做的就是赋值,因为我初始化的top元素是0,所以以此类推top元素将一直指向栈顶元素的后一位,我们直接将top作为下标进行赋值就可以了

3.赋值完成后,我们就要对top元素进行+1的处理,使它始终指向栈顶元素的下一个

入栈的具体实现如下:

// 入栈
void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	//判断是否需要扩容
	if (ps->capacity == ps->top)
	{
		int newcapacity = (ps->capacity == 0) ? 4 : ps->capacity * 2;
		DataType* tmp = (DataType*)realloc(ps->data,newcapacity * sizeof(DataType));
		if (tmp == NULL)
		{
			perror("malloc:");
			return;
		}
		ps->data = tmp;
		ps->capacity = newcapacity;
	}
	ps->data[ps->top] = data;
	ps->top++;
}

如果关于扩容这一块有问题的话,可以看这篇顺序表的实现的文章:

  • 出栈(删除栈顶的一个元素)

首先我们要判断此栈是否为空,这关乎函数是否还能继续进行下去

然后直接将top–就可以了,很简单对吧

出栈的具体实现如下:

  • 获取栈顶元素

通过之前的分析,top-1指向的下标就是栈顶元素,直接返回下标为‘ top-1 ’的元素就可以了

具体实现如下:

  • 返回有效元素个数

因为我初始化的top就是0,所以top元素的大小其实就是有效元素的大小,这里也体现了top初始化为0的好处

具体实现如下:

  • 判断栈是否为空

其实就是判断是否存在有效元素,我们可以通过top元素来判断,当top == 0时,栈就为空

这里我们要返回一个bool值,巧妙的运用一个条件判断式来进行返回,如果条件成立就会返回true反之则返回false

具体实现如下:

  • 所有需要实现的函数如下,可以仔细对照这些函数进行练习实现
  • 包含的头文件

队列的基本实现

  • 前言

与栈相同,在实现队列前,我们要先了解队列的特点,并且选择适合它的结构体

  • 队列的特点

1.先进先出

2.从队头出,从队尾入

  • 队列的结构体选择

通过队列的特点我们可以知道,我们只会从队头出数据从队尾插入数据,所以需要一个头节点和一个尾节点,而队列中不会出现要删除尾节点的情况,所以为了节省空间,我们就可以选择用链表来实现队列

  • 结构体的具体事项

因为我们已经选择了链表,所以结构体中要包含一个指向下一个节点的指针一个存数据的元素,但是这个结构体是不能直接当作队列的结构体使用的,因为队列的结构体需要包含一个头节点和一个尾节点,所以这个结构体只能作为队列的结构体,也就是说,我们要在结构体内再嵌套一个结构体

具体实现如下;

  • 初始化队列

与正常的链表初始化一样,将头指针跟尾结点初始化为NULL,然后把size初始化为0

具体实现如下:

  • 销毁队列

也与链表的销毁差不多,从头节点开始往后面遍历,将每个节点都释放一遍,然后再单独将头节点跟尾节点释放一遍,然后赋值为NULL,再将size赋值为0

具体实现如下:

  • 入队列

因为队列只能从后面插入,所以跟单链表的尾插实现是相同的,这里直接给代码了

具体实现如下:

  • 出队列

同理,队列只能从队头出数据,与单链表的头删实现相同,这里也是直接给代码了

具体实现如下:

  • 获取头部元素

头部元素就是队头的元素,直接返回队头元素就可以了

具体实现如下:

  • 获取尾部元素

就是返回尾节点的元素

具体实现如下:

  • 检测队列是否为空

这里的实现与前面栈的实现其实是相同的,也是根据条件式来直接返回值

具体实现如下:

  • 所有需要实现的函数如下,可以仔细对照这些函数进行练习实现
  • 包含的头文件

总结

  • 一些需要注意的地方

1.对于栈和队列的实现,我们一定要搞清楚为什么要选择这种结构体类型,这对于我们对栈和队列的理解是十分重要的

2.其次,在初始化时也要好好思考尤其是栈的初始化,有关后面其他函数的实现

3.记得在每个函数实现前加入assert断言函数不同函数的实现前提是不一样的,不要固定了自己的思维

  • 最后的话

栈和队列也是数据结构入门很重要的基础知识了,一定要充分的理解,这样才有利于后续对数据结构的持续学习!!!(>_<)

如果你想学习关于栈和队列的题目的话,可以观看这篇博客哦:栈和队列OJ题目 – 小园得志



评论(1)

查看评论列表
评论头像
[…] 如果你对栈和队列的基础实现有问题的话,可以观看这篇博客哦:栈和队列的基本实现 – 小园得志 […]

发表评论

表情 颜文字

插入代码