栈是一种仅能在表尾插入或删除的的数据结构。对于栈来说,表尾称为栈顶,表头为栈底,不含元素的表为空栈。
因为栈只能在栈顶这一端进行插入或删除,所以栈为先进后出(FILO)结构。
栈的一般实现为顺序栈或者链栈。
栈的基本操作
栈的初始化、判空、判满、取栈顶元素、在栈顶进行插入和删除。
在栈顶插入元素称为入栈,在栈顶删除元素称为出栈。
顺序栈的定义
#define Stack_Size 500//定义顺序栈大小
struct stack
{
int top;//栈顶指针
datatype elem[Stack_Size];//顺序栈数组
}SeqStack;
top :用来表示栈顶元素的位置;
top==-1表示空栈;
top==NULL表示栈不存在;
top>stacksize元素溢出。
链栈的定义
typedef int elemtype; /设置数据类型/
typedef struct LinkedStackNode
{
elemtype data;
struct LinkedStackNode * next;
} LinkedStackNode, * LinkedStack;
LinkedStack top; /设置头指针/
链栈没有栈满问题,大小可以随时扩充;
插入和删除在栈顶处实行;
链式栈的栈顶在链头;
与单链表存储结构一致,与顺序表逻辑结构一致 。
栈的常见算法实现
push入栈,指针top向上移动
pop出栈,指针top向下移动
栈的初始化
//顺序栈
void InitStack(SeqStack *S)
{ / *构造一个空栈S*/
S->top = -1;
}
//链栈
LinkedStack Init_LinkedStack()
{
LinkedStack top=(LinkedStackNode * )malloc (sizeof( LinkedStackNode));
if(top!=NULL)//申请空间成功
top->next=NULL;//设置栈顶指针为空
return top;
}
栈的判空
/ *顺序栈判栈空*/
int IsEmpty(SeqStack *S)
{ / *判断栈S为空栈时返回值为真,反之为假*/
return(S->top==-1?1:0);
}
//链栈
int LinkedStack_Empty(LinkedStack top)
{
if(top->next==NULL)//检查栈顶指针的值
{
return 1;//栈S为空,函数返回1
}
else
{
return 0;
}
}
判断栈满
/ *顺序栈判栈满*/
int IsFull(SeqStack *S)
{ / *判断栈S为满栈时返回值为真,反之为假*/
return(S->top==Stack_Size-1?1:0);
}
取栈顶元素
//顺序栈
int GetTop(SeqStack *S)
{
if(S->top == -1) /*栈为空*/
return 0;
else
{
return S->elem[S->top];
}
}
//链栈
int GetTop_LinkedStack(LinkedStack top)
{
if(top->next)
{
return top->next->data;
}
return 0;
}
入栈
/ *顺序栈入栈*/
int Push(SeqStack *S,StackElementType *x)
{
*/ 将栈S的栈顶元素弹出,放到x所指的存储空间中 */
if(S->top == Stack_Size-1) / *栈满不能入栈*/
return 0;
else
{
S->top++; /* 修改栈顶指针 */
S->elem[S->top] = x;
return 1;
}
}
int Push_LinkedStack(LinkedStack top,elemtype x)
//插入数据元素x为新的栈顶元素
{
LinkedStackNode * node;
node=(LinkedStackNode * )malloc(sizeof(LinkedStackNode));
if(node==NULL)
{
return 0;//申请结点空间失败,插入失败,函数返回0
}
else
{
node->data=x;//设置新结点的数据域
node->next=top->next;//设置新结点的指针城
top->next=node;//设置头结点指针城指向新的栈顶元素
return 1;//插入成功,函数返回1
}
}
出栈
/ *顺序栈出栈*/
int Pop(SeqStack *S,datatype *x)
{
*/ 将栈S的栈顶元素弹出,放到x所指的存储空间中 */
if(S->top == -1) / *栈为空*/
return 0;
else
{
*x = S->elem[S->top];
S->top--; /* 修改栈顶指针 */
return 1;
}
}
*/链栈出栈/*
int Pop_LinkedStack(LinkedStack top, elemtype *x)
{ LinkedStackNode * node;
if(top->next==NULL)
{
return 0;
}
else
{
node=top->next;//将原栈顶数据元素弹出并赋给node
*x=node->data;//将原栈顶数据元素的数据赋值给x
top->next=node->next;//top指向链栈中的下一个数据元素
free (node);//释放原栈顶数据元素所占的空间
return 1;
}
}
链栈及顺序栈的对比
两者时间复杂度一样,均为O(1);
对于空间性能,顺序栈需要事先确定一个固定长度,可能会存在空间浪费问题,但它的优势是存取时定位方便,而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制;
如果栈的使用过程中元素变化不可预料,最好使用链栈,反之,如果他的变化在可控范围内,建议使用顺序栈
双栈
两个顺序找stack1,stack2
stack1的栈底为-1,stack2的栈底为MAXSIZE
顺序栈1从左至右依次入栈,top1++
顺序栈2从左至右依次入栈,top2--
栈满条件:top1+1 == top2
双端顺序栈定义
#define MAXSIZE 500
typedef struct
{
datatype stack[MAXSIZE];
int top[2]; /*top[0]为左指针,top[1]为右指针*/
}DqStack;
双端顺序栈初始化
int initDupStack(Dqstack *S)
{
if((S==(DqStack *)malloc(sizeof(dupsqstack))) == NULL)
return 0;
S->top[0] = -1;
S->top[1] = MAXSIZE;
return 1;
}
双端顺序栈进栈操作
/ *双端顺序栈进栈操作。*/
int Push(DqStack *S, datatype x, int i)
{ / *把数据元素x压入i号堆栈*/
if(S->top[0]+1==S->top[1]) / *栈已满*/
return 0;
switch(i)
{
case 0:
S->top[0]++;
S->Stack[S->top[0]]=x;
break; /*top[0]为左指针*/
case 1:
S->top[1]--;
S->Stack[S->top[1]]=x;
break; /*top[1]为右指针*/
default: / *参数错误*/
return 0;
}
return 1;
}
双端顺序栈出栈操作
/ *双端顺序栈出栈操作。*/
int Pop(DqStack *S,datatype *x,int i)
{ /* 从i 号堆栈中弹出栈顶元素并送到x中 */
switch(i)
{
case 0:
if(S->top[0]==-1) return 0;
*x=S->Stack[S->top[0]];
S->top[0]--;
break;
case 1:
if(S->top[1]==MAXSIZE) return(FALSE);
*x=S->Stack[S->top[1]];
S->top[1]++;
break;
default:
return 0;
}
return 1;
}