大话数据结构学习笔记 - 栈与队列

大话数据结构学习笔记 - 栈与队列

栈是限定尽在表尾进行插入和删除操作的线性表。 允许插入和删除的一端称为栈顶(top), 另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构

  • 栈是线性表,即具有线性关系,只不过它是一种特殊的线性表,特殊之处在于插入被限制在栈顶, 而删除位置被限制在栈底
  • 栈的插入操作,叫做进栈,也称压栈、入栈。栈的删除操作,叫做出栈,也称弹栈
  • 栈为线性表,故同样具有顺序存储和链式存储

stack_abstract

栈的抽象数据类型

ADT 栈(stack)
Data
    同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
    InitStack(*S);  初始化操作,建立一个空栈 S
    DestroyStack(*S);  若栈存在,则销毁它
    ClearStack(*S);  将栈清空
    StackEmpty(S);   若栈为空,返回 true,否则返回 false
    GetTop(S, *e);  若栈存在且非空,用 e 返回 S 的栈顶元素
    Push(*S, e);  若栈 S 存在,插入新元素 e 到栈 S 中并成为栈顶元素
    Pop(*S, *e);  删除栈 S 中栈顶元素,并用 e 返回其值
    StackLength(S);  返回栈 S 的元素个数
endADT

栈的顺序存储结构

typedef int SElemType;  // 此处 SElemType 类型为int
typedef struct
{
    SElemType data[MAXSIZE];
    int top;  // 用于栈顶指针, 空栈 top = -1
}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;
}

进栈、出栈时间复杂度均为 O(1)

两栈共享空间

数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为栈的顶端,下标为数组长度n - 1处。栈1为空时,top1 == -1。 栈2为空时, top2 == n。栈满时top1 + 1 = top2

// 两栈共享空间
typedef struct
{
    SElemType data[MAXSIZE];
    int top1;  // 栈1 栈顶指针  top1 == -1 时为空
    int top2;  // 栈2 栈顶指针  top2 == MAXSIZE 时为空
}SqDoubleStack;

进栈

/* 插入元素 e 为新的栈顶元素 */
Status Push(SqDoubleStack *S, SElemType e, int stackNumber)
{
    if(S->top1 + 1 == S->top2)  // 判断栈满
        return ERROR;
    if(stackNumber == 1)  // 元素进栈1
        S->data[++S->top1] == e;  // 先更新栈顶指针 top1 + 1, 再赋值
    else if(stackNumer == 2)  // 元素进栈2
        S->data[--S->top2] == e;  // 先更新栈顶指针 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;  /* 栈1 为空栈 */
        *e = S->data[S->top1--];  /* 栈1 栈顶元素出栈 */
    }
    else if(stackNumber == 2)
    {
        if(S->top2 == MAXSIZE)
            return ERROR;  /* 栈2 为空栈 */
        *e = S->data[S->top2++];  /* 栈2 栈顶元素出栈 */
    }
    return OK;
}

两栈共享空间仅适用于相同数据类型的栈,否则问题会变得复杂

栈的链式存储结构

将栈顶指针用作单链表的头指针,即无需使用头结点。空栈即头指针指向空 top = NULL

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

typedef strcut 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;
}

出栈

// 若栈不空,则删除 S 的栈顶元素,用 e 返回其值,并返回 OK。否则返回 ERROR
Status Pop(LinkStack *S, SElemType *e)
{
    LinkStackPtr q;
    if(StackEmpty(*S))
        return ERROR;
    q = S->top;  // 将栈顶指针赋值给 q
    *e = q->data;
    S->top = q->next;  // 使得栈顶指针后移以为,指向下一结点
    free(q);  // 释放结点 q
    S->count--;
    return OK;
}

链栈pushpop操作时间复杂度哦均为O(1)

队列

队列(queue)是只允许在一端进行插入操作,在另一端进行删除操作的线性表, 即先进先出(First In First Out)的线性表,简称FIFO 。允许插入的一端称为队尾,允许删除的一端称为队头

队列的抽象数据类型

ADT 队列 (Queue)
Data
    同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
    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 到队列 Q 中并成为队尾元素
    DeQueue(*Q, *e): 删除队列 Q 中队头元素, 并用 e 返回其值
    QueueLength(Q): 返回队列 Q 的元素个数
endADT

循环队列

假溢出:即对列中,数组开头位置为空,而末尾位置被占用,若再入队,则数组越界,即假溢出

queue_with_sequential_storage

解决办法:使用循环队列,即头尾相接的顺序存储结构
circular_queue

循环队列的顺序存储结构

  • 判断为空: front == rear
  • 判断队满: (rear + 1) % MAXSIZE == front
  • 队列长度: (rear - front + MAXSIZE) % MAXSIZE
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;
}

入队

/* 若队列未满,则插入元素 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;
}

出队

/* 若队列不空,则删除 Q 中队头元素,用 e 返回其值 */
Status DeQueue(SqQueue *Q, QElemType *e)
{
    if(Q->rear == Q->front)  // 判断队空
        return ERROR;
    *e = Q->data[Q->front];  // 将队头元素赋值给 e
    Q->front = (Q->front + 1) % MAXSIZE;  // front 指针向后移一位,若到最后则转到数组头部
}

队列的链式存储结构

队列的链式存储结构,其实就是线性表的单链表,只不过只能尾进头出而已,简称为链队列。 队头指针指向链队列的头结点,队尾指针指向终端节点

link_queue_abstract

空队列时,frontrear都指向头结点

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

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

入队

LinkQueue_EnQueue

/* 插入元素 e 为 Q 的新的队尾元素 */
Status EnQueue(LinkQueue *Q, QElemType e)
{
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if(!s)                   /* 存储分配失败 */
        return ERROR;
    s->data = e;
    s->next = NULL;
    Q->rear->next = s;       /* 把拥有元素 e 新节点 s 赋值给原队尾结点的后继 如上图中 圈1*/
    Q->rear = s;             /* 把当前的 s 设置为队尾节点, rear 指向 s   如上图中 圈2 */
    return OK;
}

出队

注意:若除头结点外,只剩下一个元素,则需将rear指针指向头结点

LinkQueue_DeQueue

/* 若队列不空,删除 Q 的队头元素,用 e 返回其值,并返回 OK, 否则返回 ERROR */
Status DeQueue(LinkQueue *Q, QElemType *e)
{
    if(Q->front == Q->rear)
        return ERROR;
    QueuePtr p = Q->front->next;    // 将欲删除的队头结点暂存给 p, 见上图中圈 1
    *e = p->data;                   // 将欲删除的队头结点的值赋值给 e
    Q->front->next = p->next;       // 将原队头结点后继 p->next 赋值给头结点后继, 见上图中圈 2

    if(Q->rear == p)                // 若对头是队尾,则删除后将 rear 执行头结点 见上图中圈 3
        Q->rear = Q->front;
    free(p);
    return OK;
}

结语

  • 栈(stack): 是限定尽在表尾进行插入和删除操作的线性表
  • 队列(queue): 是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
  • 栈和队列都有顺序存储结构和链式存储结构
  • code

猜你喜欢

转载自blog.csdn.net/u011221820/article/details/80370431