联系
栈和队列是一类操作受限制的线性表(即限定性线性表),其特殊性在于限制了插入和删除等操作的位置。
在栈中,用户只能在指定的一端插入和删除元素,因此具有后进先出的特性。
在队列中,用户只能在一端插入元素而在另一端删除元素,因此呈现先进先出的特性。
栈
先来补充几个概念:
栈顶: 允许进行插入、删除操作的一端
栈底: 表的另一端
空栈: 没有元素的栈
进栈: 栈的插入操作
出栈: 栈的删除操作
栈分为两种:顺序栈和链栈
顺序栈:
顺序栈存储结构的定义:
#define Stack_Size 50
typedef struct
{
StackElementType elem[Stack_Size]; /*用来存放栈中元素的一维数组*/
int top; /*用来存放栈顶元素的下标*/
}SeqStack;
1.初始化
void InitStack(SeqStack *S)
{
S->top=-1;
}
2.进栈
进栈时,首先判断当前栈是否已满,如果栈已满,还要进栈就会发生上溢。
int Push(SeqStack *S,StackElementType x)
{
if(S->top==Stack_Size-1)
return 0;
S->top++;
S->elem[S->top]=x;
return 1;
}
3.出栈
出栈时,首先判断当前栈是否为空,如果栈空,还要出栈就会发生下溢。
int Pop(SeqStack *S,StackElementType *x)
{
if(S->top==-1)
return 0;
else
{
*x=S->elem[S->top];
S->top--;
return 1;
}
}
4.读栈顶元素
int GetTop(SeqStack *S,StackElementType *x)
{
if(S->top==-1)
return 0;
else
{
*x=S->elem[S->top];
return 1;
}
}
补充: 多栈共享技术
背景:使用顺序栈,因为对栈空间大小难以准确估计,从而产生有的栈溢出,有的栈空间还很空闲的情况。为了解决这一问题,可以让多个栈共享一个足够大的数组空间,通过利用栈的动态特性(即栈底位置不变,栈顶位置动态变化)来使其存储空间相互补充,这就是栈的共享技术。最常用的是两个栈的共享技术,即双端栈。
具体方法:首先申请一个共享的一维数组空间S[M],将两个栈的栈底分别放在一维数组的两端,分别是0,M-1。由于两个栈顶是动态变化的,这样可以形成互补,使得每个栈可用的最大空间与实际使用的需求有关。
双端栈的存储结构定义如下:
#define M 100
typedef struct
{
StackElementType Stack[M]; /*Stack[M]为栈区*/
StackElementType top[2]; /*top[0]和top[1]分别为两个栈顶指示器*/
}DqStack;
链栈:
链栈的优点: 不必预先估计栈的最大容量,只要系统有可用空间,链栈就不会出现溢出。
链栈的存储结构定义:
typedef struct node
{
StackElementType data;
struct node *next;
}LinkStackNode;
1.进栈
int Push(LinkStack top,StackElementType x)
{
LinkStackNode *temp;
temp=(LinkStackNode *)malloc(sizeof(LinkStackNode));
if(temp==NULL) /*申请空间失败*/
return 0;
temp->data=x;
temp->next=top->next;
top->next=temp;
return 1;
}
2.出栈
int Pop(LinkStack top,StackElementType *x)
{
LinkStackNode *temp;
temp=top->next;
if(temp==NULL)
return 0;
top->next=temp->next;
*x=temp->data;
free(temp);
return 1;
}
队列
补充几个概念:
队尾: 允许插入的一端
队头: 允许删除的一端
队列有两种重要形式:循环队列(顺序存储)和链队列(链式存储)
循环队列:
假溢出:在队列的顺序存储结构中,随着部分元素的出队,数组前面会出现一些空单元,由于只能在队尾入队,使得上述空单元无法使用的现象。
循环队列就能很好的解决假溢出现象,使队列空间得到充分利用。
但同时又面临一个新的问题:如何判断队列是空还是满?
有两种方法:损失一个元素空间和增设一个标志量
采用第一种方法时:
判队空:rear == front
判队满:(rear+1) mod MAXSIZE ==front
循环队列的类型定义如下:
#define MAXSIZE 50
typedef struct
{
QueueElementType element[MAXSIZE]; /*队列的元素空间*/
int front; /*头指针指示器*/
int rear; /*尾指针指示器*/
}SeqQueue;
1.初始化
void InitQueue(SeqQueue *Q)
{
Q->front=Q->rear=0;
}
2.入队
int EnterQueue(SeqQueue *Q,QueueElementType x)
{
if((Q->rear+1)%MAXSIZE==Q->front) /*队满*/
return 0;
Q->element[Q->rear]=x;
Q->rear=(Q->rear+1)%MAXSIZE; /*重新设置队尾指针*/
return 1;
}
3.出队
int DeleteQueue(SeqQueue *Q,QueueElementType *x)
{
if(Q->front==Q->rear) /*队列为空*/
return 0;
*x=Q->element[Q->front];
Q->front=(Q->front+1)%MAXSIZE; /*重新设置队头指针*/
return 1;
}
链队列:
链队列中,队头指针front始终指向头结点,队尾指针rear指向最后一个元素。
链队列的定义:
通常将队头指针和队尾指针封装在一个结构体中,并将该结构体类型重新命名为链队列类型
typedef struct Node
{
QueueElementType data;
struct Node *next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode *front;
LinkQueueNode *rear;
}LinkQueue;
1.初始化
int InitQueue(LinkQueue *Q)
{
Q->front=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(Q->front!=NULL)
{
Q->rear=Q->front;
Q->front->next=NULL;
return 1;
}
else
return 0; /*溢出*/
}
2.入队
int EnterQueue(LinkQueue *Q,QueueElementType x)
{
LinkQueueNode *NewNode;
NewNode=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(NewNode!=NULL)
{
NewNode->data=x;
NewNode->next=NULL;
Q->rear->next=NewNode;
Q->rear=NewNode;
return 1;
}
else
return 0; /*溢出*/
}
3.出队
int DeleteQueue(LinkQueue *Q,QueueElementType *x)
{
LinkQueueNode *p;
if(Q->front==Q->rear)
return 0;
p=Q->front->next;
Q->front->next=p->next;
if(Q->rear==p) /*如果队中只有一个元素p,则p出队后成为空队*/
Q->rear=Q->front;
*x=p->data;
free(p);
return 1;
}