数据结构--栈与队列

栈的定义

栈是限定仅在表尾进行插入和删除操作的线性表。

允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。

栈的特点:后进先出的线性表,简称LIFO结构,即栈具有线性关系,即前驱后继关系。

栈的抽象数据类型

栈(stack)
Data //同线性表元素具有相同的类型,相邻元素具有前驱后继关系
InitStack(*S)://初始化栈,建立一个空栈

DestroyStack(*S)://若栈存在,则销毁它

ClearStack(*S)://将栈清空

StackEmpty(S)://若栈为空,返回true,否则返回false

GetTop(S,*e)://若栈存在且非空,用e返回S的栈顶元素

Push(*S,e)://若栈元素存在,插入新元素e到栈S中并称为栈顶元素

Pop(*S,*e)://删除栈S中栈顶元素,并用e返回其值

StackLength(S)://返回栈S的元素个数

栈的顺序存储结构

栈的结构定义

typedef int SElemType;
//顺序栈的结构
typedef struct
{
   SElemType data[maxsize];
   int top;    //指向栈顶指针
}SqStack;

进栈

Status Push(SqStack* S,SElemType e)
{
   if(S->top==maxsize-1)  //判断栈满
   {
       return error;
   }
   S->top++;              //栈顶加一
   S-data[S->top]=e;      //将新插入元素赋值给栈顶空间
   return ok;
}

出栈

//若栈不为空,则删除栈顶元素,用e返回其值,并返回ok,否则error;

Status Pop(Sqstack* S,SElemType* e)
{
    if(S->top==-1)
    {    
        return error;
    }
    *e=S->data[S->top];//将要删除的元素赋值给e返回
    S->top--;  //栈顶指针减一
    return ok;
}

两栈共享空间

通过用两个数组来储存两个栈,让一个栈的栈底为数组的始端,下标为0;另一个栈的栈底为数组的末端,下标为数组长度减一;

基本思路:元素从数组的两端向中间靠拢,top1和top2为两栈的栈顶指针,所以只要他们俩不相遇,则两栈就可以一直使用;主要针对具有相同数据类型的两个栈使用,

两栈共享空间结构定义
typedef struct
{
    SElemType data[maxsize];
    int top1;  //栈1栈顶指针
    int top2;  //栈2栈顶指针
}SqDoubleStack;
两栈共享空间的插入代码

原理:除了要插入元素值参数外,还需要有一个判断是栈1还是栈2的栈号参数stackNmber

//插入元素e为新的栈顶元素

Status Push(SqDoubleStack* S,SElemType e,int stackNumber)
{
    if(S->top1+1==S->top2)  //栈满,不可以插入元素了;
         return error;
    if(stackNumber==1)
         S->data[++S->top1]=e;  //若是栈1,则先top1+1给数组元素赋值
    else if(stackNumber==2)
         S->data[--S->top2];    //若是栈2,则先top2-1后给数组元素赋值
    return ok;
}

     
两栈共享空间出栈代码

原理:参数就只是判断栈1栈2的参数stackNumber,

//若栈不为空,则删除S的栈顶元素,用e返回其值,并返回ok,否则返回error
Status Pop(SqDoubleStack* S,SElemType* e,int stackNumber)//因为e要返回值,所以需要取地址它
{
    if(stackNumber==1)
    {
        if(S->top1==-1)   //说明栈1已经是空栈,溢出
            return error;
        *e=S->data[S->top1--];//将栈1的栈顶元素出栈
    }
    else if(stackNumber==2)  //说明栈2已经出栈,溢出
    {
        if(S->top2==maxsize)
            return error;
        *e=S->data[S->top2++];  //将栈2的栈顶元素出栈
    }
    return ok;
}

栈的链式存储结构

思路:将栈顶放在单链表的头部,所以对于单链表常用的头节点,而链栈不需要头节点;

对于空栈来说,链表定义是头指针指向空,那么链表的空其实就是top=NULL的时候;

链栈的结构定义代码

typedef struct StackNode
{
    SElemType data;
    struct StackNode* next;
}StackNode,*LinkStackPtr;


typedef struct
{
    LinkStackPtr top;
    int count;
}LinkStack;

进栈操作

//插入元素e为新的栈顶元素

Status  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;
}

出栈操作

思路:假设变量p用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可;

//若栈不为空,则删除S的栈顶元素,用e返回其值,并返回ok,否则返回error

