栈是一个抽象的线性表,因此栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表。在线性表的表尾进行插入和删除操作,这里的表尾指的是栈顶,而不是栈底。这样使得它始终只在栈顶进行插入删除,栈底固定,最先进栈的只能在栈底。
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈也称为后进先出(List In First Out)的线性表,简称LIFO结构。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针
栈的插入操作叫做进栈也称压栈、入栈。
栈的删除操作叫做出栈
注意:
笔试习题:
一个栈的入栈序列为ABCDEF,则不可能的出栈序列是(D)
A、DEFCBA B、DCEFBA C、FEDCBA
D、FECDBA E、ABCDEF F、ADCBFE
分析:
该题主要是考虑栈的核心思想是先进后出,并且需要注意入栈和出栈的顺序是未知的,例如你可以先入栈ABCD,然后出栈D,然后入栈E,出栈E,入栈F,出栈F,然后CBA依次出栈,也就是A选项的情况。
任何出栈的元素 后面出栈的元素必须满足以下三点:
在原序列中相对位置比它小的,必须是逆序;
在原序列中相对位置比它大的,顺序没有要求;
以上两点可以间插进行。
选项D的出栈顺序FECDBA,明显出栈元素F后面的元素C和D不满足上面规律1,所以选项D是错误的,其它答案都是满足的。
因为栈是由线性表实现的,所以,栈有两种存储结构:顺序存储和链式存储。即顺序栈和链式栈。
1.结构定义
#define STACK_LEN 20 // 初始化栈的大小为20
// 顺序栈的结构定义
typedef struct Sqstack
{
int elem[STACK_LEN]; // 用于存放栈中元素的一维数组
int top; // 用来存放栈顶元素的下标
}Sqstack,*Pstack;
2.具体操作函数
值得注意的是,在这里因为是顺序结构你需要在
插入和出栈的时候,判断空或者满。
void InitStack(Pstack ps);
bool Push(Pstack ps,int val);
bool Pop(Pstack ps,int *rtv);//删除
bool GetTop(Pstack ps,int *rtv);//得到栈顶元素,但是不删除
bool IsEmpty(Pstack ps);
void Clear(Pstack ps);//top 指针的操作
void InitStack(Pstack ps)
{
assert(ps != NULL);
ps->top = 0;
}
static bool isFull(Pstack ps)
{
return ps->top == STACK_LEN;
}
bool Push(Pstack ps, int val)
{
if (isFull(ps))
{
return false;
}
ps->elem[ps->top++] = val;
return true;
}
bool IsEmpty(Pstack ps)
{
return ps->top == 0;
}
bool Pop(Pstack ps, int *rtv)//删除
{
if (IsEmpty(ps))
{
return false;
}
if (rtv != NULL)
{
ps->top--;
*rtv = ps->elem[ps->top];
}
return true;
}
bool GetTop(Pstack ps, int *rtv)//得到栈顶元素, 但是不删除
{
if (IsEmpty(ps))
{
return false;
}
if (rtv != NULL)
{
*rtv = ps->elem[ps->top - 1];
}
return true;
}
void Clear(Pstack ps)//top 指针的操作
{
ps->top = 0;
}
void Destroy(Pstack ps)
{
Clear(ps);
}
void Show(Pstack ps)
{
for (int i = 0; i < ps->top; i++)
{
printf("%d ", ps->elem[i]);
}
printf("\n");
}
之前顺序栈中数组的长度为define宏定义的常数。当元素个数超过数组最大长度时,就会插入失败。同顺序表的扩容一样,也可以实现扩容的顺序栈。即当元素个数超过设定的最大长度时,可以在再动态申请更大的内存来存放元素。
首先,因为该顺序栈要实现扩容,则不能用数组来实现。因此可以动态申请一块内存,将栈中元素放入其中。通过动态内存申请返回的指针来以数组的形式访问栈中元素。
其次,该动态内存的大小初始时可以设置默认值,如果超过默认值时,可以重新申请更大的内存,从而达到扩容的目的。
最后,需要知道栈中实际元素的个数。来记录顺序表中最后一个元素所在的位置下标。以及与默认长度进行对比。
扩容顺序栈的结构定义:
// 扩容顺序栈的结构定义
typedef struct SeqStack
{
int *data; // 动态申请顺序栈的内存空间
int size; // 顺序栈的实际长度
int maxlength; // 顺序栈的最大长度
}SeqStack,*Pstack;
基本操作:(基本操作和顺序栈相同,但由于扩容顺序栈为动态开辟,则需要增加扩容操作和销毁栈的操作)
void InitStack(Pstack ps); // 初始化栈
bool IsFull(Pstack ps); // 判满
void Inc(Pstack ps); // 扩容
bool Push(Pstack ps,int val); // 入栈
bool Pop(Pstack ps,int *rtv); // 出栈
bool GetTop(Pstack ps,int *rtv); //得到栈顶元素(不删除)
bool IsEmpty(Pstack ps); // 判空
void Clear(Pstack ps); // 清空栈
void Destroy(Pstack ps) ; // 销毁栈
void Show(Pstack ps); // 打印