第 3 章 栈、队列和数组
3.1 栈
3.1.1 栈的定义
栈是只允许在一端进行插入或删除操作的线性表。
栈顶:线性表允许进行插入删除的那一端。
栈底:固定的,不允许进行插入删除的另一端。
空栈:不含任何元素的空表。
3.1.2 栈的基本操作
InitStack(&S):初始化一个空栈S。
StackEmpty(S):判断一个栈是否为空,如栈S为空则返回1,否则返回0。
Push(&S,x):进栈,若栈S未满,则将x加入使之成为新栈顶。
Pop(&S,&x):出栈,若栈S未空,则弹出栈顶元素,并用x返回。
GetTop(S,&x):读栈顶元素,若栈S非空,则用x返回栈顶元素。
DestroyStack(&S):销毁栈,并释放栈S占用的存储空间(“&”表示引用调用)。
3.2 顺序栈
3.2.1 顺序栈的实现
采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top) 指示当前栈顶元素的位置。
栈的顺序存储类型可描述为
#define MaxSize 50 //定义栈中元素的最大个数
typedef int ElemType;
typedef struct{
ElemType data[MaxSize]; //存放栈中元素
int top; //栈顶指针
}SqStack;
3.2.2 顺序栈的基本操作
初始化
void InitStack(SqStack *S)
{
S->top=-1;
}
判栈空
int StackEmpty(SqStack S)
{
if(S.top==-1)
return 1;
else
return 0;
}
进栈
int Push(SqStack *S,ElemType e)
{
if(S->top==MaxSize-1) //栈满,报错
return 0;
S->data[++S->top]=e; //指针先加1,再入栈
return 1;
}
出栈
int Pop(SqStack *S,ElemType *e)
{
if(S->top==-1) //栈空,报错
return 0;
*e=S->data[S->top--]; //先出栈,指针再减1
return 1;
}
读栈顶元素
int GetTop(SqStack *S,ElemType *e)
{
if(S->top==-1) //栈空,报错
return 0;
*e=S->data[S->top]; //e记录栈顶元素
return 1;
}
输出栈
void list(SqStack *S)
{
int i;
for(i=0;i<=S->top;i++)
printf("%4d",S->data[i]);
printf("\n");
}
3.3 链栈
3.3.1 链栈的定义
采用链式存储的栈称为链栈。
链栈存储类型可描述为
typedef int ElemType;
typedef struct LinkStack{
ElemType data;
struct LinkStack *next;
}linkstack;
3.3.2 链栈上的基本操作
初始化操作
linkstack * InitStack(void)
{
linkstack *S;
S=(linkstack *)malloc(sizeof(linkstack));
S->next=NULL;
return S;
}
取栈顶元素操作
int GetTop(linkstack *S,ElemType *e)
{
if(S->next==NULL)
return 0; //若栈空,返回0
*e=S->next->data;
return 1;
}
入栈操作
int Push(linkstack *S,ElemType e)
{
linkstack *p;
p=(linkstack *)malloc(sizeof(linkstack));
if(!p) return 0; //申请空间失败,返回0
p->data=e;
p->next=S->next;
S->next=p;
return 1;
}
出栈操作
int Pop(linkstack *S,ElemType *e)
{
linkstack *q;
if(S->next==NULL)
return 0; //若栈空,返回0
q=S->next;
*e=q->data;
S->next=q->next;
free(q);
return 1;
}
判空操作
int EmptyStack(linkstack *S)
{
if(S->next==NULL)
return 1;
else
return 0;
}
输出操作
void list(linkstack *S)
{
linkstack *p;
p=S->next;
while(p!=NULL)
{
printf("%4d",p->data);
p=p->next;
}
printf("\n");
}
3.4 队列
3.4.1 队列的定义
队列简称队,也是一种操作受限的线性表,只允许在表的一端插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。
队头:允许删除的一端,又称队首。
队尾:允许插入的一端。
3.4.2 队列的基本操作
InitQueue(Q):初始化操作,构造一个空队列Q。
GetLen(Q):求长度操作,返回队列Q的元素个数,即队列的长度。
GetFront(Q,e):取队头元素操作,通过e返回队列Q的队头元素值。
EnQueue(Q,e):入队操作,将值为e的元素插入到队列中,使e称为新的队尾元素。
OutQueue(Q,&e):出队操作,删除队列Q中的队头元素,同时将队头元素通过e带回。原队列中的第 2 个元素称为新的队头元素。
EmptyQueue(Q):判空操作,判断队列Q是否为空,若队列为空,则返回1,否则返回0。
List(Q):从队头到队尾依次输出队列Q中的所有元素。
3.5 顺序队列
3.5.1 顺序队列
顺序队列是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置。
顺序队列类型可描述为
#define MaxSize 50 //定义队列中元素的最大个数
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize]; //存放队列元素
int front; //队头指针
int rear; //队尾指针
}SqQueue;
3.5.2 循环队列
将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。循环队列的类型描述与顺序队列一致。
3.5.3 循环队列上的基本操作
初始化
void InitQueue(SqQueue *Q)
{
Q->rear=Q->front=0; //初始化队首、队尾指针
}
int GetLen(SqQueue *Q)
{
return (Q->rear-Q->front+MaxSize)%MaxSize;
}
判空操作
int IsEmpty(SqQueue Q)
{
if(Q.rear==Q.front)
return 1;
else
return 0;
}
获取队列长度
int GetLen(SqQueue *Q)
{
return (Q->rear-Q->front+MaxSize)%MaxSize;
}
入队操作
int EnQueue(SqQueue *Q,ElemType e)
{
if((Q->rear+1)%MaxSize==Q->front)
return 0;
Q->data[Q->rear]=e;
Q->rear=(Q->rear+1)%MaxSize;
return 1;
}
出队操作
int OutQueue(SqQueue *Q,ElemType *e)
{
if(Q->rear==Q->front)
return 0;
*e=Q->data[Q->front];
Q->front=(Q->front+1)%MaxSize;
return 1;
}
输出操作
void list(SqQueue *Q)
{
int i;
i=Q->front;
while(i!=Q->rear)
{
printf("%4d",Q->data[i]);
i=(i+1)%MaxSize;
}
printf("\n");
}
在主函数中调用上述函数
int main()
{
SqQueue q;
int n;
InitQueue(&q);
n=IsEmpty(q);
printf("%d\n",n);
EnQueue(&q,1);
EnQueue(&q,2);
EnQueue(&q,3);
EnQueue(&q,4);
EnQueue(&q,5);
list(&q);
n=IsEmpty(q);
printf("%d\n",n);
n=GetLen(&q);
printf("%d\n",n);
OutQueue(&q,&n);
list(&q);
printf("%d\n",n);
return 0;
}
3.6 链队列
3.6.1 链队列的定义
队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。
队列的链式存储类型可描述为
typedef int ElemType;
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front;
LinkNode *rear;
}LinkQueue;
3.6.1 链队列上的基本操作
初始化
void InitQueue(LinkQueue *Q)
{
Q->front=Q->rear=(LinkNode *)malloc(sizeof(LinkNode)); //建立头结点
Q->front->next=NULL; //初始为空
}
判空操作
int IsEmpty(LinkQueue *Q)
{
if(Q->front==Q->rear)
return 1;
else
return 0;
}
求队列长度
int GetLen(LinkQueue *Q)
{
int i;
LinkNode *p;
p=Q->front->next;
i=0;
while(p!=NULL)
{
i++;
p=p->next;
}
return i;
}
入队操作
void EnQueue(LinkQueue *Q,ElemType e)
{
LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=e;
s->next=NULL;
Q->rear->next=s;
Q->rear=s;
}
出队操作
int DeQueue(LinkQueue *Q,ElemType *e)
{
LinkNode *p;
if(Q->front==Q->rear)
return 0;
p=Q->front->next;
*e=p->data;
Q->front->next=p->next;
if(Q->rear==p)
Q->rear=Q->front;
free(p);
return 1;
}
取队头元素操作
int GetFront(LinkQueue *Q,ElemType *e)
{
if(Q->front==Q->rear)
return 0;
*e=Q->front->next->data;
return 1;
}
输出操作
void list(LinkQueue *Q)
{
LinkNode *p;
p=Q->front->next;
while(p!=NULL)
{
printf("%4d",p->data);
p=p->next;
}
printf("\n");
}
3.7 双端队列
双端队列是指允许两端都可以进行入队和出队操作的队列,其元素的逻辑结构仍是线性结构。将队列的两端分别称为前端和后端,两端都可以入队和出队。
输入受限的双端队列:允许在其一端进行插入和删除,但在另一端只允许删除的双端队列称为输入受限的双端队列。
输出受限的双端队列:允许在其一端进行插入和删除,但在另一端只允许插入的双端队列称为输出受限的双端队列。