1. 栈的抽象数据类型定义:
2.顺序栈的存储方式
同一般线性表的顺序存储结构完全相同:
利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
- 附设top指针,指示栈顶元素在顺序栈中的位置
- 另设base指针,指示栈底元素在顺序栈中的位置
- 另外,用stacksize表示栈可使用的最大容量
但是,为了方便操作,通常top指向真正的栈顶元素 之上的 下标地址,如下图
3.顺序栈的相关基础操作汇总
- 初始化操作:操作结果:构造一个空栈 S。
InitStack(SqStack* s)
- 判定S是否为空栈:
初始条件:栈S 已存在。
操作结果:若栈S为空栈,则返回TRUE,否则 FALSE.
StackEmpty(SqStack s)
- 求栈的长度
初始条件:栈S 已存在。
操作结果:返回S的元素个数,即栈的长度
StackLength(SqStack s)
- 栈置空操作:
初始条件:栈S 已存在。
操作结果:将S清为空栈。
ClearStack(SqStack * s)
- 销毁栈操作:
初始条件:栈S 已存在。
操作结果:栈S 被销毁。
DestroyStack(SqStack *s)
- 入栈操作(重点)
初始条件:栈S已存在且未满。
操作结果:插入元素e为新的栈顶元素。
Push(SqStack* s, SElemType e)
- 出栈操作(重点)
初始条件:栈S 已存在且非空:
操作结果:删除S的栈顶元素a.,并用e返回其值。
Pop(SqStack* s, SElemType* e)
- 取栈顶元素
初始条件:栈S 已存在且非空。
操作结果:用e返回S的栈顶元素。
GetTop(SqStack s, SElemType* e)
- 从栈顶开始输出整栈
初始条件:栈S 已存在且非空。
操作结果:从栈顶输出栈中所有元素。
printStack(SqStack s)
4.整栈操作模拟与异常分析
4.1操作模拟:
由于本篇中顺序栈含栈顶,栈底指针;并且栈顶指针指向栈顶元素的下一个位置
所以空栈与栈满的判断条件也有所不同:
4.2异常处理:
使用数组作为顺序栈存储方式的特点:
而这里我们要注意:
上溢是一种错误,使问题的处理无法进行;
而下溢一般认为是一种结束条件,即问题处理结束。
5.顺序栈的整表创建(含栈底,栈顶指针)
5.1顺序栈的结构定义:
typedef struct Sqstack
{
SElemType* base;//栈底指针
SElemType* top;//栈顶指针
int stacksize;//栈可用的最大容量
}SqStack;
- 在结构定义中,一个顺序栈里包含了栈底指针,栈顶指针,以及栈可用的最大容量这三个部分
- 而别名SqStack用来定义顺序栈
SqStack* 用来表示指向栈的指针
5.2顺序栈的初始化
注意:传入参数时,需传入栈的指针,即&s,这样才能修改栈中的数据
bool InitStack(SqStack* s)
5.2.1算法步骤:
(1)为顺序栈初始化空间,同时让栈底指针(base)指向被初始化的空间
(2)初始化栈顶指针 指向栈底位置
(3)初始化该顺序栈的最大容量
//2.顺序栈的初始化
//注意:传入参数时,需传入栈的指针,即&s,这样才能修改栈中的数据
bool InitStack(SqStack* s)
{
//[1]为顺序栈初始化空间,同时让栈底指针指向被初始化的空间
s->base = (SElemType *)malloc(sizeof(SElemType) * MAXSIZE);
if (s->base == NULL)
{
printf("内存分配失败!\n");
exit(-1);
}
//[2]初始化栈顶指针 指向栈底位置
s->top = s->base;
//[3]初始化该顺序栈的最大容量
s->stacksize = MAXSIZE;
return true;
}
5.3判断顺序栈是否为空
注意:传入参数时,不需要传入栈的指针,即&s,因为判空操作只需要访问栈顶指针与栈底指针,并不做修改
bool StackEmpty(SqStack s)
5.3.1算法步骤:
(1)若栈顶指针与栈底指针相同,说明栈空;否则栈非空,
//3.判断顺序栈是否为空
//注意:传入参数时,不需要传入栈的指针,即&s,因为判空操作只需要访问栈顶指针与栈底指针,并不做修改
bool StackEmpty(SqStack s)
{
//[1]若栈顶指针与栈底指针相同,说明栈空
if (s.top == s.base)
{
return true;
}
else
{
return false;
}
}
5.4求顺序栈的长度
注意:传入参数时,不需要传入栈的指针,即&s,因为求长度只需要计算栈顶指针与栈底指针之差,并不做修改
int StackLength(SqStack s)
5.4.1算法步骤:
(1)栈顶指针 与 栈底指针的 差值即为顺序栈的长度
这里直接使用指针的差值,不需要"/sizeof(SqElemType)";因为两个指针的基类型为同一类型,其相减结果即为顺序栈的长度
//4.求顺序栈的长度
//注意:传入参数时,不需要传入栈的指针,即&s,因为求长度只需要计算栈顶指针与栈底指针之差,并不做修改
int StackLength(SqStack s)
{
//栈顶指针 与 栈底指针的 差值即为顺序栈的长度
return s.top - s.base;
}
5.5清空顺序栈
注意:传入参数时,需要传入栈的指针,即&s,因为清空栈需要修改栈顶指针,即s.top
bool ClearStack(SqStack * s)
5.5.1算法步骤:
(1)判断栈内存是否分配(通过判断栈底指针是否为空来实现)
(2)修改栈顶指针指向栈底位置即为清空(由于进栈时值会覆盖,故不需要删除元素值)
//5.清空顺序栈
//注意:传入参数时,需要传入栈的指针,即&s,因为清空栈需要修改栈顶指针,即s.top
bool ClearStack(SqStack * s)
{
//[1]判断栈内存是否分配
if (s->base == NULL)
{
printf("栈不存在!\n");
return false;
}
//[2]修改 栈顶指针 指向 栈底位置,即为清空
//(由于进栈时值会覆盖,故不需要删除元素值)
s->top = s->base;
return true;
}
5.6销毁顺序栈
注意:传入参数时,需要传入栈的指针,即&s,因为销毁栈需要修改清空内存,并置空栈中指针以及栈大小
bool DestroyStack(SqStack *s)
5.6.1算法步骤:
(1)若当前顺序栈存在,则先释放内存,再置空栈大小以及栈指针
<1>通过栈底指针释放内存
<2>置空栈大小以及栈指针(防止悬挂指针问题)
最终栈里除了stacksize,base和top被置空以外,其余部分均被销毁
//6.销毁顺序栈
//注意:传入参数时,需要传入栈的指针,即&s,因为销毁栈需要修改清空内存,并置空栈中指针以及栈大小
bool DestroyStack(SqStack *s)
{
//[1]若当前顺序栈存在,则先释放内存,再置空栈大小以及栈指针
if (s->base != NULL)
{
//<1>通过栈底指针释放内存
free(s->base);
//<2>置空栈大小以及栈指针
s->stacksize = 0;
s->base = NULL;
s->top = NULL;
}
return true;
}
5.7顺序栈的入栈(核心1)
注意:传入参数时,需要传入栈的指针,即&s,因为入栈操作需要 更改栈顶指针以及 添加栈顶元素
bool Push(SqStack* s, SElemType e)
5.7.1算法步骤:
(1)判断栈是否已满(即 栈顶指针 与 栈底指针 之差 是否等于 栈的最大容量)
(2)入栈操作:
<1>先将元素e赋值给栈顶指针当前指向的位置
<2>然后栈顶指针自增1,指向当前 栈顶元素的 下一个位置
入栈操作可以浓缩为一步:
*s->top++ = e;
//7.核心1:顺序栈的入栈
//注意:传入参数时,需要传入栈的指针,即&s,因为入栈操作需要 更改栈顶指针以及 添加栈顶元素
bool Push(SqStack* s, SElemType e)
{
//[1]判断栈是否已满(即 栈顶指针 与 栈底指针 之差 是否等于 栈的最大容量)
if (s->top - s->base == s->stacksize)
{
printf("栈满!\n");
return false;
}
//[2]入栈操作:
//<1>先将元素e赋值给栈顶指针当前指向的位置
*(s->top) = e;
//<2>然后栈顶指针自增1,指向当前 栈顶元素的 下一个位置
(s->top)++;
//等价于 *s->top++ = e;先赋值,再向下移动
return true;
}
5.8顺序栈的出栈(核心2)
注意:传入参数时,需要传入栈的指针,即&s,因为出栈操作需要 更改栈顶指针以及 带出栈顶元素
bool Pop(SqStack* s, SElemType* e)
5.8.1算法步骤:
(1)判断是否栈空(即栈顶指针 是否与 栈底指针指向相同)
(2)出栈操作:
<1>先将栈顶指针自减1,让其指向当前栈顶元素
<2>再将当前栈顶指针指向的栈顶元素 赋值给e
出栈操作可以浓缩为一步:
*e= *(–s->top);
//8.核心2:顺序栈的出栈
//注意:传入参数时,需要传入栈的指针,即&s,因为出栈操作需要 更改栈顶指针以及 带出栈顶元素
bool Pop(SqStack* s, SElemType* e)
{
//[1]判断是否栈空(即栈顶指针 是否与 栈底指针指向相同)
if (s->base == s->top)
{
printf("栈空!\n");
return false;
}
//[2]出栈操作:
//<1>先将栈顶指针自减1,让其指向当前栈顶元素
--s->top;
//<2>再将当前栈顶指针指向的栈顶元素 赋值给e
*e = *(s->top);
//等价于:*e=*(--s->top);先向底部移动栈顶指针,再取出栈顶元素
return true;
}
5.9取栈顶元素
注意:传入参数时,不需要传入栈的指针,即&s,因为只取元素,不修改栈的数据
bool GetTop(SqStack s, SElemType* e)
5.9.1算法步骤:
(1)判断栈是否为空
(2)获取栈顶元素(注意是s.top - 1处的元素)
//9.取栈顶元素
//注意:传入参数时,不需要传入栈的指针,即&s,因为只取元素,不修改栈的数据
bool GetTop(SqStack s, SElemType* e)
{
//[1]判断栈是否为空
if (s.base == s.top)
{
printf("栈空!\n");
return false;
}
//[2]获取栈顶元素:
*e = *(s.top - 1);
return true;
}
5.10输出顺序栈中的所有元素(从顺序栈顶开始输出)
bool printStack(SqStack s)
5.10.1算法步骤:
(1)判断该顺序栈是否为空
(2)定义临时指针指向当前栈顶元素(s.top - 1处的元素)
(3)p从栈顶开始,向栈底移动并依次输出栈中的元素
//10.输出顺序栈中的所有元素(从顺序栈顶开始输出)
bool printStack(SqStack s)
{
//[1]判断该顺序栈是否为空
if (s.base == s.top)
{
printf("顺序栈为空!\n");
return false;
}
//[2]定义临时指针指向当前栈顶元素
SElemType* p = s.top - 1;
//[3]p从栈顶开始,向栈底移动并依次输出栈中的元素
while (p>=s.base)
{
printf("%d-->", *(p));
p--;
}
printf("end\n");
return true;
}
21.所有操作如下:
//顺序栈的基本操作与实现(1.为了操作方便,栈顶指针指向栈顶元素的下一位置)
//(2.直接使用指针来定义栈顶,栈底指针)
#include<stdio.h>
#include<stdlib.h>
#define bool int
#define true 1
#define false 0
typedef int SElemType;//栈中数据元素的类型定义
#define MAXSIZE 100//顺序栈的最大容量
//1.顺序栈的结构(这里直接使用指针来 定义栈顶指针 与 栈底指针)
//实际上也可以使用int类型的角标来模拟栈顶,栈底指针
typedef struct Sqstack
{
SElemType* base;//栈底指针
SElemType* top;//栈顶指针
int stacksize;//栈可用的最大容量
}SqStack;
//2.顺序栈的初始化
//注意:传入参数时,需传入栈的指针,即&s,这样才能修改栈中的数据
bool InitStack(SqStack* s)
{
//[1]为顺序栈初始化空间,同时让栈底指针指向被初始化的空间
s->base = (SElemType *)malloc(sizeof(SElemType) * MAXSIZE);
if (s->base == NULL)
{
printf("内存分配失败!\n");
exit(-1);
}
//[2]初始化栈顶指针 指向栈底位置
s->top = s->base;
//[3]初始化该顺序栈的最大容量
s->stacksize = MAXSIZE;
return true;
}
//3.判断顺序栈是否为空
//注意:传入参数时,不需要传入栈的指针,即&s,因为判空操作只需要访问栈顶指针与栈底指针,并不做修改
bool StackEmpty(SqStack s)
{
//[1]若栈顶指针与栈底指针相同,说明栈空
if (s.top == s.base)
{
return true;
}
else
{
return false;
}
}
//4.求顺序栈的长度
//注意:传入参数时,不需要传入栈的指针,即&s,因为求长度只需要计算栈顶指针与栈底指针之差,并不做修改
int StackLength(SqStack s)
{
//栈顶指针 与 栈底指针的 差值即为顺序栈的长度
return s.top - s.base;
}
//5.清空顺序栈
//注意:传入参数时,需要传入栈的指针,即&s,因为清空栈需要修改栈顶指针,即s.top
bool ClearStack(SqStack * s)
{
//[1]判断栈内存是否分配
if (s->base == NULL)
{
printf("栈不存在!\n");
return false;
}
//[2]修改 栈顶指针 指向 栈底位置,即为清空
//(由于进栈时值会覆盖,故不需要删除元素值)
s->top = s->base;
return true;
}
//6.销毁顺序栈
//注意:传入参数时,需要传入栈的指针,即&s,因为销毁栈需要修改清空内存,并置空栈中指针以及栈大小
bool DestroyStack(SqStack *s)
{
//[1]若当前顺序栈不为空,则先释放内存,再置空栈大小以及栈指针
if (s->base != NULL)
{
//<1>通过栈底指针释放内存
free(s->base);
//<2>置空栈大小以及栈指针
s->stacksize = 0;
s->base = NULL;
s->top = NULL;
}
return true;
}
//7.核心1:顺序栈的入栈
//注意:传入参数时,需要传入栈的指针,即&s,因为入栈操作需要 更改栈顶指针以及 添加栈顶元素
bool Push(SqStack* s, SElemType e)
{
//[1]判断栈是否已满(即 栈顶指针 与 栈底指针 之差 是否等于 栈的最大容量)
if (s->top - s->base == s->stacksize)
{
printf("栈满!\n");
return false;
}
//[2]入栈操作:
//<1>先将元素e赋值给栈顶指针当前指向的位置
*(s->top) = e;
//<2>然后栈顶指针自增1,指向当前 栈顶元素的 下一个位置
(s->top)++;
//等价于 *s->top++ = e;先赋值,再向下移动
return true;
}
//8.核心2:顺序栈的出栈
//注意:传入参数时,需要传入栈的指针,即&s,因为出栈操作需要 更改栈顶指针以及 带出栈顶元素
bool Pop(SqStack* s, SElemType* e)
{
//[1]判断是否栈空(即栈顶指针 是否与 栈底指针指向相同)
if (s->base == s->top)
{
printf("栈空!\n");
return false;
}
//[2]出栈操作:
//<1>先将栈顶指针自减1,让其指向当前栈顶元素
--s->top;
//<2>再将当前栈顶指针指向的栈顶元素 赋值给e
*e = *(s->top);
//等价于:*e=*(--s->top);先向底部移动栈顶指针,再取出栈顶元素
return true;
}
//9.取栈顶元素
//注意:传入参数时,不需要传入栈的指针,即&s,因为只取元素,不修改栈的数据
bool GetTop(SqStack s, SElemType* e)
{
//[1]判断栈是否为空
if (s.base == s.top)
{
printf("栈空!\n");
return false;
}
//[2]获取栈顶元素:
*e = *(s.top - 1);
return true;
}
//10.输出顺序栈中的所有元素(从顺序栈顶开始输出)
bool printStack(SqStack s)
{
//[1]判断该顺序栈是否为空
if (s.base == s.top)
{
printf("顺序栈为空!\n");
return false;
}
//定义临时指针指向当前栈顶元素
SElemType* p = s.top - 1;
//p从栈顶开始,向栈底移动并依次输出栈中的元素
while (p>=s.base)
{
printf("%d-->", *(p));
p--;
}
printf("end\n");
return true;
}
int main()
{
SqStack s1;
InitStack(&s1); // 初始化栈
// 入栈
Push(&s1, 10);
Push(&s1, 20);
Push(&s1, 30);
Push(&s1, 31);
Push(&s1, 32);
printf("当前栈的长度: %d\n", StackLength(s1));
printf("\n");
// 获取栈顶元素
SElemType ee;
GetTop(s1, &ee);
printf("当前栈顶元素为:%d\n", ee);
printf("\n");
printf("从当前栈顶元素向下输出:\n");
printStack(s1);
// 出栈
printf("\n");
SElemType e[10];
for (int i = 0; i < 3; i++)
{
Pop(&s1, &e[i]);
printf("出栈元素: %d\n", e[i]);
}
printf("\n");
printf("从当前栈顶元素向下输出:\n");
printStack(s1);
// 清空栈
ClearStack(&s1);
printf("栈是否为空: \n");
if (StackEmpty(s1))
{
printf("栈为空!\n");
}
// 销毁栈
if (DestroyStack(&s1))
{
printf("销毁成功!\n");
}
return 0;
}