第三章栈和队列
3.1栈
3.1.1栈的逻辑结构
1. 栈:限定仅在表的一端进行插入和删除操作的线性表。
允许插入和删除的一端称为栈顶,另一端称为栈底。
空栈:
不含任何数据元素的栈。
2.栈的操作特性:后进先出
3.注意:栈只是对表插入和删除操作的位置进行了限制,并没有限定插入和删除操作进行的时间。
4.栈的抽象数据类型定义:
ADT Stack
Data
栈中元素具有相同类型及后进先出特性,
相邻元素具有前驱和后继关系
Operation
InitStack
前置条件:栈不存在
输入:无
功能:栈的初始化
输出:无
后置条件:构造一个空栈
DestroyStack
前置条件:栈已存在
输入:无
功能:销毁栈
输出:无
后置条件:释放栈所占用的存储空间
Push
前置条件:栈已存在
输入:元素值x
功能:在栈顶插入一个元素x
输出:如果插入不成功,抛出异常
后置条件:如果插入成功,栈顶增加了一个元素
Pop
前置条件:栈已存在
输入:无
功能:删除栈顶元素
输出:如果删除成功,返回被删元素值,否则,抛出异常
后置条件:如果删除成功,栈减少了一个元素
GetTop
前置条件:栈已存在
输入:无
功能:读取当前的栈顶元素
输出:若栈不空,返回当前的栈顶元素值
后置条件:栈不变
Empty
前置条件:栈已存在
输入:无
功能:判断栈是否为空
输出:如果栈为空,返回1,否则,返回0
后置条件:栈不变
endADT
3.1.2栈的顺序存储结构及实现
1.存储栈元素的数组长度为StackSize,栈空时栈顶指针top=-1:;栈满时栈顶指针top=StackSize-1.入栈时,栈顶指针top加1;出栈时,栈顶指针top减1.
2.顺序栈类的说明:
const int MAX_SIZE=100;
template <class DataType>
class
seqStack
{
public:
seqStack ( ) ;
~seqStack ( );
void Push ( DataType x );
DataType Pop ( );
DataType GetTop ( );
bool Empty ( );
private:
DataType data[MAX_SIZE];
int top;
}
3.顺序栈的入栈算法push
template <class DataType>
void seqStack<DataType> ::Push ( DataType x)
{
if (top == MAX_SIZE-1) throw “溢出”;
top++;
data[top] = x;
}
4.顺序栈的出栈算法Pop:
template <class DataType>
DataType seqStack<DataType> :: Pop ( )
{
if (top == -1) throw “溢出”;
x = data[top--];
return x;
}
3.1.3栈的链接存储结构及实现
1.链栈的类说明:
template <class DataType>
class LinkStack
{
public:
LinkStack
( )
;
~LinkStack
( )
;
void Push
(
DataType x
)
;
DataType Pop
( )
;
DataType GetTop
( )
;
bool Empty
( )
;
private:
Node<DataType> *top;
}
2.链栈的入栈算法push
template <class DataType>
void LinkStack<DataType>::Push
(
DataType x
)
{
s = new Node<DataType>;
s->data = x;
s->next = top;
top = s;
}
3.链栈的出栈算法pop
template <class DataType>
DataType LinkStack<DataType> ::Pop( )
{
if
(
top == NULL
)
throw "下溢";
x = top->data;
p = top;
top = top->next;
delete p;
return x;
}
3.1.4顺序栈和链栈的比较:
时间性能:
相同,都是常数时间O(1)。
空间性能:
Ø
顺序栈:有元素个数的限制和空间浪费的问题。
Ø
链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满,但是每个元素都需要一个指针域,从而产生了结构性开销。
总之,当栈的使用过程中元素
个数变化
较大时,用链栈是适宜的,反之,应该采用顺序栈。
3.2队列
3.2.1队列的定义
1.队列:
只允许在
一端
进行插入操作,而
另一端
进行删除操作的线性表。
允许插入(也称入队、进队)的一端称为队尾,允许删除(也称出队)的一端称为队头。
空队列:
不含任何数据元素的队列。
2.队列的操作特性:先进先出
3.队列的抽象数据类型定义:
ADT Queue
Data
队列中元素具有相同类型及先进先出特性,
相邻元素具有前驱和后继关系
Operation
InitQueue
前置条件:队列不存在
输入:无
功能:初始化队列
输出:无
后置条件:创建一个空队列
DestroyQueue
前置条件:队列已存在
输入:无
功能:销毁队列
输出:无
后置条件:释放队列所占用的存储空间
EnQueue
前置条件:队列已存在
输入:元素值x
功能:在队尾插入一个元素
输出:如果插入不成功,抛出异常
后置条件:如果插入成功,队尾增加了一个元素
DeQueue
前置条件:队列已存在
输入:无
功能:删除队头元素
输出:如果删除成功,返回被删元素值
后置条件:如果删除成功,队头减少了一个元素
GetQueue
前置条件:队列已存在
输入:无
功能:读取队头元素
输出:若队列不空,返回队头元素
后置条件:队列不变
Empty
前置条件:队列已存在
输入:无
功能:判断队列是否为空
输出:如果队列为空,返回1,否则,返回0
后置条件:队列不变
endADT
3.2.2队列的顺序存储结构及实现
1.队头指针front指向队头元素的前一个位置,
队尾指针rear指向队尾元素。
2.假溢出:当元素被插入到数组中下标最大的位置上之后,队列的空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做假溢出。
3.循环队列类的声明:
const int QueueSize=100;
template <class DataType>
class CirQueue
{
public:
CirQueue
( )
;
~ CirQueue
( );
void EnQueue
(
DataType x
)
;
DataType DeQueue
( )
;
DataType GetQueue
( )
;
bool Empty
( );
private:
DataType data[QueueSize];
int front, rear;
};
4.循环队列的实现入队:
template <class DataType>
void CirQueue<DataType> ::EnQueue(DataType x)
{
if ((rear+1) % QueueSize == front) throw "上溢";
rear =(rear+1) % QueueSize;
data[rear] = x;
}
5.循环队列的出队;
template <class DataType>
DataType CirQueue<DataType> ::DeQueue( )
{
if (rear == front) throw "下溢";
front = (front + 1) % QueueSize;
return data[front];
}
6.循环队列的实现读队头元素
template <class DataType>
DataType CirQueue<DataType> ::GetQueue( )
{
if (rear == front) throw "下溢";
i = (front + 1) % QueueSize;
return data[i];
}
DataType CirQueue<DataType> ::GetQueue( )
{
if (rear == front) throw "下溢";
i = (front + 1) % QueueSize;
return data[i];
}
3.2.3队列的链接存储结构及实现
1.链队列类的说明:
template <class DataType>
class LinkQueue
{
public:
LinkQueue( );
~LinkQueue( );
void EnQueue(DataType x);
DataType DeQueue( );
DataType GetQueue( );
bool Empty( );
private:
Node<DataType> *front, *rear;
};
class LinkQueue
{
public:
LinkQueue( );
~LinkQueue( );
void EnQueue(DataType x);
DataType DeQueue( );
DataType GetQueue( );
bool Empty( );
private:
Node<DataType> *front, *rear;
};
2.链队列的构造函数算法:
template <class DataType>
LinkQueue<DataType> ::LinkQueue( )
{
front = new Node<DataType>;
front->next = NULL;
rear = front;
}
LinkQueue<DataType> ::LinkQueue( )
{
front = new Node<DataType>;
front->next = NULL;
rear = front;
}
3.链队列的入队算法:
template <class DataType>
void LinkQueue<DataType> ::EnQueue(DataType x)
{
s = new Node<DataType>;
s->data = x;
s->next = NULL;
rear->next = s;
rear = s;
}
void LinkQueue<DataType> ::EnQueue(DataType x)
{
s = new Node<DataType>;
s->data = x;
s->next = NULL;
rear->next = s;
rear = s;
}
4.链队列的出队算法:
template <class DataType>
DataType LinkQueue<DataType> ::DeQueue( )
{
if (rear == front) throw "下溢";
p = front->next;
x = p->data;
front->next = p->next;
if (p->next == NULL) rear = front;
delete p;
return x;
}
DataType LinkQueue<DataType> ::DeQueue( )
{
if (rear == front) throw "下溢";
p = front->next;
x = p->data;
front->next = p->next;
if (p->next == NULL) rear = front;
delete p;
return x;
}
5.循环队列与链队列的比较:
时间性能:
循环队列和链队列的基本操作都需要常数时间O (1)。
循环队列和链队列的基本操作都需要常数时间O (1)。
空间性能:
循环队列:必须预先确定一个固定的长度,所以有存储元素个数的限制和空间浪费的问题。
链队列:没有队列满的问题,只有当内存没有可用空间时才会出现队列满,但是每个元素都需要一个指针域,从而产生了结构性开销。
循环队列:必须预先确定一个固定的长度,所以有存储元素个数的限制和空间浪费的问题。
链队列:没有队列满的问题,只有当内存没有可用空间时才会出现队列满,但是每个元素都需要一个指针域,从而产生了结构性开销。