栈和队列
栈又称为后进先出的线性表,简称LIFO结构
栈顶用top表示,当栈存在一个元素时,top=0;当为空栈时top=-1;
栈的顺序储存结构
栈的结构定义
typedef int SElemTyoe;//SElemType根据实际情况而定,这里假设为int
typedef struct
{
SElemType data[MAXSIZE];
int top;//用于栈顶指针
}SqStack;
栈的顺序储存结构-进栈
Status Push(SqStack *s, SElemType e)
{
if(S->top==MAXSIZE-1)//栈满
{
return ERROR;
}
S->top++;//栈顶指针增加1
S->data[S->top]=e;//将新插入的元素赋值给栈顶空间
return OK;
}
栈的顺序储存结构-出栈
Status Pop(SqStack *S,SElemType *e)
{
if(S->top==-1)
return ERROR;
*e=S->data[S->top];//将要删除的栈顶元素赋值给e
S->top--;
return OK;
}
两栈共享空间
数组有两个端点,两个栈有两个栈底,一个栈的栈底为数组的始端,即下标为0处,另一个栈的栈底为数组的末端,即n-1处。两个栈如果增加元素,就是两端点向中间延伸
typedef struct
{
SElemType data[MAXSIZE];
int top1;
int top2;
}SqDoubleStack;
//插入元素,需要一个标志判断是栈1还是栈2
Status Push(SqDoubleStack *S, SElemType e,int stackNumber)
{
if(S->top1+1==S->top2)//栈已满
return ERROR;
if(stackNumber==1)//栈1有元素进栈
S->data[++S->top1]=e;//栈1则先top1+1后给数组元素赋值
else if(stackNumber==2)//栈2有元素进栈
S->data[--S->top2]=e;//栈2先top2-1后给数组元素赋值
return OK;
}
//删除元素
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
{
if(stackNumber==1)
{
if(S->top1==-1)
return ERROR;
*e=S->data[S->top1--];
}
else if(stackNumber ==2)
{
if(S->top==MAXSIZE)
return ERROR;
*e=S->data[S->top2++];
}
return OK;
}
栈的链式储存结构
链栈的空其实就是top=NULL
链栈的结构代码如下
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack
{
LinkStackPtr top;
int count;
}LinkStack;
栈的链式储存结构–进栈操作
Statusn Push(LinkStack *S, SElemType e)
{
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top;
S->top=s;//将新的结点s赋值给栈顶指针
S->count++;
return OK;
}
栈的链式储存结构–出栈操作
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;
}
队列的定义
队列是只允许在一端进行插入操作,在另一端进行删除操作的线性表。是一种先进先出的的线性表,简称FIFO
循环队列的定义
循环队列
队列顺序存储的不足之处就是插入和删除数据时需要移动元素,还容易发生假溢出(数组空间有多余的,但队列队尾已经超出数组最大位置,称为假溢出)
队列有两个指针,front指向队头元素,rear指向队尾元素的下一个位置。
把队列的头尾相接的顺序储存结构称为循环队列。
我们采取当队列为空时就是front==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;
}
返回队列的长度
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;
Q->rear=(Q->rear+1)%MAXSZIE //rear向后移动一位,若到最后则跳转到数组头部
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;//把当前的s设置为队尾结点,rear指向s
return OK;
}
队列的链式储存结构—出队列
Status DeQueue(LinkQueue *e,QElemtype *e)
{
QueuePtr p;
if(Q->front==Q->rear)//队列为空
return ERROR;
p=Q->front->next;//先将欲删除的结点保存下来
*e=p->data;
Q->front->next=p->next;//重新给头结点的后继赋值
if(Q->rear==p)//如果对头是队尾,即删除后便成为空队列此时需要将rear指向头结点
Q->rear=Q->front;
free(p);
return OK;
}
栈和队列总结
栈和队列都可以用线性表的顺序储存结构来实现
对于栈如果两栈的数据类型相同,则可以用数组的两端作为栈底共享空间的方法,最大程度利用数组的空间。
对于队列,循环队列使得队头和队尾可以在数组中循环变化,避免了插入和删除时需要移动数据,时间复杂度由O(n)减小到O(1)