(1)从数据结构角度看,栈和队列是操作受限的线性表,他们的逻辑结构相同。
(2)从抽象数据类型角度看,栈和队列是两种重要的抽象数据类型。
一、栈
(1)栈的逻辑结构:
1、栈的定义
栈:限定仅在表的一端进行插入和删除操作的线性表。
允许插入和删除的一端称为栈顶,另一端称为栈底。
空栈:不含任何数据元素的栈。
插入:入栈、进栈、压栈
删除:出栈、弹栈
栈的操作特性:后进先出
2、栈的抽象数据类型定义
ADTStack
Data
栈中元素具有相同类型及后进先出特性,
相邻元素具有前驱和后继关系
Operation
InitStack
前置条件:栈不存在
输入:无
功能:栈的初始化
输出:无
后置条件:构造一个空栈
DestroyStack
前置条件:栈已存在
输入:无
功能:销毁栈
输出:无
后置条件:释放栈所占用的存储空间
Push
前置条件:栈已存在
输入:元素值x
功能:入栈操作,在栈顶插入一个元素x
输出:如果插入不成功,抛出异常
后置条件:如果插入成功,栈顶增加了一个元素
Pop
前置条件:栈已存在
输入:无
功能:出栈操作,删除栈顶元素
输出:如果删除成功,返回被删元素值,否则,抛出异常
后置条件:如果删除成功,栈减少了一个元素
GetTop
前置条件:栈已存在
输入:无
功能:读取当前的栈顶元素
输出:若栈不空,返回当前的栈顶元素值
后置条件:栈不变
Empty
前置条件:栈已存在
输入:无
功能:判断栈是否为空
输出:如果栈为空,返回1,否则,返回0
后置条件:栈不变
endADT
(2)栈的顺序存储结构及实现
1、栈的顺序存储结构——顺序栈
栈的顺序存储结构称之为顺序栈。
通常把数组中下标为0的一端作为栈底,同时附设指针top指示栈顶元素在数组中的位置。(top=数组元素的下标)
设存储栈元素的数组长度为StackSize,则:
栈空时,栈顶指针top==-1;
栈满时,栈顶指针top==StackSize-1.
入栈时,top=top+1;
出栈时,top=top-1.
2、顺序栈的实现
类的声明
constint StackSize=100;
template<class DataType>
classseqStack
{
public:
seqStack( ) {top=-1;}
~seqStack( ){}
voidPush ( DataType x );
DataTypePop ( );
DataTypeGetTop ( ){if(top!=-1) return data[top];}
intEmpty ( ){top==-1?return 1:return 0;}
private:
DataTypedata[StackSize];
int top;
};
入栈Push
template<class DataType>
voidSeqStack<DataType> ::Push ( DataType x)
{
if (top== StackSize -1) throw “上溢”;
top++;
data[top]= x;
}
出栈Pop
template<class DataType>
DataTypeseqStack<DataType> :: Pop ( )
{
if (top== -1) throw “下溢”;
x =data[top--];
returnx;
}
3、两栈共享空间
两栈共享空间:使用一个数组来存储两个栈,让一个栈的栈底为该数组的始端,另一个栈的栈底为该数组的末端,两个栈从各自的端点向中间延伸。
栈1的底固定在下标为0的一端;
栈2的底固定在下标为StackSize-1的一端。
top1和top2分别为栈1和栈2的栈顶指针;
StackSize为整个数组空间的大小
栈1为空:top 1=-1;
栈2为空:top 2=StackSize;
栈满:top 2=top1 + 1;
入栈:top 1++; top 2--;
出栈:top 1--; top 2++;
类的声明:
constint StackSize=100;
template<class DataType>
classBothStack
{
public:
BothStack(){top 1=-1;top 2= StackSize;}
~BothStack(){}
voidPush(int i, DataType x);
DataTypePop(int i);
DataTypeGetTop(int i);
intEmpty(int i);
private:
DataTypedata[StackSize];
inttop1, top2;
};
入栈Push
template<class DataType>
voidSeqStack<DataType> ::Push (int I, DataType x)
{
if (top1== top 2 - 1) throw “上溢”;
if(i==1)data[++top 1]=x;
if(i==2)data[--top 2]=x;
}
出栈Pop
template<class DataType>
DataTypeseqStack<DataType> :: Pop ( int i)
{
if(i==1)
{
if(top1==-1) throw“下溢”;
returndata[top1--];
}
if(i==2)
{
if(top2==StackSize) throw“下溢”;
returndata[top2++];
}
}
(3)栈的链接存储结构及实现
1、栈的链接存储结构——链栈
栈的链接存储结构称之为链栈。
将链头作为栈顶,方便操作;链栈不需要附设头结点
2、链栈的实现
类的声明
template<class DataType>
classLinkStack
{
public:
LinkStack(){top=NULL;}
~LinkStack();
voidPush(DataType x);
DataTypePop( );
DataTypeGetTop(if(top!=NULL) return top->data; );
intEmpty( );
private:
Node<DataType>*top;
};
入栈Push
template<class DataType>
voidLinkStack<DataType> ::Push(DataType x)
{
s = newNode<DataType>;
s->data= x;
s->next= top;
top = s;
}
出栈Pop
template<class DataType>
DataTypeLinkStack<DataType> ::Pop( )
{
if (top== NULL) throw "下溢";
x =top->data;
p = top;
top =top->next;
deletep;
returnx;
}
(4)顺序栈和链栈的比较
时间性能:相同,都是常数时间O(1)。
空间性能:
顺序栈:有元素个数的限制和空间浪费的问题。
链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满,但是每个元素都需要一个指针域,从而产生了结构性开销。
总之,当栈的使用过程中元素个数变化较大时,用链栈是适宜的,反之,应该采用顺序栈。
二、队列
(1)队列的逻辑结构
定义
队列:只允许在一端进行插入操作,而另一端进行删除操作的线性表。
允许插入(也称入队、进队)的一端称为队尾,允许删除(也称出队)的一端称为队头。
空队列:不含任何数据元素的队列。
队列的操作特性:先进先出
队列的抽象数据类型定义
ADTQueue
Data
队列中元素具有相同类型及先进先出特性,
相邻元素具有前驱和后继关系
Operation
InitQueue
前置条件:队列不存在
输入:无
功能:初始化队列
输出:无
后置条件:创建一个空队列
DestroyQueue
前置条件:队列已存在
输入:无
功能:销毁队列
输出:无
后置条件:释放队列所占用的存储空间
EnQueue
前置条件:队列已存在
输入:元素值x
功能:在队尾插入一个元素
输出:如果插入不成功,抛出异常
后置条件:如果插入成功,队尾增加了一个元素
DeQueue
前置条件:队列已存在
输入:无
功能:删除队头元素
输出:如果删除成功,返回被删元素值
后置条件:如果删除成功,队头减少了一个元素
GetQueue
前置条件:队列已存在
输入:无
功能:读取队头元素
输出:若队列不空,返回队头元素
后置条件:队列不变
Empty
前置条件:队列已存在
输入:无
功能:判断队列是否为空
输出:如果队列为空,返回1,否则,返回0
后置条件:队列不变
endADT
(5)队列的顺序存储结构及实现
1、队列的顺序存储结构——循环队列
设置队头、队尾两个指针
约定:队头指针front指向队头元素的前一个位置,
队尾指针rear指向队尾元素。
假溢出:当元素被插入到数组中下标最大的位置上之后,队列的空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做假溢出
循环队列:将存储队列的数组头尾相接。
队空:rear=front
队满:(rear+1)%QueueSize=front
2、循环队列的实现
类的声明:
constint QueueSize=100;
template<class DataType>
classCirQueue
{
public:
CirQueue(){front=rear=QueueSize-1;}
~CirQueue( ){}
voidEnQueue(DataType x);
DataTypeDeQueue( );
DataTypeGetQueue( );
intEmpty( ){front==rear?return 1:return 0;}
private:
DataTypedata[QueueSize];
intfront, rear;
};
入队EnQueue
template<class DataType>
voidCirQueue<DataType> ::EnQueue(DataType x)
{
if((rear+1) % QueueSize == front) throw "上溢";
rear=(rear+1) % QueueSize;
data[rear]= x;
}
出队DeQueue
template<class DataType>
DataTypeCirQueue<DataType> ::DeQueue( )
{
if (rear== front) throw "下溢";
front=(front + 1) % QueueSize;
return data[front];
}
读队头元素GetQueue
template<class DataType>
DataTypeCirQueue<DataType> ::GetQueue( )
{
if (rear== front) throw "下溢";
i =(front + 1) % QueueSize;
returndata[i];
}
(6)队列的链接存储结构及实现
1、队列的链接存储结构——链队列
队列的链接存储结构称之为链队列。
队头指针即为链表的头指针。
2、链队列的实现
类的声明
template<class DataType>
classLinkQueue
{
public:
LinkQueue();
~LinkQueue();
voidEnQueue(DataType x);
DataTypeDeQueue( );
DataTypeGetQueue( );
intEmpty( ){front==rear?return 1:return 0;}
private:
Node<DataType>*front, *rear;
};
构造函数LinkQueue
template<class DataType>
LinkQueue<DataType>::LinkQueue( )
{
s = newNode<DataType>;
s->next= NULL;
rear =front=s;
}
入队EnQueue
template<class DataType>
voidLinkQueue<DataType> ::EnQueue(DataType x)
{
s = newNode<DataType>;
s->data= x;
s->next= NULL;
rear->next= s;
rear =s;
}
出队DeQueue
template<class DataType>
DataTypeLinkQueue<DataType> ::DeQueue( )
{
if (rear== front) throw "下溢";
p =front->next;
x =p->data;
front->next= p->next;
if(p->next == NULL) rear = front;
deletep;
returnx;
}
(7)循环队列和链队列的比较
时间性能:循环队列和链队列的基本操作都需要常数时间O(1)。
空间性能:
1、循环队列:必须预先确定一个固定的长度,所以有存储元素个数的限制和空间浪费的问题。
2、链队列:没有队列满的问题,只有当内存没有可用空间时才会出现队列满,但是每个元素都需要一个指针域,从而产生了结构性开销。