栈是一种操作受限的线性表。
栈的逻辑结构:插入和删除等操作只能发生在表的一端的特殊的线性表。
栈的物理结构:可以是顺序栈,也可以是链式栈,此外还有一种特殊的双堆栈。
一、顺序栈
顺序栈很大程度上就是个一维数组,只是规定对栈的操作只能在称为栈顶的一端进行。
顺序栈的类型定义通常如下:
/*顺序栈的类型定义*/
struct SeqStack{
DataType data[MaxSize];
int top;
};
定义的SeqStack类型由一个data[]数组和一个整型变量top构成,top用来表示当前栈顶元素的位置,也可称其为top指针,其值一般为≥-1的整数。
其结构图大致如下:

顺序栈的主要操作实现如下:
1.置空栈;初始化栈
void InitStack(SeqStack *s)
{
s->top=-1; //也可令top=0;
}
2.判断是否栈空
bool StackEmpty(SeqStack *s)
{
return(s->top==-1);
}
3.判断是否栈空
bool StackFull(SeqStack *s)
{
return(s->top==MaxSize-1);
}
4.进栈:在栈顶存入值为x的元素
int Push(SeqStack *s,DataType x)
{
if(StackFull(s)) //判断栈是否满溢
return 0;
s->data[++s->top]=x;
return 1;
}
5.出栈:取栈顶元素
DataType Pop(SeqStack *s)
{
if(StackEmpty(s)) //判断栈是否为空
return 0;
return(s->data[s->top--]);
}
一个完整的程序:
#include <iostream>
using namespace std;
#define MaxSize 100
typedef int DataType;
/*顺序栈的类型定义*/
struct SeqStack{
DataType data[MaxSize];
int top;
};
int main()
{
void InitStack(SeqStack *s);
bool StackEmpty(SeqStack *s);
bool StackFull(SeqStack *s);
int Push(SeqStack *s,DataType x);
DataType Pop(SeqStack *s);
SeqStack p,*s;
s=&p;
InitStack(s); //初始化栈
for(int i=0;i<=10;i++) //将0~10间的数值存入栈中
Push(s,i);
while(!StackEmpty(s)) //当栈为非空时弹出栈顶元素
cout<<Pop(s)<<' ';
cout<<endl;
return 0;
}
/*初始化栈*/
void InitStack(SeqStack *s)
{
s->top=-1; //也可令top=0;
}
/*判断是否栈空*/
bool StackEmpty(SeqStack *s)
{
return(s->top==-1);
}
/*判断是否栈满*/
bool StackFull(SeqStack *s)
{
return(s->top==MaxSize-1);
}
/*在栈顶存入值为x的元素*/
int Push(SeqStack *s,DataType x)
{
if(StackFull(s)) //判断栈是否满溢
return 0;
s->data[++s->top]=x;
return 1;
}
/*退栈;取栈顶元素*/
DataType Pop(SeqStack *s)
{
if(StackEmpty(s)) //判断栈是否为空
return 0;
return(s->data[s->top--]);
}
二、双堆栈
在同一个数组里实现两个堆栈的结构可将之称为双堆栈。为最大可能地利用数组空间,可使两个栈分别从数组的两头开始向中间生长,当两个栈的栈顶指针相遇时(RightTop-LeftTop==1),表示两个栈都满了。
双堆栈的结构图如下:

定义双堆栈的结构类型时,相较于顺序栈,只需多加一个表示第二个栈栈顶的指针即可,可作如下定义:
/*定义双堆栈结构*/
struct DStack
{
ElementType data[MaxSize];
int LeftTop;
int RightTop;
};
在使用双堆栈时,可引入一个整型或布尔型的Tag变量,来只是是对左堆栈或右堆栈进行操作。
双堆栈的主要操作实现如下:
1.置空栈:初始化
void InitStack(DStack *s)
{
s->LeftTop=-1;
s->RightTop=MaxSize;
}
2.入栈:根据Tag值将元素x读入某一个栈的栈顶
void Push(DStack *s,ElementType x,int Tag)
{
if(s->RightTop-s->LeftTop==1)
cout<<"堆栈满"<<endl;
else{
if(Tag==1)
s->data[++(s->LeftTop)]=x;
else
s->data[--(s->RightTop)]=x;
}
}
3.退栈:根据Tag值读出某个栈的元素
ElementType Pop(DStack *s,int Tag)
{
if(Tag==1){
if(s->LeftTop==-1){
cout<<"堆栈1空"<<endl;
return 0;
}
else
return(s->data[(s->LeftTop)--]);
}
else{
if(s->RightTop==MaxSize){
cout<<"堆栈2空"<<endl;
return 0;
}
else
return(s->data[(s->RightTop)++]);
}
}
下面是一个将-10~10间的正整数存入数组左边,负整数存入数组右边,并将正负数交替输出的完整程序:
#include <iostream>
using namespace std;
#define MaxSize 20
typedef int ElementType;
/*定义双堆栈结构*/
struct DStack
{
ElementType data[MaxSize];
int LeftTop;
int RightTop;
};
int main()
{
void InitStack(DStack *s);
void Push(DStack *s,ElementType x,int Tag);
ElementType Pop(DStack *s,int Tag);
DStack *s,Stack;
s=&Stack;
InitStack(s);
for(int i=1;i<11;i++) //将1~10间的整数存在数组的左边
Push(s,i,1);
for(int i=-1;i>-11;i--) //将-10~-1间的整数存在数组的右边
Push(s,i,2);
while(s->LeftTop!=-1 || s->RightTop!=MaxSize)
{ /*实现±1~±10之间的整数交替输出*/
cout<<Pop(s,1)<<' ';
cout<<Pop(s,2)<<' ';
}
cout<<endl;
return 0;
}
/*初始化栈*/
void InitStack(DStack *s)
{
s->LeftTop=-1;
s->RightTop=MaxSize;
}
/*入栈:根据Tag值将元素x写入某个栈的栈顶*/
void Push(DStack *s,ElementType x,int Tag)
{
if(s->RightTop-s->LeftTop==1)
cout<<"堆栈满"<<endl;
else{
if(Tag==1)
s->data[++(s->LeftTop)]=x;
else
s->data[--(s->RightTop)]=x;
}
}
/*退栈:根据Tag值读出某个栈的栈顶元素*/
ElementType Pop(DStack *s,int Tag)
{
if(Tag==1){
if(s->LeftTop==-1){
cout<<"堆栈1空"<<endl;
return 0;
}
else
return(s->data[(s->LeftTop)--]);
}
else{
if(s->RightTop==MaxSize){
cout<<"堆栈2空"<<endl;
return 0;
}
else
return(s->data[(s->RightTop)++]);
}
}
程序运行结果如下:

