文章目录
栈
1. 栈的定义
类似弹夹里的子弹一样先进去后出来,后进的先出来。
栈(stack)是限定仅在表尾进行插入和删除操作的线性表。
栈顶:允许插入和删除的一端,
栈底:固定的,不允许插入和删除的另一端
栈又称为后进先出的线性表,简称LIFO结构。
2. 栈的抽象数据模型
进栈(插入) push
出栈(删除)pop
Operation
InitStack(*S):初始化操作,建立一个空栈S
DestoryStack(*S):若栈存在,则销毁它
ClearStack(*S):将栈清空
StackEmpty(S):若栈为空,返回True,否则返回False
GetTop(S,*e):若栈存在且非空,用e返回S的栈顶元素
Push(*S,e):若栈S存在,插入新元素e到栈S中并成为栈顶元素
Pop(*S,*e):删除栈S中栈顶元素,并用e返回其值。
StackLength(S):返回栈S的元素个数
3. 栈的顺序存储结构
栈的定义:
typedef int SElemType;
typedef struct
{
SElemType data[MAXSIZE];
int top;//用于栈顶指针
}SqStack;
3.1 进栈push
//插入元素e为新的栈顶元素
Status Push(SqStack *S,SElemType e)
{
if(S->top == MAXSIZE-1)//栈满
{
return ERROR;
}
S->top++;
S->data[s->top] = e;
return OK;
}
3.2 出栈pop
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack *S,SElemType *e)
{
if(S->top == -1)
return ERROR;
*e = S->data[S->top];//将要删除的栈顶元素赋值给e
S->top--;//栈顶指针减一
return OK;
}
Tips:如果我们有两个相同类型的栈,我们为他们各自开辟了数组空间,极有可能出现第一个栈满了,再进栈就溢出了,而另一个栈还有很多存储空间空闲,所以就有了两栈共享空间。
4. 两栈共享空间
简单来说,一个数组有两个端点,而两个栈有两个栈底,一个栈的栈底为数组的始端,即下标为0处,另一个栈的的栈底为数组的尾端,两个栈由两个端点向中间延伸。
通常适用于一个栈增长时另一个栈在缩短的情况(两个栈相同数据类型)。
两栈为空:0号栈:top1 = -1 1号栈:top2 = MaxSize
栈满:top1 + 1 == top2
//两栈共享空间结构
typedef struct
{
SElemType data[MAXSIZE];
int top1;//栈顶指针
int top2;
}SqDoubleStack;
4.1 进栈push
需要插入元素值参数和判断是0号栈还是1号栈的栈号参数 stackNumber
//插入元素e为新的栈顶元素
Status Push(SqDoubleStack *S,SElemType e,int stackNumber)
{
if(S->top1+1 == S->top2)//栈满
return ERROR;
if(stackNumber == 0)//0号栈有元素进栈
S->data[++S->top1] = e;
else if(stackNumber == 1)//1号栈有元素进栈
S->data[--S->top2] = e;
}
4.2 出栈pop
Status Pop(SqDoubleStack *S,SElemType e,int stackNumber)
{
if(stackNumber == 0)//0号栈有元素出栈
if(S->top1 == -1)//空栈
return ERROR;
*e = S->data[S->top1--];
else if(stackNumber == 1)//1号栈有元素出栈
{
if(S->top2 == MAXSIZE)//空栈
return ERROR;
*e = S->data[S->top2++];
}
return OK;
}
5. 栈的链式存储结构(链栈)
Tips:链栈不需要头节点,且基本不存在栈满的情况(除非内存没有可以使用的空间)。
//空栈时,头指针指向空,即 top = NULL
typedef struct StackNode
{
SELemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack
{
LinkStackPtr top;
int count;
}LinkStack;
5.1 进栈push
//插入元素e为新的栈顶元素
Status push(LinkStack *S,SElemType e)
{
LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
s->data = e;
s->next = S->top;//当前的栈顶元素赋值给新结点的直接后继
S->top = s;//新的结点s赋值给栈顶指针
S->count++;
return OK;
}
时间复杂度为O(1)
5.2 出栈pop
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(LinkStack *S,SElemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))
return ERROR;
*e = S->top->data;
p = S->top;//将栈顶结点赋值给p
S->top = S->top->next;//栈顶指针下移一位
free(p);//释放结点p
S->count--;
return OK;
}
时间复杂度为O(1)
6. 栈的应用:递归
6.1 斐波那契数列的实现(兔子繁殖问题)
//斐波那契的递归函数
int Fbi(int i)
{
if(i<2)
return i == 0 ? 0 : 1;
return Fbi(i-1)+Fbi(i-2);//这里的Fbi就是函数自己,它在调用自己
}
int main()
{
int i;
for(int i = 0;i < 40;i++)
{
printf("%d",Fbi(i));
}
return 0;
}