栈与队列
栈
1.定义:仅在表尾进行插入与删除操作的线性表
2.原理:允许插入与删除的一端称为栈顶,另一端称为栈底,不含任何元素称为空栈;属于后入先出结构(Last in First out)
抽象数据类型如下:
ADT 栈(stack)
Date
同线性表。元素具有相同的类型,相邻元素间有前驱和后继的关系
Operation
初始化操作,建立一个空栈;
判断栈是否为空
判断栈是否为满
删除并返回栈顶元素
将元素压入栈
3.实现:
(1)顺序存储结构及其实现
栈结构及其定义
#define MAXSIZE <栈能存储的最大元素个数>
typedef struct Snode * Stack;
struct SNode{
Elementtype Date[MAXSIZE];
int top;//栈顶元素位置变量
};
初始化操作,建立一个空栈;
void InitStack(Stack S)
{
S->top=-1;
}
入栈
void Push(Stack S,Elenmenttype e)
{
if(MAxsize-1==S->top){
printf("栈满了");
return ;
}
else{
S->Date[++S->top]=e;
return ;
}
}
出栈
void Pop(Stack S,Elenmenttype *e)
{
if(-1==S->top){
printf("栈空");
return ;
}
else{
*e=S->Date[S->top--};
return ;
}
}
两栈共享存储空间(通过flag判定操作哪一个栈)87```c
typedef DStack *S;
struct DStack{
Elenmenttype Date[MAXSIZE];
int top1;
int top2;
};
//初始化top1=-1;top2=MAXSIZE-1;
入栈
```c
void DPush(DStack S,Elementtype e,int flag)
{
if(S->top2-1==S->top1){
printf("栈满了\n");
return ;
}
else{
if(flag==1)
S->Date[++S->top1]=e;
if(falg==2)
S->Date[--S->top2]=e;
return ;
}
}
出栈
void DPop(DStack S,Elementtype *e,int flag)
{
if(flag==1){
if(S->top1==-1){
printf("栈1空的\n");
return ;
}
*e=S->Date[S->top1--];
}
else if(flag==2){
if(S->top2==MAXSIZE){
printf("栈2空的\n");
return ;
}
*e=S->Date[S->top2++];
}
(2)链式存储结构及其实现
栈结构及其定义
链式结构的堆栈在哪一端进行push pop呢?即栈顶指针应该在链表的哪一头? 答:因为链表存在头指针,而栈存在栈顶指针,故将两者合二为一,故将链表头部作为栈顶
typedef struct StackNode{
Elementtype Date;
struct StackNode *next;//可以用非完全类型定义指针
}StackNode,*LinkStackPtr;
//定义链式堆栈需要栈顶指针,该指针指向一个结点
typedef struct LinkStack{
LinkStackPtr top;
int count;//统计当前堆栈元素个数
}LinkStack;
入栈
void Push(LinkStack *S,Elementtypye e)
{
LinkStackPtr P=(LinkStackPtr)malloc(sizeof(Stacknode));
P->Date=e;
P->Next=S->top;
S->top=P;
S->count++;
return ;
}
出栈
void Pop(LinkStack *S,Elementtypye *e)
{
LinkStackPtr P=S->top;
*e=S->top->Date;
S->top=S->top->Next;
free(P);
S->count--;
return ;
}
4.应用:
多项式的四则计算:
(1)计算机是通过将多项式后缀表达式中数字扔进堆栈中进行运算;
(2)如何将中缀表达式变为后缀表达式? 将多项式中的符号扔进堆栈;
队列
1.定义:只允许一端(队尾)进行插入操作,另一端(队头)进行删除的线性表;属先进先出结构(First in First out)
2.实现
(1)顺序存储实现
由一个队列头位置变量,一个队列尾位置变量,一个一位数组描述
顺序队列容易出现假溢出现象,即队尾溢出,但队头位置空闲,故采用循环队列(队列头尾相接)!
此时如何判定满?(此时空置一个元素)
(rear+1)%MAXSIZE==front
如何判定空?
rear==front
如何确定当前队列长度?
front<rear时:rear-front
front>rear时:rear+MAXSIZE-front
故总结起来就是
(MAXSIZE-front + rear)%MAXSIZE
顺序存储结构实现形式
typedef struct Qnode*Queue;
struct Qnode
{
Elementtype Date[MAXSIZE];
int rear;
int front;
};
//初始化队列
void InitQueue(Queue Q)
{
Q->front=0;
Q->rear=0;
return ;
}
//插入
void AddQueue(Queue Q,Elenmenttype e)
{
if((Q->rear+1)%MAXSIZE==Q->front){
printf("队列满了");
return ;
}
Q->Date[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
return ;
}
//队头删除
void AddQueue(Queue Q,Elementtype *e)
{
if(Q->rear==Q->front){
printf("队空");
return ;
}
*e=Q->Date[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return ;
}
(2)链式存储实现
链式存储结构的队列其实就是单链表,只不过有一点特殊之处是:不同于普通单链表可以在任意位置插入与删除(位置合法)链式存储结构的队列只能尾进头出。
总的来说,如果可以确定队列的最大值,建议用循环队列,如果不能预估队列的长度,则用链队列。
typedef struct Qnode{
Qelementtype date;
struct Qnode *next;
}QNode,*QueuePtr;
typedef struct Queue{
QueuePtr front,rear;
}LinkQueue;
入队列
void EnQueue(LinkQueue *Q,Qelementtype e)
{
QueuePtr s=(QueuePtr)malloc(sizeof(Qnode));
if(!s)//分配内存失败
exit(OVERFLOW);
s->data=e;
s->next=NULL;
Q->rear->next=s;
Q->rear=s;
}
出队列
void DeQueue(LinkQueue *Q,Qelementtype *e)//该队列有头结点
{
QueuePtr p;
if(Q->front==Q->rear)
printf("空队列");
p=Q->front->next;//将与删除的队列的第一个节点赋给p
*e=p->data;
Q->front->next=p->next;
free(p);
}