系列文章目录
数据结构——顺序表的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语言实现栈,我们对栈有了最基本的了解和认识,但这远远不够,只有多刷题,利用栈解题,才能将知识融汇变通。
如果文章对你有所帮助,请不要吝啬你的三连哦!