Status Pop(LinkStack* S,SElemtype* e)
{
    LinkStackPtr p;  //定义 一个变量p
    if(StackEmpty(*S))
    { 
        return error;
    }
    *e=S->top->data;   
    p=S->top;          //将栈顶结点赋值给p,
    S->top=S->top->next;//使得栈顶指针下移一位,指向后一结点
    free(p);           //释放结点p
    S->count--;
    return ok;
}

队列的定义

队列是一种先进先出的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为对头。

队列的抽象数据类型:

同线性表,元素具有想同的类型,相邻元素具有前驱和后继关系

InitQueue(*Q)://初始化操作,建立一个空队列Q;
DestroyQueue(*Q)://若队列Q存在,则销毁它;
ClearQueue(*Q)://将队列Q清空
QueueEmpty(Q)://若队列Q为空,返回true,否则返回false
GetHead(Q,*e)://若队列Q存在且非空,用e返回队列Q的对头元素
EnQueue(*Q,e)://若队列Q存在,插入新元素,并用e返回其值
DeQueue(*Q,*e)://删除队列Q中对头元素,并用e返回其值
QueueLength(Q)://返回队列Q的元素个数

循环队列

定义:把像队列头尾相连的顺序存储结构称为循环队列;

队空的条件是 Q->front == Q->rear 。若入队元素的速度快于出队元素的速度,则队尾指针很快就会赶上队首指针,如图( d1 )所示,此时可以看出队满时也有 Q ->front == Q -> rear 。
为了区分队空还是队满的情况,有三种处理方式:
(1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是种较为普遍的做法,约定以“队头指针在队尾指针的下一位置作为队满的标志”,如图 ( d2 )所示。

队满条件: (Q->rear + 1)%Maxsize == Q->front
队空条件仍: Q->front == Q->rear
队列中元素的个数: (Q->rear - Q ->front + Maxsize)% Maxsize
(2)类型中增设表示元素个数的数据成员。这样,队空的条件为 Q->size == O ;队满的条件为 Q->size == Maxsize 。这两种情况都有 Q->front == Q->rear
(3)类型中增设tag 数据成员,以区分是队满还是队空。tag 等于0时,若因删除导致 Q->front == Q->rear ,则为队空;tag 等于 1 时,若因插入导致 Q ->front == Q->rear ,则为队满。

循环队列的顺序存储结构代码:

typedef int QElemType;
typedef struct
{
    QElemType data[maxsize];
    int front;  //头指针
    int rear;   //尾指针,若队列不为空,指向队列尾元素的下一个位置
}SqQueue;

循环队列的初始化代码:

//初始化一个空队列Q
Status InitQueue((SqQueue *Q)
{
    Q->front=0;
    Q->rear=0;
    return ok;
}

循环队列求队列长度的代码:

//返回Q的元素个数,也就是队列当前长度
int QueueLength(SqQueue Q)
{
    return (Q.rear-Q.front+maxsize)%maxsize;
}

循环队列的入队列操作代码:

//若队列未满,则插入元素e为Q新的队尾元素
Status EnQueue(SqQueue* Q,QElemType e)
{
    if((Q->rear+1)%maxsize==Q->front)//队列满的判断
    {
        return error;
    }
    Q->data[Q-rear]=e;    //将元素e赋值给对尾
    Q->rear=(Q->rear+1)%maxsize;//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和real都指向头结点。

链队列的结构:

typedef int QElemType;
 typedef struct QNode  //结点结构
{
    QElemType data;
    struct QNode* next;
}QNode,*QueuePtr;

typedef struct//队列的链表结构
{
    QueuePtr front,rear;//队头,队尾指针
}LinkQueue;

队列的链式存储结构--入队操作

//插入元素e为Q的新的队尾元素
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; //把拥有元素e的新节点s赋值给原队尾结点的后继
    Q->rear=s;       //把当前的s设置为队尾结点,rear指向s
    return ok;
}

出队

出队操作时,就是头节点的后继结点出队,将头节点的后继改为它后面的结点,若链表除头节点外只剩下元素,则需要将rear指向头节点

//若队列不为空,删除Q的队头元素,用e返回其值,并返回ok,否则返回error
Status DeQueue(LinkQueue* Q,QElemType* e)
{
    QueuePtr p;
    if(Q->front==Q->rear)
        return error;
    p=Q->front->next;//将删除的队头结点暂存给p
    *e=p->data;      //将要删除的队头结点的值赋值给e
    Q->front->next=P->next;//将原队头结点的后继p->next赋值给头节点后继
    if(Q->rear==p)     //若队头就是队尾,则删除后将rear指向头节点
        Q->rear=Q->front;
    free(p);
    return ok;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值