学习笔记:大话数据结构-栈

大话数据结构-栈

栈的定义

stack是限定仅在表尾进行插入和删除操作的线性表,允许插入和删除的一端称为栈顶,另一端称为栈底

  • 栈又称为后进先出的线性表,简称LIFO结构
  • 栈的插入操作,为压栈、入栈;栈的删除操作,为出栈
typedef int SElemType; /*SElemType类型根据实际情况而定,这里假设为int*/
typedef struct
{
    
    
    SElemType data[MAXSIZE];
    int top; /*空栈top=-1,栈满etc*/
}SqStack;

栈的顺序存储结构

/*进栈操作 插入元素e为新的栈顶元素*/
Status Push(SqStack *S,SElemType e)
{
    
    
    if(S->top == MAXSIZE -1) /*栈满*/
    {
    
    
        return ERROR;
    }
    S->top++; /*栈顶指针增加一*/
    S->data[S->top] = e; /*将新插入元素赋值给栈顶空间*/
    return OK;
}
/*出栈操作:若栈不空,则删除S的栈顶元素,用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;
}

两栈共享空间——使用数组来存储两个栈

  1. 相同类型的栈的设计技巧
  2. top1+1==top2(栈满);栈1为空栈,top2=0,栈2满;栈2为空栈,栈1的top1为n-1,栈1满。
/*两栈共享空间结构*/
typedef struct
{
    
    
    SElemType data[MAXSIZE];
    int top1; /*栈1栈顶指针*/
    int top2; /*栈2栈顶指针*/
}SqDoubleStack;

Status Push(SqDoubleStack *S,SElemType e, int stacklNumber)
{
    
    
    if(S->top1+1==S->top2) /*栈满*/
        return ERROR;
/* ****自增、自减
* ++i/--i, i自增/减1后再参与其他的运算;i++/i--,i参与运算后,i的值域再自增/减1*/
    if(stacklNumber==1)  /*若栈1有元素进栈*/
        S->data[++S->top1] = e; /*若栈1给top1+1后给数组元素赋值*/
    else if (stacklNumber ==2)/*若栈2有元素进栈*/
        S->data[--S->top2]=e; /*若栈2给top2-1后给数组元素赋值*/
    return OK;
}
/*若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR*/
Status Pop(SqDoubleStack *S,SElemType *e, int stackNumber)
{
    
    
    if (stackNumber == 1) {
    
    
        if (S->top1 == -1)  /*空栈,溢出*/
            return ERROR;
        *e = S->data[S->top1--]; /*栈1的栈顶元素出栈*/
    }
    if (stackNumber == 2)
    {
    
    
        if(S->top2==MAXSIZE)  /*空栈,溢出*/
            return ERROR;
        *e = S->data[S->top2++]; /*栈2的栈顶元素出栈*/
//        data[s--]等价于data[s];s--;
//        data[++s]等价于++s;data[s];
//        data[++s],++s入栈;先取出data[s],s--会删除栈顶的元素
    }
}

栈的链式存储结构以及实现

  1. 单链表有头指针,而栈顶指针也是必须的,因此把栈顶放在单链表的头部(头结点->栈顶)
  2. 链栈基本不存在栈满的情况下;对于空栈来说,链表原定义头指针指向空,那么链栈的空为top = NULL
typedef struct StackNode
{
    
    
    ElemType data;
    struct StackNode *next;
}StackNode, *LinkStackPtr;

typedef struct LinkStack
{
    
    
    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*/
#include <stdbool.h>
bool StackEmpty(LinkStack S)
{
    
    
    if(S.top ==NULL) /*如果是空栈,则top为*/
        return TRUE;
}
Status Pop(LinkStack *S,SElemType *e)
{
    
    
    LinkStackPtr p;
    if (StackEmpty(*S))
        return ERROR;
    *e = S->top->data;
    p = S->top;
    S->top = S->top->next;
    free(p);
    S->count--;
    return OK;
}

栈的应用

  1. 递归,把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称作递归函数,注意定义不满足调用时退出的条件命令
    a. 在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都会被压入栈中。
    b. 在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,恢复调用状态
  2. 四则表达式求值
    a. 有括号的表达式从左到右,栈从空到有元素,最后再因全部匹配成功后成为空栈
    b. 后缀表达式和中缀表达式(标准四则运算表达式)

队列

在可以确定队列长度最大值的情况下,建议使用循环队列;当无法预估队列的长度,则用链队列

  1. 只允许在一端进行插入操作,而在另一端进行删除操作的线性表->先进先出FIFO
  2. 基于线性表的顺序存储结构得到队列,出队列时一定要全部移动, 不限制队列的元素必须存储在数组的前n个单元则出队性能大幅增加
  3. 为了避免当只有一个元素时,队头和队尾重合引起麻烦,使用两个指针front(指向队头元素),rear(指向队尾元素)
    3.1. 问题一: front(指向队头元素),rear(指向队尾元素),但是会导致队尾没位置但是队首有位置的情况
    ->使用队列首尾相接的顺序存储结构,记为循环队列
    3.2. 问题二:判断队列是否为空,我们使用front ==rear,但是队列满时也满足该条件
    ->方法一:设置标志变量,当flag =0,队列为空;当flag = 1,队列满
    ->方法二:当队列满,保留一个元素空间
    a.队列满的条件:(rear+1)%QueueSize == front(rear可能在front右边,也可能在front左侧)
    b.队列长度:(rear-front+QueueSize)

循环队列的顺序存储结构

typedef int QElemType; //视情况而定
typedef struct
{
    
    
    QElemType data[MAXSIZE];
    int front; /*头指针*/
    int rear; /*尾指针,若队列不空,指向队列元素的下一个位置*/
}SqQueue;

/*初始化一个空队列*/
Status InitQueue(SqQueue *Q)
{
    
    
    Q->front= 0;
    Q->rear=0;
    return OK;
}
/*返回Q的元素个数,也就是队列的当前长度*/
int QueueLength(SqQueue Q)
{
    
    
    return (Q.rear - Q.front+MAXSIZE)%MAXSIZE;
}
/*循环队列的入队列操作代码*/
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指针向后移一个位置,在末尾则转到数组头部
}

/*循环队列的出队列操作代码*/
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指针向后移一位置,若到最后转到数组头部
}

链队列

线性表的单链表,尾进头出, 队列的链式存储结构以及实现

typedef int QElemType;
typedef struct QNode
{
    
    
    QElemType data;
    struct QNode *next;
}QNode,*QueuePtr;

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

/*插入元素e为Q的新的队尾元素*/
#include <math.h>
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; //把拥有yuansue新结点s赋值为原队尾结点的后继
    Q->rear=s; //把当前的s设置为队尾结点,rear指向s
    return OK;
}
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; //将原队头结点后继赋值给头结点后继
    if (Q->rear == p)
        Q->rear = Q->front; //若队头是队尾,则删除后将rear指向头结点(只有头结点和一个元素)
    free(p);
    return OK;
}

猜你喜欢

转载自blog.csdn.net/weixin_43464554/article/details/113264820