目录
3.1栈和队列的定义和特点
- 栈和队列是两种常用的、重要的数据结构
- 栈和队列是限定插入和删除只能在表的“端点”进行的线性表
- 栈
- 后进先出(FLFO)
- 应用
- 数据转换/括号匹配/行编辑顺序/迷宫求解/表达式求值/八皇后问题/函数调用/递归调用的实现
- 栈仅在表尾进行插入、删除,表尾为栈顶(Top),表头为栈底(Base)
- 插入元素到栈顶(表尾)的操作,称为入栈 = Push
- 栈顶删除最后一个元素的操作,出栈 = Pop
- 队列
- 先进先出(FIFO)
- 应用
- 脱机打印输出/多用户系统排队/按用户优先级排队,每个优先级一个队列/实时控制系统/网络电文传输
3.2案例引入
- 栈
- 进制转换
- 括号匹配的检验
- 表达式求值
- 队列
- 舞伴问题
3.3栈的实现和操作的实现
- 栈的顺序存储——顺序栈
- 栈的链式存储——链栈
- 到栈底的数据元素,栈底在低地址端
- top指针,指示栈顶元素
- base指针,指示栈底元素
- top通常指示真正的栈顶元素之上的下标地址
- stacksize表示栈可使用的最大容量
- 使用数组作为顺序栈的特点
- 简单,易产生溢出(数据大小固定)
- 上溢:栈已经满,又要压入元素
- 下溢:栈已经空,又要弹出元素
顺序栈的表示
#define MAXSIZE 100
typedef struct{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
顺序栈初始化
Status InitStack(SqStack &S){
S.base = new SElemType[MAXSIZE];
if(!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = MAXSIZE;
return OK;
}
顺序栈是否为空
Status StackEmpty(SqStack S){
if(S.top == S.base)
return OK;
else
return FALSE;
}
顺序表长度
int StackLength(SqStack S){
return S.top - S.base;
}
清空顺序栈
Status ClearStack(SqStack S){
if(S.base) S.top = S.base;
return OK;
}
销毁顺序栈
Status DestoryStack(SqStack &S){
if(!S.base){
delete S.base;
S.stacksize = 0;
S.base = S.top = NULL;
}
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){
if(S.top == S.base)
return ERROR;
e = *--S.top;
return OK;
}
链栈的表示
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
LinkStack S;
链栈的初始化
void InitStack(LinkStack &S){
S = NULL;
return OK;
}
链栈是否为空
Status StackEmpty(LinkStack S){
if(S==NULL) return TRUE;
else return FALSE;
}
链栈的入栈
Status Push(LinkStack &S,SElemType e){
p = new StackNode;
p->data = 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;
}
取栈顶元素
SElemType GetTop(LinkStack S){
if(S!-NULL){
return S->data;
}
}
3.4栈和递归
- 递归定义:
- 若一个对象部分包含它自己,或用它自己给自己定义,称这个对象是递归的
- 若一个过程直接的或间接的调用自己,称这个过程是递归的过程
- 二叉树、广义表等都有用递归
- 递归就是把一个问题化成基本项和归纳项
- 递归优缺点
- 优点:结构清晰、程序易读
- 缺点:时间开销大
- 解决方法
- 用循环结构代替递归
- 自用栈模拟系统的运行时栈
3.5队列的表示和操作的实现
- 插入元素称为入队,删除元素称为出队
- 队列的存储结构为链队和顺序队(循环顺序队)
顺序队表示
- 队列表示
- 代码
#define MAXQSIZE 100
typedef struct{
QElemType *base;
int front;//头指针
int rear;//尾指针
}SqQueue;
- 存在假溢出问题,解决方法
- 使用循环队列
- Base[0]接在base[MAXQSIZE-1]之后,若rear+1==M,则令rear = 0;(利用模运算)
- 插入
- Q.base[Q.rear]=x;
- Q.rear=(Q.rear+1)%MAXSIZE;
- 删除
- X = Q.base[s.front];
- Q.front=(Q.front+1)%MAXSIZE
- 对空
- Front == rear
- 队满
- (Rear+1)%MAXQSIZE==front
- 队列的初始化
- 代码
Status InitQueue(SqQueue &Q){
Q.base = new QElemType[MAXQSIZE];
if(!Q.base) exit(OVERFLOW);
Q.front = Q.rear = 0;
return OK;
}
- 队列长度
- 代码
int QueueLength(SqQueue Q){
return ((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
- 循环队列入队
- 代码
Status EnQueue(SqQueue &Q,QElemType e){
if((Q.rear+1)%MAXSIZE==Q.front) return ERROR;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXQSIZE;
return OK;
}
- 循环队列出队
- 代码
Status DeQueue(SqQueue &Q,QElemType &e){
if(Q.front==Q.rear) return ERROR;
e = Q.base[Q.front];
Q.front = (Q.front+1)%MAXQSIZE;
return OK;
}
- 取队头元素
- 代码
SElemType GetHead(SqQueue Q){
if(Q.front != Q.rear)
return Q.base[Q.front];
}
链队表示
- 无法估计所用队列的长度,就用链队列
- 链队类型定义
- 代码
#define MAXQSIZE 100
typedef struct Onode{
QElemType data;
struct Qnode *next;
}QNode, *QuenePtr;
- 链队初始化
- 代码
Status InitQueue(LinkQueue &Q){
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if(!Q.front) exit(OVERFLOW);
Q.front->next = NULL;
return OK;
}
- 销毁链队列
- 代码
Status DestoryQueue(LinkQueue &Q){
while(Q.front){
p = Q.front->next;
free(Q.front);
Q.front = p;
}
return OK;
}
- 入队
- 代码
Status EnQueue(LinkQueue &Q,QElemType e){
p = (QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOE);
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;
delete p;
return OK;
}