数据结构——栈的C语言代码实现

系列文章目录

数据结构——顺序表的C语言代码实现
数据结构——八种链表的C语言代码实现
数据结构——栈的C语言代码实现
数据结构——队列的C语言代码实现
数据结构——堆的C语言代码实现


前言

该篇文章主要介绍栈是概念,栈的常见种类、栈的实现

提示:以下是本篇文章正文内容,下面案例可供参考

一、基础知识

1.栈的概念(stack)

援引百科:
栈的正式定义是:栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底

简而言之,栈是一种特殊的线性表 ,它只允许在栈的一端进行插入和删除的操作,此端就是栈顶。
此外,在栈中插入被称为压栈,删除被称为出栈
在这里插入图片描述

2.栈的种类

数据的物理存储结构中最常用的就是:顺序存储和链式存储。

顺序表和链表
根据栈只在一端操作的特殊性,以及我们对顺序表和链表的熟悉,很容易联想到

❶顺序表的尾插尾删
❷顺序表的头插头删
❸链表的头插头删
❹链表的尾插尾删

总的来看四种实现方法,我们应该怎么选择?
(1)先利用以前对顺序表和链表的实现经历:
我们知道在顺序表的头部和中部进行插入和删除,需要移动数组,非常繁琐;而链表的尾插尾删虽然不用移动链表,但我们需要遍历查找尾节点前的一个节点,讲他的Next指针置为空,同样麻烦。即使使用双向链表,在操作简便性上有所改进,仍会造成节点过于庞大,相较数组没有优势。故可以淘汰顺序表的头插头删和链表的头插头删;
(2)再结合顺序表和链表的优缺点:
原本二者比较时,顺序表降维打击的优势便是支持随机访问。但是在栈的特性限制下,该优势荡然无存。貌似局势逆转,却仍优势不变!因为顺序表在内存访问方面拥有致命优势——命中率高,该概念可拜读大神文章CPU缓存知识
因此,在实现栈时,采用顺序表的尾插尾删具有无可比拟的优势!
在使用顺序表的知识实现栈时,仍需要选择静态和动态,此二者间的优劣关系已无需赘述。

二、代码实现

1.stack.h

(1) 引用函数库

注意,c语言并不直接支持bool类型数据,在使用时要引用stdbool.h
代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
typedef int DataType;

(2) 定义栈

实现栈时,我们可以比照顺序表,设置size和capacity,为了后续实现打印栈顶元素的直观,该处将size命名为Top
代码如下

typedef int DataType;
typedef struct stack
{
	DataType* arr;
	int Top;
	int Capacity;
}ST;

(3) 声明接口函数

仍是常见的接口函数:初始化、压栈、出栈、销毁、判断空栈、取栈顶元素、栈元素个数。
代码如下:

//初始化顺序栈
void STInit(ST *ps);
//压栈
void STPush(ST * ps, DataType x);
//出栈
void STPop(ST *ps);
//栈顶元素
DataType STTop(ST *ps);
//检测栈是否为空
bool STEmpty(ST* ps);
//栈的元素个数
int STSize(ST* ps);
//销毁栈
void STDestroy(ST* ps);

2.stack.c

(1)初始化顺序栈

代码如下:

//初始化顺序栈
void STInit(ST* ps)
{
	ps->arr = NULL;
	ps->Top = ps->Capacity = 0;
}

(2)压栈

代码如下:

//压栈
void STPush(ST* ps, DataType x)
{
	/*if (ps == NULL)
	{
		exit(-1);
	}*/
	if (ps->Top == ps->Capacity)
	{
		int NewCapacity =ps->Capacity == 0 ? 4 : ps->Capacity * 2;
		//进行扩容
		DataType* tem = realloc(ps->arr, NewCapacity * sizeof(DataType));
		//注意realloc函数使用细节
		if (tem != NULL)
		{
			ps->arr = tem;
		}
		else
		{
			printf("扩容失败!\n");
			return;
		}
		//更新容量
		ps->Capacity = NewCapacity;
	}
	ps->arr[ps->Top] = x;
	ps->Top++;
}

(3)出栈

代码如下:

//出栈
void STPop(ST* ps)
{
	if (ps->Top == 0)
	{
		printf("栈为空\n");
		return;
	}
	ps->Top--;
}

(4)栈顶元素

我们在实现压栈操作时,先存储数据然后再递加Top,所以我们在取栈顶元素时要把Top减一;这和的数组情况类似,数组是从下标0开始存储数据的,而数组中元素个数是从1增加的,因此第n个数的下标为n-1
代码如下:

//栈顶元素
DataType STTop(ST* ps)
{
	//注意此时Top要减一,因为在压栈过程中我
	//们先进行数据进栈,然后再Top加一
	return ps->arr[(ps->Top)-1];
}

(5)检测栈是否为空

此处判断也可以使用if语句进行分类讨论,但不如这种写法简便。
代码如下:

//检测栈是否为空
bool STEmpty(ST* ps)
{
	return ps->Top == 0;
}

(6)栈的元素个数

注意此时的Top不用减一,原因如上!
代码如下:

//栈的元素个数
int STSize(ST* ps)
{
	return ps->Top;
}

(7)销毁栈

压栈过程中我们只是递减了Top,并没有销毁对应的空间。因此在栈使用结束后,要记得进行销毁操作
代码如下:

//释放栈
void STDestroy(ST* ps)
{
	free(ps->arr);
	ps->Capacity = 0;
	ps->Top = 0;
	ps = NULL;
}

3.test.c

代码如下

#include"stack.h"
int main()
{
	ST *st=(ST*)malloc(sizeof(ST));
	STInit(st);
	STPush(st, 1);
	STPush(st, 1);
	STPush(st, 1);
	STPush(st, 1);
	//注意栈打印栈顶元素的方法
	//先取栈顶元素,然后打印出来
	//再调用压栈函数,使栈顶元素更新
	while (!STEmpty(st))
	{
		printf("%d ", STTop(st));
		STPop(st);
	}
	printf("\n%d\n", STSize(st));
	STPush(st, 1);
	STPush(st, 3);
	STPush(st, 4);
	while (!STEmpty(st))
	{
		printf("%d ", STTop(st));
		STPop(st);
	}
	STPush(st, 5);
	STPush(st, 6);
	STPush(st, 7);
	while (!STEmpty(st))
	{
		printf("%d ", STTop(st));
		STPop(st);
	}
	printf("\n%d\n", STSize(st));
	STDestroy(st);
	return 0;
}

该代码中使用的是传值调用,定义的st是ST*类型的,要注意一定不可以直接给st赋NULL,应当使用动态开辟,为其分配空间。
此时也可以使用传址调用,只需要定义ST类型的变量,接口函数中传&st即可。
注意:在打印栈时,先使用取栈顶的函数取得栈顶元素并打印,然后使用压栈函数使栈顶元素更新。


总结

通过运用C语言实现栈,我们对栈有了最基本的了解和认识,但这远远不够,只有多刷题,利用栈解题,才能将知识融汇变通。
如果文章对你有所帮助,请不要吝啬你的三连哦!
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值