接下来这一节我们来学习堆栈和队列!
(一)堆栈
能顺序存储运算数,并且在需要的时候倒序输出!这就是堆栈!
堆栈(Stack):具有一定操作约束的线性表。旨在一端(top,栈顶)做插入,删除。
(1)堆栈实行先入后出原则
Push(S,A)
Push(S,B)
Push(S,C)
Pop(s)
Pop(s)
Pop(s)//输出CBA
(2) 栈的顺序结构通常是由一个一维数组和一个记录栈顶元素位置的变量组成的
#define Maxsize//存储数据元素的最大个数
typedef struct SNode*Stack;
struct SNode{
int Date[20];
int top;
};
重要操作:
1)入栈
void Push(Stack PtrS,ElementType item)
{
if(Ptrl->top==Maxsize-1)
{
printf("堆栈满“);
return;
}
else{
Ptrs->Date[++PtrS->Top]=item;//->优先级高于++
return;
}
2)出栈
ElementType Pop(stack ptrs)
{
if(Ptrs->Top==-1)
{
printf("堆栈空”);
return ERROR;
}//ERROR是ElementType的特殊值,标志错误
else
{
return(Ptrs->Date[(Ptrs->Top)--]);
}
3)用一个数组存储俩个堆栈,要求尽量最大化地利用数组空间,使得数组只要有入栈操作就可以成功.
思路:俩个堆栈分别放在数组的俩边,向中间生长,当俩个栈顶的指针相遇的时候,表示都满了.
#define Maxsize 10
struct DStack{
int Date[Maxsize];
int Top1;//堆栈1的栈顶指针
int Top2;//堆栈2的栈顶指针
}s;
堆栈为空的条件
S.Top1=-1;
S.Top2=Maxsize;
void push(struct DStack*PtrS,int item,int Tag)//tag作为俩个堆栈的标志,取值为1和2
{
if(PtrS->Top2-PtrS->Top1==1)
{
printf("堆栈满"); return;
}
if(Tag==1)
PtrS->Date[++(PtrS->Top1)]=item;
else
Ptrs->Date[--(PtrS->Top2)]=item;
}
int pop(ptrs,int tag)
{
if(tag==1)
{
if(ptrs->top1==-1){
printf("堆栈1空");
return;
}
else return Ptrs->Data[Ptrs->top1--];
}
else{
if(ptrs->top2==maxsize)
{ printf("堆栈2空");
return NULL;}
else return Ptrs->Data[Ptrs->top2++];
}
}
(3)堆栈的链式存储结构
栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在 链栈的栈顶实现。
用链表来表示堆栈时候,链表一定在堆栈头上。Top一定在链表头上.
typedef struct SNode*Stack;
struct SNode{
int Date;
struct SNode*Next;
}
Stack CreateStack()
{//构建一个堆栈的头结点,返回指针
Stack S;
S=(Stack)malloc(sizeof(struct SNode));
S->Nest=NULL;
return S;
}
int IsEmpty(Stack S)
{ //判断堆栈S是否为空,若为空返回整数1,否则返回0
return(S->Next==NULL);
}
void Push(ElementType item,Stack S)
{ //将元素item放在堆栈S中
struct SNode *TmpCell;
TmpCell=(struct SNode*)malloc(struct SNode);
TmpCell->Element=item;
TmpCell->Next=S->Next;
S->Next=TmpCell;
}
ElementType Pop(Stack S)
{
//删除并且返回堆栈S的栈顶元素
struct SNode *FirstCell;
ElementType TopElem;
if(isempty(s))
{
printf("堆栈空”);
return NULL;
}
else{
FirstCell=s->Next;
S->Nest=FirstCell->Next;
TopElem=FirstCell->Element;
free(FirstCell);
return TopElem;
}
}
(4)举例: 中缀表达式的运算求值——转化为后缀表达式
2+9/3-5-------2 9 3 / +5 -
1 ) 运算数的相对顺序不变
2) 运算符号的顺序改变(存储等待中的符号,并将等待中的符号和后续的符号进行对比)
遇到左括号,压入堆栈
遇到右括号,就把栈顶的运算符弹出并且输出,直到遇到左括号
遇到运算符:比较运算符和栈顶运算符的优先级(大于栈顶,压栈。小于栈顶,将栈顶运算符号弹出来输出,再比较新的栈顶运算符,直到运算符大于栈顶运算符的优先级为止,然后将该运算符压栈)
输出:2 9 3 输出:2 9 3 / +
存储:+ / 存储:
有括号怎么办:a*(b+c)/d=abc+*d/
(二)队列
队列:具有一定操作约束的线性表
(1)插入和删除操作:只能在一端插入,一端删除
数据插入:入队列(AddQ)
数据删除:出队列(DeleteQ)
先来先服务:先进先出表FIFO
( 2 )顺序存储:一个一维数组和一个记录队列头元素位置的变量front和一个记录队列尾元素位置的变量rear组成
#define MaxSize 10
struct QNode{
int a[MaxSize];
int rear;
int front;
};
typedef struct QNode*queue;
顺环队列的形成,如果前几个元素已经空了删除了,但是rear已经指向队尾,但仍然要加入,因此要形成闭环.当front=rear可以认为队列没有元素.但比如有a个元素,但是有a+1种情况(0个元素,1个元素...a个元素)。导致无法充分判定空和满。
解决方法:(1)使用额外标记:Size或者tag域
(2)有n个数组空间,但是只使用(n-1)个数组空间
入队列:
void Add(Queue PtrQ,int item)
{
if((PtrQ->rear+1)%MaxSize==ptrl->front)//要变成0可以使用求余函数
{
printf("队列满");
return;
}
PtrQ->rear=(PtrQ->rear+1)%MaxSize;
PtrQ->Data[PtrQ->rear]=item;
}
出队列:
int deleteQ(queue PtrQ)
{
if(PtrQ->front==ptrQ->rear)
{
printf("队列空”);
return error;
}
else{
PtrQ->front=(PtrQ->front+1)%MaxSize;
return PtrQ->a[PtrQ->front);
}
}
(3)队列的链式存储结构的实现 :单链表.删除和插入分别在链表的俩个头进行.
前面是front 后面是rear
struct Node{
int Data;
struct Node*Next;
}//链表中的每个节点
struct QNode{//链队列结构
struct Node*rear;
struct Node*front;
};
typedef struct QNode*Queue;
Queue PtrQ;//为包含rear和front的结构体的指针
不带头结点的链式队列出队的操作 :
int DeleteQ(Queue PtrQ)
{
struct Node*FrontCell;
int FrontELem;
if(PtrQ->front==NULL)
{
printf("队列空");
return ERROR;
}
FrontCell=PtrQ->front;
if(PtrQ->front==PtrQ->rear)//若队列中只有一个元素
PtrQ->front=PtrQ->rear=NULL;//删除后队列置为空
else
PtrQ->front=PtrQ->front->Next;
FrontElem=FrontCell->Data;
free(FrontCell);//释放被删除节点的空间
return FrontElem;
}