三、链栈
链栈是栈的链式存储结构,它的运算是受限的单链表,插入和删除操作仅限制在表头位置上进行。像单链表一样,它也可添加一个数据域为空的头结点。
同顺序栈相比,链栈是可扩充的,它没有栈满的问题。
链栈的结构图如下:

链栈的主要操作实现如下:
0.链栈的类型定义
typedef struct Node
{
ElementType data;
Node *next;
}LinkStack;
1.置空栈:初始化链栈
LinkStack *CreateStack()
{
LinkStack *s;
s=new LinkStack; /*s是堆栈的数据域为空的头结点*/
s->next=NULL;
return(s);
}
2.判断栈是否为空
/*判断链栈是否为空,返回值为布尔型*/
bool IsEmpty(LinkStack *s)
{
return(s->next==NULL);
}
3.入栈
/*将元素x压入堆栈s的栈顶结点(紧跟头结点之后的那个结点)中*/
void Push(LinkStack *s,ElementType x)
{
LinkStack *p;
p=new LinkStack;
p->data=x;
p->next=s->next;
s->next=p;
}
4.出栈
/*返回栈顶元素,并释放该元素对应的栈顶结点*/
ElementType Pop(LinkStack *s)
{
LinkStack *top;
ElementType x;
if(IsEmpty(s)){
cout<<"under flow"<<endl;
return 0;
}
else{
top=s->next;
x=top->data;
s->next=top->next;
delete top;
return(x);
}
}
一个完整的程序:
#include <iostream>
using namespace std;
typedef int ElementType;
/*链栈的结点类型定义*/
typedef struct Node
{
ElementType data;
Node *next;
}LinkStack;
int main()
{
LinkStack *CreateStack();
bool IsEmpty(LinkStack *s);
void Push(LinkStack *s,ElementType x);
ElementType Pop(LinkStack *s);
LinkStack *s;
s=CreateStack(); //构建链栈s的头结点
for(int i=1;i<=10;i++) //依次将1~10间的数值作为栈顶元素压入堆栈中
Push(s,i);
while(!IsEmpty(s)) //依次弹出堆栈中的栈顶元素,直到堆栈为空
cout<<Pop(s)<<' ';
return 0;
}
/*构建一个链栈的头结点,返回该结点的指针*/
LinkStack *CreateStack()
{
LinkStack *s;
s=new LinkStack; /*s是堆栈的数据域为空的头结点*/
s->next=NULL;
return(s);
}
/*判断链栈是否为空,返回值为布尔型*/
bool IsEmpty(LinkStack *s)
{
return(s->next==NULL);
}
/*将元素x压入堆栈s的栈顶结点(紧跟头结点之后的那个结点)中*/
void Push(LinkStack *s,ElementType x)
{
LinkStack *p;
p=new LinkStack;
p->data=x;
p->next=s->next;
s->next=p;
}
/*返回栈顶元素,并释放该元素对应的栈顶结点*/
ElementType Pop(LinkStack *s)
{
LinkStack *top;
ElementType x;
if(IsEmpty(s)){
cout<<"under flow"<<endl;
return 0;
}
else{
top=s->next;
x=top->data;
s->next=top->next;
delete top;
return(x);
}
}
本文深入探讨了栈的三种主要存储结构:顺序栈、双堆栈和链栈。详细介绍了每种结构的特点、操作实现及应用场景,通过具体示例帮助读者理解不同栈结构的工作原理。

170

被折叠的 条评论
为什么被折叠?



