【C语言数据结构与算法】五、线性表之栈
栈
观看这里的uu建议先看顺序表和链表相关内容
线性表之顺序表
线性表之单链表
线性表之双链表
一、前言
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列等。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
栈是一种特殊的线性表,既可以以数组形式存储也可以以链式结构的形式存储。以数组形式存储称为数组栈(特殊的线性表),以链式结构的形式存储称为链式栈(特殊的链表)。
一、栈
1·栈的概念及其结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

栈只在栈顶进行操作,不能在栈底和栈顶之间的部位进行操作。
2·栈的分类
栈分为数组栈和链式栈。
数组栈:以数组形式存储的栈。
链式栈:以链式结构的形式存储的栈。
数组栈和链式栈优缺点对比:
数组栈:
- 优点:第一、栈是栈顶进行操作,对数组栈而言,栈顶是顺序表的尾部,顺序表的尾部操作效率很高。第二、顺序表的顺序是正逻辑,即栈顶对应数组末端,栈底对应数组首端,操作方便。第三、顺序表的cache搬运效率比较高。
- 缺点:顺序表进行扩容后的容量随扩容的次数指数增长,难免会有空间的浪费。
链式栈:
- 优点:链表没有空间浪费,空间的大小始终是所需要的空间。
- 缺点:链表进行栈的操作时,如果尾部作为栈顶,单链表的效率不高,只有双向链表进行尾部操作时,效率会高一些,但是效率仍然比顺序表低一些。第二、双向链表操作时结构比较复杂。第三、如果是单链表形式的栈,只有头部作为栈顶,对头部进行操作,效率会高,但是这样链表的顺序是反逻辑,操作不方便。
综合考虑,数组栈是最优选择。
3·栈的接口函数
栈头文件List.h的声明如下:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a; //数组起点地址指针
int top; // 栈顶
int capacity; // 容量
}ST;
// 初始化栈
void StackInit(ST* ps);
// 扩容
void CheckStackcapaticy(ST* ps);
// 入栈
void StackPush(ST* ps, STDataType x);
// 出栈
void StackPop(ST* ps);
// 获取栈顶元素
STDataType StackTop(ST* ps);
// 获取栈中有效元素个数
int StackSize(ST* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(ST* ps);
// 销毁栈
void StackDestroy(ST* ps);
// 打印栈
void StackPrint(ST* ps);
三、栈的实现
栈的实现大部分是顺序表中的一些操作,这里不做详细解释,只给源码和某些函数调试结果。
函数的实现思路在代码注释中已详细解释。
1·初始化、扩容与销毁
初始化函数在Stack.c中如下:
// 初始化栈
void StackInit(ST* ps)
{
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
扩容函数在Stack.c中如下:
// 扩容
void CheckStackcapaticy(ST* ps)
{
//如果数据存满
if (ps->top == ps->capacity)
{
//如果还未开辟空间,则设置为4,如果开辟空间,则新容量为旧容量的两倍
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//开辟空间
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
//扩容失败
if (tmp == NULL)
{
printf("realloc fail");
exit(-1);
}
//扩容成功
else
{
ps->a = tmp;
//当前容量设置为新容量值
ps->capacity = newcapacity;
}
}
}
销毁函数在Stack.c中如下:
// 销毁栈
void StackDestroy(ST* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
以上函数与顺序表中的毫无差别。
2·入栈、出栈、判断栈是否为空
入栈函数在Stack.c中如下:
// 入栈
void StackPush(ST* ps, STDataType x)
{
//检查并扩容
CheckStackcapaticy(ps);
//入栈
ps->a[ps->top] = x;
//栈顶加一
ps->top++;
}
出栈函数在Stack.c中如下:
// 出栈
// 出栈
void StackPop(ST* ps)
{
//不为空出栈
assert(!StackEmpty(ps));
//不能访问相对于删除
ps->top--;
}
判断栈是否为空在Stack.c中如下:
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(ST* ps)
{
return ps->top == 0;
}
具体使用方式如下:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stack.h"
//入栈出栈
void StackTest1()
{
ST p ;
//初始化
StackInit(&p);
//入栈1234
StackPush(&p, 1);
StackPush(&p, 2);
StackPush(&p, 3);
StackPush(&p, 4);
//出栈
StackPop(&p);
StackPop(&p);
//再次出栈
StackPop(&p);
StackPop(&p);
StackPop(&p);
}
int main()
{
StackTest1();
return 0;
}
执行调试结果如下:
1·从栈顶存入1234,并对相应变量进行监测:

2·以上面情况为基础,出栈两次,并对相应变量进行监测:

p.top变为2,意味着当前栈中能被访问到的数据只有a[0]和a[1]。
3·以上面情况为基础,再出栈三次,并对相应变量进行监测:

当前只有两个数据却出栈三次,因此程序中断并报错。
3·获取栈顶元素
获取栈顶元素在Stack.c中如下:
// 获取栈顶元素
STDataType StackTop(ST* ps)
{
//不为空可获取
assert(!StackEmpty(ps));
return ps->a[ps->top-1];
}
具体使用方式如下:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stack.h"
//获取栈顶元素
void StackTest2()
{
ST p ;
//初始化
StackInit(&p);
//入栈1234
StackPush(&p, 1);
StackPush(&p, 2);
StackPush(&p, 3);
StackPush(&p, 4);
//获取栈顶元素
STDataType top1 = StackTop(&p);
//出栈一数据后继续获取栈顶元素
StackPop(&p);
STDataType top2 = StackTop(&p);
}
int main()
{
StackTest2();
return 0;
}
执行调试结果如下:
从栈顶存入1234,获取栈顶元素并对相应变量进行监测:

4·从栈顶开始打印数据
打印函数在Stack.c中如下:
// 遍历打印栈
void StackPrint(ST* ps)
{
//不为空执行
while (!StackEmpty(ps))
{
//从栈顶访问,因此从栈顶遍历打印
printf("%d ", StackTop(ps));
//每打印栈顶出栈一次
StackPop(ps);
}
}
具体使用方式如下:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stack.h"
//从栈顶开始打印数据
void StackTest3()
{
ST p;
//初始化
StackInit(&p);
//入栈1234
StackPush(&p, 1);
StackPush(&p, 2);
StackPush(&p, 3);
StackPush(&p, 4);
//从栈顶打印元素
StackPrint(&p);
}
int main()
{
StackTest3();
return 0;
}
执行调试结果如下:
从栈顶存入1234,从栈顶开始打印数据:


浙公网安备 33010602011771号