Stack 栈(更新时间:2016-04-13)
吼吼,终于把线性表施工完成了,现在开始给线性表的扩展栈和队列的施工。
本来昨天就能写完的,结果昨天看了CUBA东南赛区决赛,激动的没有心情写了!
简介
栈 是一种线性表的扩展。当我们在讲线性表的时候是不考虑线性表的顺序的。而当我们规定线性表的表头和表尾之后,我们可以按照元素进出表的顺序来规定特定的数据结构。其中栈是元素先进后出的一种线性表,即表的一端封闭,只能在表的另一端出入元素。
下面给出示意图:
对于栈的存储,同线性表一样有 线性存储 和 链式存储 两种。
代码样例(线性存储)
下面先介绍线性存储:
struct StackNode;
typedef struct StackNode *PtrToNode;
typedef PtrToNode Stack;
typedef int ElementType;
struct StackNode
{
int capacity; //栈的容量
int TOP; //栈顶指针
ElementType *Element; //数值域
};
栈的初始化:
Stack Initial( void ) //栈的初始化
{
Stack S;
S = ( Stack ) malloc ( sizeof( struct StackNode ) ); //分配表头空间
if( S == NULL ) //判断是否分配到空间
{
cout << "Out of space!\n";
exit(0);
}
S->capacity = MaxN; //分配初始栈容量
S->TOP = -1; //初始化栈顶指针
S->Element = ( ElementType* ) malloc ( S->capacity * sizeof( ElementType ) );
if( S->Element == NULL ) //分配并判断数值域空间
{
cout << "Out of space!\n";
exit(0);
}
return S; //返回表头指针
}
对于一个栈来说,最重要的两种操作就是在栈顶 压入元素(PUSH) 和 弹出元素(POP)
Push顾名思义就是在栈的最顶端压入一个元素,使得栈里面的元素增多:
Stack Push( Stack S, ElementType X ) //压栈操作
{
if( IsFull(S) ) //是否栈满
{
S->capacity++; //扩容并重新分配数值域
S->Element = ( ElementType* ) realloc ( S->Element, S->capacity * sizeof( ElementType ) );
}
S->Element[ ++S->top ] = X; //将元素压入栈顶
return S;
}
与Push相对,Pop操作则是获得栈顶的元素,并将其从栈里面弹出来,使得栈中的元素减少:
ElementType Pop( Stack S ) //出栈操作
{
if( IsEmpty( S ) ) //是否栈空
{
cout << "Empty Stack!\n";
return 0;
}
return S->Element[ S->top-- ]; //返回栈顶,并将栈顶指针下移
}
下面给出相关的辅助函数:
int IsEmpty( Stack S ) //判断是否栈空
{
return (S->top < 0);
}
int IsFull( Stack S ) //判断是否栈满
{
return ( S->top == S->capacity );
}
ElementType Top( Stack S ) //返回栈顶元素
{
if( !IsEmpty( S ) )
return S->Element[ S->top ];
else
return 0;
}
Stack MakeEmpty( Stack S ) //清空栈中元素
{
while( !IsEmpty( S ) )
Pop( S );
return S;
}
void Delete( Stack S ) //删除栈
{
free( S->Element ); //释放数值域
free( S ); //释放头节点
}
代码样例(链式存储)
链式存储通过使用链表来实现栈,其操作种类与线性存储基本一致。由于采用链式存储,所以栈在操作过程中不受到容量的限制,因此也就没有了判断栈满以及栈扩容等操作。
struct StackNode;
typedef struct StackNode *PtrToNode;
typedef PtrToNode Stack;
typedef PtrToNode SNode;
typedef int ElementType;
struct StackNode
{
ElementType Element; //数值域
SNode Next;
};
Stack Initial( void ) //栈的初始化
{
Stack S;
S = ( Stack ) malloc ( sizeof( struct StackNode ) ); //分配表头空间
if( S == NULL ) //判断是否分配到空间
{
cout << "Out of space!\n";
exit(0);
}
S->Next = NULL;
return S; //返回表头指针
}
int IsEmpty( Stack S ) //判断是否栈空
{
return ( S->Next == NULL );
}
ElementType Top( Stack S ) //返回栈顶元素
{
if( S->Next )
return S->Next->Element;
else
return 0;
}
Stack Push( Stack S, ElementType X ) //压栈操作
{
SNode tmp;
tmp = ( SNode ) malloc ( sizeof( struct StackNode ) ); //分配新节点空间
if( tmp == NULL )
{
cout << "Out of space!\n";
return S;
}
tmp->Element = X; //赋值栈顶指针的数值域
tmp->Next = S->Next; //将栈链接到新的栈顶之后
S->Next = tmp; //将元素压入栈顶
return S;
}
ElementType Pop( Stack S ) //出栈操作
{
SNode tmp;
ElementType X;
if( IsEmpty( S ) ) //是否栈空
{
cout << "Empty Stack!\n";
return 0;
}
tmp = S->Next; //临时保存栈顶
S->Next = tmp->Next; //移除栈顶
X = tmp->Element; //临时保存栈顶数值域
free( tmp ); //释放原栈顶指针
return X; //返回原栈顶值
}
Stack MakeEmpty( Stack S ) //清空栈中元素
{
while( !IsEmpty( S ) )
Pop( S );
return S;
}
void Delete( Stack S ) //删除栈
{
S = MakeEmpty( S ); //清空栈
free( S ); //释放头节点空间
}
应用
栈 就是通过简单的Push和Pop操作来实现许多复杂的功能,栈在日常编程或者计算机内存中一般充当着临时存储器的作用,即我们可以将某一个变量暂时不用的变量值存到栈中来使得这个变量可以用作其他用途,而当我们需要用到那个值的时候就可以通过出栈操作来重新获取那个值。最常见的应用就是在计算中缀表达式使用栈来实现运算符之间的优先级比较(即常规计算器的实现,Doge自己也写了一个计算器,整理之后会上传源代码)。