第3章 栈和队列
3.1 栈和队列的特点
| 栈 | 队列 | |
|---|---|---|
| 定义 | 限定只能在表的一端进行插入和删除运算的线性表(栈顶操作) | 头删尾插 |
| 逻辑结构 | 与线性表相同,一对一关系 | ~ |
| 存储结构 | 顺序栈/链栈 | 顺序队/栈队 |
| 运算规则 | 采用后进先出(LIFO)原则 | 采用先进先出(FIFO)原则 |
| 实现方式 | 主要是掌握入栈和出栈函数的书写 | 主要是掌握入队和出队操作 |
3.2 栈的表示和操作的实现
ADT Stack{
数据关系:D
数据关系:S
基本操作:
InitStack(&S)//初始化操作,建立一个空栈
DestroyStack(&S)//销毁栈操作
StackEmpty(S);
StackLength;
ClearStack(&S);
GetTop(S,&e)//取栈顶元素
Push(&S,e);
Pop(&S,&e);
}ADT Stack;
3.3 顺序栈的表示和实现
顺序栈的初始化
① 为顺序栈动态分配一个最大容量为MAXSIZE的数组空间,使base指向这段空间的基地址,即栈底。
② 栈顶指针top初始为base,表示栈为空。
③ stacksize置为栈的最大容量MAXSIZE
//顺序栈的初始化
Status InitStack(SqStack& S) {
//分配栈空间,方式一
S.base = (ElemType*)malloc(MaxSize * sizeof(ElemType));
//分配栈空间,方式二
//S.base = new ElemType(MaxSize);
if (S.base) exit(OVERFLOW);//空间分配失败
S.top = S.base;
S.stacksize = MaxSize;
return OK;
}
//顺序栈的存储结构
# define MaxSize 100//顺序栈存储空间的初始容量
typedef struct {
ElemType* base, * top;//栈底指针(指向第一个元素,下标一般为0)和栈尾指针
int stacksize;//栈可用最大容量
}SqStack;
顺序栈的基本操作
//顺序栈是否为空栈
Status StackEmpty(SqStack S) {
//若栈为空,则栈顶等于栈底
if (S.top == S.base) return 1;
return 0;
}
//求顺序栈的长度
int StackLength(SqStack S) {
return S.top - S.base;
}
//取栈顶元素
ElemType GetTop(SqStack S) {
//栈非空
if (S.top != S.base) return *(S.top - 1);
}
//清空顺序栈
Status ClearStack(SqStack &S) {
if (S.base) S.top = S.base;
printf("顺序栈已清空!\n");
return OK;
}
//销毁顺序栈
Status ClearStack(SqStack& S) {
if (S.base)
{
free(S.base);
S.stacksize = 0;
S.top = S.base = NULL;
}
printf("顺序栈已销毁!\n");
return OK;
}
顺序栈的入栈
① 判断是否栈满,若栈满则出错(上溢)
② 元素e压入栈底
③ 栈顶指针+1
//入栈
Status Push(SqStack& S, ElemType e) {
if (S.top - S.base == S.stacksize) return ERROR;//栈满
*S.top = e;//先将元素压栈
S.top++;//栈顶指针再++
//以上两步可以合并为:*S.top++=e;
return OK;
}
顺序栈的出栈
① 判断是否为空栈,若为空栈则出错(下溢)
② 栈顶指针-1
③ 获取栈顶元素e
//出栈
Status Pop(SqStack& S, ElemType& e) {
if (S.top == S.base) return ERROR;//栈空
--S.top;//指针先减
e = *S.top;//再取值
//以上两步可以合并为:e=*--S.top;
return OK;
}
进制转换
//数制转化(十进制转任意进制)
void conversion(SqStack S,int num, int target) {
ElemType e;
//① 将N于目标进制取余数压栈再商目标进制
while (num != 0) {
Push(S, num % target);
num /= target;
}
//② 当栈非空,取出数据
while (!StackEmpty(S))
{
Pop(S, e);
printf("%d\t", e);
}
}
3.4 链栈的表示和实现
链栈的初始化
//链栈的初始化
Status InitStack(LinkStack& S) {
//构造一个空栈,栈顶指针置为空
S = NULL;
return OK;
}
//链栈的存储结构
typedef struct StackNode{
ElemType data;
struct StackNode* next;
}StackNode,*LinkStack;
链栈的基本操作
//链栈判空
Status StackEmpty(LinkStack S) {
if (S) return 0;//非空
return 1;
}
//取栈顶元素
ElemType GetTop(LinkStack S) {
if (S == NULL) return ERROR;
return S->data;
}
//链栈的遍历
void StackPrint(LinkStack S) {
if (S==NULL) return;//递归终止
else {
printf("%d\t", S->data);
StackPrint(S->next);//递归调用
}
}
链栈的入栈
① 为入栈元素 e 分配空间, 用指针 p 指向。
② 将新结点数据域置为e。
③ 将新结点插入栈顶。
④ 修改栈顶指针为 p
//链栈的入栈
Status PushStack(LinkStack& S, ElemType e) {
StackNode* p;
p = new StackNode;//生成新结点p
if (!p) exit(OVERFLOW);//空间分配失败
p->data = e;//将新结点数据域置为e
p->next = S;//将新结点插入到栈顶
S = p;//修改栈顶指针位置
return OK;
}
链栈的出栈
① 判断栈是否为空 , 若空则返回ERROR。
② 将栈顶元素赋给e。
③ 临时保存栈顶元素的空间, 以备释放。
④ 修改栈顶指针, 指向新的栈顶元素。
⑤ 释放原栈顶元素的空间。
//链栈的出栈
Status Pop(LinkStack& S, ElemType& e) {
StackNode* p;
if (S) return ERROR;
e = S->data;//删除之前,先把要删除的元素返回
p = S;//将要删除元素的地址备份一下,以备释放
S = S->next;//指向下一个元素;
delete (p);
return OK;
}
3.5 队列的表示和操作的实现
ADT Stack{
数据关系:D
数据关系:S
基本操作:
InitQueue(&Q);
DestroyQueue(&Q);
ClearQueue(&Q);
QueueLength(Q);
GetHead(Q,&e);
EnQueue(&Q,e);
DeQueue(&Q,&e)
}ADT Stack;
3.6 队列的顺序表示和实现
//队列的顺序表示
#define MAXQSIZE 100//最大数列的长度
#define QElemType int
typedef struct {
QElemType* data;//初始化动态分配空间
int front;//头指针,若队列不空,指向队列头元素
int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
队列的顺序表示存在两种问题:(rear = MAXQSIZE,发生溢出)
- ① 若,front =0,rear=MAXQSIZE时再入队,此时为真溢出。
- ② 若,front≠0,rear=MAXQSIZE时再入队,此时为假溢出。
解决假溢出的方法:
- ① 将队中元素依次向队头方向移动(缺点:浪费时间)
- ② 使用循环列表(需少用一个空间解决队满时的判断)
data[0]接在data[MAXQSIZE-1]之后,若rear+1==M;则令rear=0;
- ① 实现方法:利用取模运算;
- ② 队空:front==rear;
- ③ 队满:(rear+1)%MAXQSIZE==front
- ④ 插入元素:Q.data[s.rear] = e; Q.rear = (rear+1)%MAXQSIZE;
- ⑤ 删除元素:x = Q.data[s.front]; Q.front= (Q.front+1)%MAXQSIZE;
队列的初始化操作
初始化:
- ① 为队列分配 一 个最大容量为 M 心 CSIZE 的数组空间, base 指向数组空间的首地址。
- ② 头指针和尾指针置为零, 表示队列为空。
//循环队列初始化
Status InitQueue(SqQueue& Q) {
Q.data = new ElemType[MAXSIZE];//分配数组空间
if (Q.data == NULL) exit(OVERFLOW);//空间分配失败
Q.front = Q.rear = 0;//头指针和尾指针置为0,队列为空
return OK;
}
//求循环队列的长度
int QueueLength(SqQueue Q) {
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
//取队头的元素
ElemType GetHead(SqQueue Q) {
if (Q.front != Q.rear) return Q.data[Q.front];
return 0;
}
队列的入队操作
① 判断队列是否满,若满则返回ERROR。
② 将新元素插入队尾。
③ 队尾指针加1。
//循环队列入队
Status EnQueue(SqQueue& Q, ElemType e) {
if ((Q.rear + 1) % MAXSIZE == Q.front) return ERROR;//队满
Q.data[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXSIZE;//队尾指针+1
return OK;
}
队列的出队操作
① 判断队列是否为空,若空则返回ERROR。
② 保存队头元素。
③ 队头指针加1。
//循环队列出队
Status DeQueue(SqQueue& Q, ElemType& e) {
if (Q.front == Q.rear) return ERROR;//队空
e = Q.data[Q.front];//保存队头元素
Q.front = (Q.front + 1) % MAXSIZE;//队头指针+1
return OK;
}
3.7 队列的链式表示与实现
链队的初始化操作
//链队的存储结构
typedef struct QNode {
ElemType data;
struct QNode* next;
}QNode,*QueuePtr;
typedef struct {
QueuePtr front;//队头指针
QueuePtr rear;//队尾指针
}LinkQueue;
初始化:
- ① 生成新结点作为头结点, 队头和队尾指针指向此结点。
- ② 头结点的指针域置空。
//链队的初始化
Status InitQueue(LinkQueue& Q) {
//生成新结点作为头结点,队头和队尾指针指向此结点
Q.front = Q.rear = new QNode;
if (!Q.front) exit(OVERFLOW);//分配空间失败
Q.front->next = NULL;
return OK;
}
//链队的销毁操作
Status DestroyQueue(LinkQueue& Q) {
QNode* p;
while (Q.front) {
p = Q.front->next;
free(Q.front);
Q.front = p;
//为了节省指针变量,也可以将所有的指针变量p变为Q.rear
}
return OK;
}
//取队头元素
Status GetHead(LinkQueue Q, ElemType& e) {
if (Q.front == Q.rear) return ERROR;
e = Q.front->next->data;
return OK;
}
链队的入队
① 为入队元素分配结点空间,用指针p指向。
② 将新结点数据域置为e。
③ 将新结点插入到队尾 。
④ 修改队尾指针为p。
//入队
Status EnQueue(LinkQueue& Q, ElemType e) {
QNode* p = new QNode;//为入队元素分配结点空间,用指针p指向
p->data = e;
p->next = NULL;Q.rear->next = p;//将新结点插入到队尾
Q.rear = p;//修改队尾指针
return OK;
}
链队的出队
① 判断队列是否为空,若空则返回ERROR。
② 临时保存队头元素的空间,以备释放。
③ 修改队头指针,指向下 一 个结点。
④ 判断出队元素是否为最后 一 个元素,若是,则将队尾指针重新赋值, 指向头结点。
⑤ 释放原队头元素的空间。
//出队
Status DeQueue(LinkQueue& Q, ElemType& e) {
if (Q.front == Q.rear) return ERROR;//空队列
QNode* p;
p = Q.front->next;
e = p->data;
Q.front->next = p->next;//修改头指针
if (Q.rear == p) Q.rear = Q.front;//最后一个元素被删,队尾指针指向头结点
delete p;
return OK;
}