这里写目录标题
栈和队列
一、栈
1 栈的相关概念
(1)定义
限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作)。
(2)逻辑结构
同线性表相同,仍为一对一关系。
(3)存储结构
用顺序栈和链栈存储均可,但仍以顺序栈更常见。
(4)运算规则
只能在栈顶运算,且访问结点时依照后进先出(LIFO)的原则。
2 栈的抽象数据类型定义
ADT Stack{
数据对象:D = { ai | ai ϵ ElemSet, i = 1, 2, …, n, n>= 0 }
数据关系:R1 = { < ai-1,ai > | ai-1,ai ϵ D,i =2,… n }
约定an端为栈顶,a1端为栈底
基本操作:
InitStack( &S )
操作结果:构造一个空栈S。
DestroyStack( &S )
初始条件:栈S已存在。
操作结果:销毁栈S。
ClearStack( &S )
初始条件:栈S已存在。
操作结果:将栈S清空为空栈。
StackEmpty( S )
初始条件: 栈S已存在。
操作结果:若栈S为空栈,则返回TRUE,否则返回FALSE。
StackLength( S )
初始条件: 栈S已存在。
操作结果:返回栈S中数据元素的个数,即栈的长度。
GetTop( S,&e )
初始条件: 栈S已存在且非空。
操作结果:用e返回S中栈顶元素。
Push( &S,e )
初始条件: 栈S已存在。
操作结果:插入元素e为S中新的栈顶元素。
Pop( &S,&e )
初始条件: 栈S已存在且非空。
操作结果:删除S的栈顶元素,并用e返回其值。
}ADT Stack;
3 顺序栈的实现
顺序栈的存储结构:
#define MAXSIZE 100
typedef struct{
SElemType *base; //栈底指针
SElemType *top; //栈顶指针
int stacksize; //栈容量
}SqStack;
顺序栈算法的实现:
Status
InitStack( SqStack &S ){ //构造一个空栈
S.base = new SElemType[MAXSIZE] //或s.base=(SElemType *)malloc(AXSIZE*sizeof(SElemType));
if( !( S.base ) )
exit(OVERFLOW); //存储分配失败
S.top = S.base;
S.stacksize = MAXSIZE ;
return OK;
}
Status
DestroyStack( SqStack &S ){
if( S.base ){
delete S.base; //释放这片空间
S.stacksize = 0 ;
S.base = S.top = NULL;
}
return OK;
}
Status
ClearStack( SqStack &S ){
if( S.base )
S.top = S.base;
return OK;
}
Status
StackEmpty( SqStack S ){
//若栈为空,返回TRUE;否则返回FALSE
if( S.top = S.base )
return TRUE;
else
return FALSE;
}
Status
StackLength( SqStack S ){
return S.top-S.base;
}
Status
GetTop( SqStack S,SElemType &e ){
if( StackEmpty( S ) ) //不为空栈
return ERROR;
e = *( S.top - 1 );
return OK;
}
Status
Push( SqStack &S,SElemType e ){
if( S.top-S.base = S.stacksize ) //栈满
return ERROR;
*S.top++ = e;
return OK;
}
Status
Pop( SqStack &S,SElemType &e ){
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK
if( StackEmpty( S ) )
return ERROR;
e = *--S.top;
return OK;
}
4 链栈的实现
链栈是运算受限的单链表,只能在栈表头部进行操作
链栈的存储结构:
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
LinkStack S;
链栈算法的部分操作:
Status
InitStack( LinkStack &S ){
//构造一个空栈,栈顶指针置为空
S = NULL;
return OK;
}
Status
StackEmpty( LinkStack S ){
if( S = NULL )
return TRUE;
else
return FALSE;
}
Status
GetTop( LinkStack S ){
if( S != NULL )
return S->data;
}
Status
Push( LinkStack &S,SElemType e ){
p = new StackNode; //生成新结点p
p->data = e; //将新结点数据域置为e
p->next = S; //将新结点插入栈顶
S = p; //修改栈顶指针
return OK;
}
Status
Pop( LinkStack &S,SElemType &e ){
if( S == NULL )
return ERROR;
e = S->data;
p = S;
S = S->next;
delete p;
return OK;
}
5 栈与递归的实现
栈还有一个重要应用是在程序设计语言中实现递归。递归函数就是一个直接调用自己或通过一系列的调用语句间接的调用自己的函数。
对于一个递归问题,通常用分治法求解。
分治法:对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解。
分治法求解递归问题算法的一般形式:
void p(参数表){
if (递归结束条件) 可直接求解步骤 ; ------基本项
else p(较小的参数); ------归纳项
}
二、队列
1 队列的相关概念
(1)定义
限定只能在表的一端进行插入,在表的另一端删除运算的线性表(头删尾插)。
(2)逻辑结构
同线性表相同,仍为一对一关系。
(3)存储结构
用顺序队和链队存储均可,但仍以循环顺序队列更常见。
(4)运算规则
只能在队首和队尾运算,且访问结点时依照先进先出(FIFO)的原则。
2 队列的抽象数据类型定义
ADT Queue{
数据对象:D = { ai | ai ϵ ElemSet, i = 1, 2, …, n, n>= 0 }
数据关系:R1 = { < ai-1,ai > | ai-1,ai ϵ D,i =2,… n }
约定an端为队尾,a1端为队头
基本操作:
InitQueue( &Q )
操作结果:构造一个空队列Q。
DestroyQueue( &Q )
初始条件:队列Q已存在。
操作结果:销毁队列Q。
ClearQueue( &Q )
初始条件:队列Q已存在。
操作结果:将队列Q清空为空队列。
QueueEmpty( Q )
初始条件: 队列Q已存在。
操作结果:若队列Q为空队列,则返回TRUE,否则返回FALSE。
QueueLength( Q )
初始条件:队列Q已存在。
操作结果:返回队列Q中数据元素的个数,即队列的长度。
GetHead( Q,&e )
初始条件: 队列Q已存在且非空。
操作结果:用e返回队列Q中队头元素。
EnQueue( &Q,e )
初始条件: 队列Q已存在。
操作结果:插入元素e为队列Q中新的队尾元素。
DeQueue( &Q,&e )
初始条件: 队列Q已存在且非空。
操作结果:删除队列Q的队头元素,并用e返回其值。
}ADT Queue;
3 循环顺序队列的实现
顺序队列的存储结构:
#define MAXQSIZE 100
typedef struct{
QElemType *base; //初始化的动态分配存储空间
int front; //头指针
int rear; //尾指针
}SqQueue;
循环队列的部分算法:
Status
InitQueue( SqQueue &Q ){
//构造一个空队列Q
Q.base = ( QElemType * )malloc( MAXQSIZE *sizeof( QElemType ) );
if( ! Q.base )
exit(OVERFLOW); //分配失败
Q.front = Q.rear = 0;
return OK;
}
Status
QueueLength( SqQueue Q ){
//返回Q的元素个数,即队列的长度
return ( Q.rear - Q.front + MAXQSIZE ) % MAXQSIZE;
}
Status
EnQueue( SqQueue &Q,QElemType e ){
//插入元素e为Q的新的队尾元素
if( ( Q.rear + 1 ) % MAXQSIZE == Q.front ) //队列满
return ERROR;
Q.base[ Q.rear ] = e;
Q.rear = ( Q.rear + 1 ) % MAXQSIZE;
return OK;
}
Status
DeQueue( SqQueue &Q,QElemType &e ){
//若队列不空,则删除Q的队头元素,用e返回其值,并返回OK
//否则返回ERROR
if( Q.front ==Q.rear )
return ERROR;
e = Q.base[ Q.front ];
Q.front = ( Q.front + 1 ) % MAXQSIZE;
return OK;
}
QElemType
GetHead( SqQueue Q ){
if( Q.front !=Q.rear )
return Q.base[ Q.front ];
}
4 链队列的实现(带头结点)
链式队列的存储结构:
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
链式队列的部分算法:
Status
InitQueue( LinkQueue &Q ){
//构造一个空队列Q
Q.front = Q.rear = ( QueuePtr )malloc( sizeof ( QNode ));
if( !Q.front )
exit(OVERFLOW);
Q.front->next = NULL;
return OK;
}
Status
DestroyQueue( LinkQueue &Q ){
//销毁队列,尾指针没有用处,使用可以当做一个保留指针使用
while( Q.front ){
Q.rear = Q.front->next;
free( Q.front );
Q.front = Q.rear;
}
return OK;
}
Status
EnQueue( LinkQueue &Q,QElemType e ){
//插入e为Q的新队尾元素
p = ( QueuePtr )malloc( sizeof ( QNode ));
if( !p )
exit( OVERFLOW );
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status
DeQueue( LinkQueue &Q,QElemType &e ){
if( Q.front ==Q.rear )
return ERROR;
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if( Q.rear ==p ) //当队尾元素被删除后,队尾指针也丢失了,这时需对队尾指针重新赋值(指向头结点)
Q.rear = Q.front;
free( p );
return OK;
}