栈和队列
栈和队列的定义和特点
栈 后进先出-手电筒电池
队列 先进先出-排队
栈和队列是限定插入和删除只能在表的端点进行的线性表
栈的定义和特点
栈(stack)是一个特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表(后进先出)
表尾(an)栈顶(top)
表头(a1)栈底(base)
- 定义:限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作)
- 逻辑结构:一对一关系
- 存储结构:顺序表和链栈存储均可
- 运算规则:后进先出,栈顶运算
队列的定义和特点
队列(queue)是一种先进先出的线性表,在表尾插入,表头删除
- 定义:是一种先进先出的线性表,在表尾插入,表头删除
- 逻辑结构:一对一
- 存储结构:顺序队或链队
- 运算规则:先进先出
栈的表示和操作的实现
栈的抽象数据类型定义
ADT stack{
数据对象:
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
基本操作:
InitStack(&S): 初始化操作,建立一个空栈S。
DestroyStack(&S): 若栈存在,则销毁它。
ClearStack(&S): 将栈清空。栈置空操作
StackEmpty(S): 若栈为空,返回true,否则返回false。
GetTop(S,&e): 若栈存在且非空,用e返回S的栈顶元素。
Push(&S,e): 入栈操作(压栈操作),若栈S存在,插入新元素e到栈S中并成为栈顶元素。
Pop(&S,&e): 出站操作,删除栈S中栈顶元素,并用e返回其值。
StackLength(S): 返回栈S的元素个数。即求栈的长度
}ADT Stack
栈的顺序存储-顺序栈
栈的链式存储-链栈
顺序栈的表现和实现
空栈:base == top是栈空标志
栈满:top-base == stacksize
使用数组作为顺序栈存储方式的特点:简单方便,但是容易溢出
- 上溢:栈满,又要压入元素
- 下溢:栈空,又要弹出元素
上溢是种错误,使问题的处理无法进行;下溢是一种结束条件,即问题处理结束 - 顺序栈存储结构描述
#define MAXSIZE 100
typedef struct{
SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int stacksize;//栈可用最大容量
}SqStack;
- 顺序栈的初始化
Status InitStack(SqStack &S)//构造一个空栈
{
S.base=(SElemType*)malloc(MAXSIZE*sizeof(SqStack));
if(!S.base)
return ERROR;
S.top=S.base;
S.stacksize=MAXSIZE;
return OK;
}
- 顺序栈判断是否为空
Status StackEmpty(SqStack S)
{
if(S.base == S.top)
return TRUE;
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 DestroyStack(SqStack &S)
{
if(S.base)
{
free(S.base);
S.stacksize=0;
S.base=S.top=NULL;
}
return OK;
}
- 顺序栈入栈
#define STACKINCREMENT 100
Status Push(SqStack &S,SElemType e)
{
if(S.top-S.base >= S.stacksize)//若存储空间已满,则扩充
{
S.base=(SElemType*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S.base)//若分配空间失败
{
printf("OVERFLOW");
return ERROR;
}
S.top=S.base+S.stacksize;//修改空间增加后的基地址
S.stacksize += STACKINCREMENT;//修改增加空间后的基地址
}
*S.top++ = e;//e压栈后,top指针再后移一位
return OK;
}
- 顺序栈出栈
Status Pop(SqStack &S,SElemType &e)
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则ERROR
{
if(S.base == S.top)
{
printf("THE STACK IS NULL");
return ERROR;
}
e=*--S.top;
}
链栈的表现和实现
链栈是__运算受限__的单链表,只能在链表头部进行操作
- 链栈的存储结构描述
typedef int SElemType;
typedef struct SNode{
SElemType data;
struct SNode *next;
}SNode,*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)
{
LinkStack p=(LinkStack)malloc(sizeof(SNode));
if(!p)
return ERROR;
p->data=e;
p->next=S;//将新结点插入栈顶
S=p;//修改栈顶指针
return OK;
}
- 链栈的出栈
Status Pop(LinkStack &S,SElemType &e)
{
if(S == NULL)
return ERROR;
LinkStack p=S;//p指针来指向栈顶元素
e=p->data;//传被删数据域的值
S=p->next;//将栈顶指针指向原栈顶元素的下一个元素结点
free(p);
return OK;
}
- 去栈顶元素
SElemType GetTop(LinkStack S)
{
if(S!=NULL)
return S->data;
}
栈与递归
- 例如:递归求n的阶乘
分治法求解递归问题的一般形式
void p(参数表)
{
if(递归结束条件)可直接求解步骤---基本项
else p(较小参数)---归纳项
}
c语言学习过略过。。。
队列的表示和操作的实现
队列的抽象数据类型定义
ADT 队列 (Queue)
{
数据对象
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系 。
基本操作
InitQueue(&Q) :初始化操作,建立一个空队列Q。
DestroyQueue(&Q) :若队列Q存在,则销毁它。
ClearQueue(&Q) :将队列Q清空。
QueueEmpty(Q) :若队列Q为空,返回true,否则返回false。
GetHead(Q,&e) :若队列Q存在且非空,用e返回队列Q的队头元素。
EnQueue(&Q,e) :若队列Q存在,插入新元素e到队列Q中并成为队尾元素 。
DeQueue(&Q,&e) :删除队列Q中队头元素,并用e返回其值。
QueueLength(Q) :返回队列Q的元素个数,即队长
.....队列置空,遍历队列等操作.....
}ADT Queue
顺序队列的表示与操作
- 顺序队列的存储结构描述
#define MAXQSIZE 100
Typedef struct{
QElemType *base;//初始化的动态分配存储空间
int front;//指示队首元素存储单元的位置(队首指针)
int rear;//指示队尾元素的下一存储单元的位置(队尾指针)
//两个表示下标位置,不是真正的指针
}SqQueue;
初始情况:front=rear=0;
入队:base[rear]=x;rear++;
出队:x=base[front];front++;
空队标志:front==rear
真溢出:front=0; rear=MAXQSIZE;
假溢出:front!=0; rear=MAXQSZIE;
- 解决假溢出–引入循环队列
base[0]接在base[MAXQSIZE-1]之后,若rear+1M,则rear=0;
实现方法:利用模(%)运算==
插入元素:
Q.base[Q.rear]=x;
Q.rear=(Q.rear+1) % MAXQSIZE;
删除元素:
x=Q.base[Q.front];
Q.front=(Q.front+1) % MAXQSIZE; - 循环队列解决队满判断方法–少用一个元素空间
空队–front==rear
队满时,因为少用一个元素空间,队尾指针如果再+1,就与队首指针重合
队满:(rear+1) % MAXQSIZE == front; - 循环队列的初始化
Status InitQueue(SqQueue &Q)
{
Q.base=(QElemType*)malloc(MAXQSIZE * sizeof(QElemType));
if(!Q.base)
return ERROR;
Q.front=Q.rear;
return OK;
}
- 循环队列长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
- 循环队列入队
Status EnQueue(SqQueue &Q,QElemType e)
{
if((Q.rear+1)%MAXQSIZE == Q.front)//队满
return ERROR;
Q.base[Q.rear]=e;//新元素加入队尾
Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针+1
return OK;
}
- 循环队列出队
Status DeQueue(SqQueue &Q,QElemType &e)
{
if(Q.rear == Q.front)
return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return OK;
}
- 取队头元素Q.front
SElemType GetHead(SqQueue Q)
{
if(Q.rear!=Q.front)
return Q.base[Q.front];
}
链队列的表示与操作
- 链队列的存储结构
#define MAXQSIZE 100
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;//链队列的结点类型以及指向结点的指针类型
typedef struct
{
QueuePtr front;//队首指针
QueuePtr next;//队尾指针
}LinkQueue;//链队列类型
- 链队列的初始化
Status InitQueue(LinkQueue &Q)
{
Q.front=(QueuePtr)malloc(sizeof(QNode));
if(!Q.front)
return ERROR;
Q.front->next=NULL;
Q.rear=Q.front;
return OK;
}
- 销毁链队列
Status DestoryQueue(LinkQueue &Q)
{
while(Q.front)
{
Q.rear = Q.front->next;//用Q.rear来遍历链队列一个一个往下走销毁
free(Q.front);
Q.front=Q.rear;//执行到最后成NULL
}
return OK;
}
- 链队列入队
Status EnQueue(LinkQueue &Q,QElemType e)
{
QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
if(!p)
return ERROR;
p->data=e;
p->next=Q.rear->next;
Q.rear->next = p;
return OK;
}
- 链队列出队
Status DeQueue(LinkQueue &Q,QElemType &e)
{
if(Q.rear == Q.front)
return ERROR;
QueuePtr p = Q.front->next;
e=p->data;
Q.front->next=p->next;
if(p == Q.rear)//如果被删结点时队尾结点
Q.rear == Q.front;
free(p);
return OK;
}
- 链队列取队头元素
Status GetHead(LinkQueue Q,QElemType &e)
{
if(Q.rear == Q.front)
return ERROR;
e=Q.front->next->data;
return OK;
}