栈(stack):
-
是限定仅在表位进行插入和删除操作的线性表
-
允许插入的一段称为栈顶(top),另一端称为栈底(bottom),不包含任何数据元素的栈称为空栈
-
栈又被称为是后进先出的线性表,简称LIFO结构
-
栈的插入操作叫做进栈 push 也称压栈,入栈
-
栈的删除操作叫做出栈 pop 也称弹栈
-
栈的抽象数据类型
-
ADT 栈 stack Data 同线性表,元素具有相同的类型,相邻元素具有前驱和后继关系 Operation InitStack(*S):初始化操作,建立一个空栈 DestoryStack(*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的元素个数 endADT
typedef int SElemType; typedef struct { SelemType data[MAXSIZE]; int top; }SqStack;
Status Push(SqStack *S,SElemType e) { if(S->top == MAXSIZE-1) { return ERROR; } S->top++; S->data[S->top]; return OK; }
Status Pop(SqStack *S,SElemType *e) { if(S->top == -1) { return ERROR; } *e = s->data[S->top]; S->top--; return OK; }
两栈共享空间(前提是两栈的数据类型相同)
结构代码如下
typedef struct { SElemType data[MAXSIZE]; int top1; int top2; }SqDoubleStack;
push操作
Status Push(SqDoubleStack *S,SElemType e,int stackNumber) { if(S->top1+1 == S->top2) { return ERROR; } if(StackNumber == 1) { S->data[++S->top1]=e; } if(StackNumber == 2) { S->data[--S->top2]=e; } return OK; }
pop操作
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber) { if(stackNumber == 1) { if(S->top == -1) { return ERROR; } *e=S->data[S->top1--]; } else if(stackNumber == 2) { if(S->top2 == MAXSIZE) { return ERROR; } *e=S->data[S->top2++]; } return OK; }
栈的链式存储结构(链栈)
typedef struct StackNode { SElemType data; struct StackNode *next; }StackNode,*LinkStackPtr; typedef struct LinkStack { LinkStackPtr top; int count; }LinkStack;
链栈的push操作
Status Push(LinkStack *S,SElemType e) { LinkStackPtr s =(LinkStackPtr)malloc(sizeof(StackNode)); s->data=e; s->next=S->top; s->top=s; s->count++; return OK; }
链栈的pop出栈操作
Status Pop(LinkStack *S,SElemType *e) { LinkStackPtr p; if(StackEmpty(*S)) { return ERROR; } *e = S->top->data; p=S->top; S->top=S->top->next; free(p); S->count--; return OK; }
若栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些
队列
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
队列是一种先进先出(FIFO),允许插入的一段称为队尾,允许删除的一端称为队头
ADT 队列
Data
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
InitQueue(*Q):初始化操作,建立一个空队列Q
DestoryQueue(*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的元素个数
endADT
指针front指向队头元素
指针rear指向队尾元素的下一个位置
队列的最大尺寸为QueueSize
循环队列
队列的头尾相接的顺序存储结构称为循环队列,rear指针指向队尾时,若再添加元素,rear指针从头开始
队列满的条件是:(rear+1)%QueueSize == front
通用的计算队列长度公式为:(rear-front+QueueSize)%QueueSize
//循环队列的顺序存储结构实现代码
typedef int QElemType;
typedef struct
{
QElemType data[MAXSIZE];
int front; /*头指针*/
int rear; /*尾指针,若队列不空,指向队列尾元素的下一个位置*/
}SqQueue;
//循环队列的初始化
Status InitQueue(SqQueue *Q)
{
Q->front=0;
Q->rear=0;
return OK;
}
//返回Q的元素个数,也就是队列的当前长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
//循环队列的入队列操作代码
Status EnQueue(SqQueue *Q,QElemType e)
{
if((Q->rear+1)%MAXSIZE == Q->front) //队列满的判断
{
return ERROR;
}
Q->data[Q->rear]=e; //rear指针向后移动一个位置
Q->rear = (Q->rear+1)%MAXSIZE; //若到最后则转向数组头部
return OK;
}
//循环队列出队列操作代码
Status DeQueue(SqQueue *Q,QElemType *e)
{
if(Q->front == Q->rear) //队列空的判断
{
return ERROR;
}
*e=Q->data[Q->front]; //将队头元素赋值给e
Q->front=(Q->front+1)%MAXSIZE; /*front指针向后移动一个位置,若到最后则转到数组头部*/
return OK;
}
队列的链式存储结构及实现
队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列
front指针指向头结点
rear指针指向终端结点
空队列时front和rear都指向头结点
链队列的结构
typedef int QElemType;
typedef struct QNode //结点结构
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front,rear; //队头、队尾指针
}LinkQueue;
//入队操作实现代码
Status EnQueue(LinkQueue *Q,QElemType e)
{
QueuePtr s=(QueuePtr)malloc(sizeof(QNode));
if(!s)
{
exit(OVERFLOW);
}
S->data=e;
S->next=NULL;
Q->rear->next=s;
Q->rear=s;
return OK;
}
出队操作
结点的后继结点出队,头结点的后继改为它后面的结点
若链表除头结点外只剩一个元素,则需要将rear指向头结点