数据结构学习笔记(三:栈和队列)

栈的基本概念

1.栈的定义

  • 栈(Stack):只允许在一端进行插入或删除的线性表
  • 栈顶(Top):线性表允许进行插入和删除的那一端。
  • 栈底(Bottom):固定的,不允许进行插入和删除的另一端。
  • 空栈:不含任何元素的空表。
  • 栈的操作特性为后进先出(LIFO)
  • 栈的数学性质:n个不同元素进栈,出栈元素不同排列的个数为\(\frac{1}{n+1}C^n_{2n}\)

2.栈的基本操作

  • InitStack(&S): 初始化一个空栈S。
  • StackEmpty(S): 判断一个栈是否为空。
  • Push(&S, x): 进栈,若S未满,则将x加入使之成为新栈顶。
  • Pop(&S, &x): 出栈,若S非空,则弹出栈顶元素,并用x返回。
  • GetTop(&S, &x): 读栈顶元素,若S非空,则用x返回栈顶元素。
  • DestoryStack(&S): 销毁栈,并释放S占用的存储空间。

栈的顺序存储结构

1.顺序栈

  • 顺序栈利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶元素的位置。

  • 顺序栈的存储类型描述:

    #define MaxSize 50
    typedef struct {
        Elemtype data[MaxSize];
        int top;
    } SqStack;
    
  • 栈顶指针:S.top,初始时设置S.top=-1

  • 栈顶元素:S.data[S.top]

  • 进栈操作:栈未满时,栈顶指针先加1,再赋值给栈顶元素

    bool Push(SqStack &S, ElemType x) {
        if (S.top == MaxSize - 1)
            return false;
        S.data[++S.top] = x;
        return true;
    
  • 出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1

  • 栈空条件:S.top==-1

  • 栈满条件:S.top==MaxSize-1

  • 栈长:S.top+1

2.共享栈

  • 利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
  • top0=-1时,0号栈为空,top1=MaxSize时1号栈为空
  • 仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。
  • 当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减1再赋值;出栈时则刚好相反。

栈的链式存储

  • 链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。

  • 通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。这里规定链栈没有头结点,头指针指向栈顶元素。

  • 链栈的类型描述:

    typedef struct Linknode {
        ELemType data;
        struct Linknode *next;
    } *LiStack;
    

队列的基本概念

1. 队列的定义

  • 队列也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除
  • 队列的操作特性是先进先出(FIFO)
  • 队头(Front):允许删除的一端,又称队首
  • 队尾(Rear):允许插入的一端
  • 空队列:不含任何元素的空表

2. 队列常见的基本操作

  • InitQueue(&Q):
  • QueueEmpty(Q):
  • EnQueue(&Q, x):
  • DeQueue(&Q, &x):
  • GetHead(Q, &x):

队列的顺序存储结构

1. 顺序队列

  • 队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:
    • 队头指针front指向队头元素
    • 队尾指针rear指向队尾元素的下一个位置
  • 顺序队列的类型描述:
    #define MaxSize 50
    typedef struct {
        ElemType data[MaxSize];
        int front, rear; 
    } SqQueue;
    
  • 初始状态(队空条件):Q.front == Q.rear == 0
  • 进队操作:队不满时,先送值到队尾元素,再将队尾指针加1
  • 出队操作:队不为空时,先取队头元素值,再将队头指针加1
  • d中仅有一个元素,此时入队出现“上溢出”,但这种一处并不是真正的溢出,在data数组中依然存在可以存放元素的空位,所以这是一种“假溢出”

2.循环队列

  • 循环队列弥补了顺序队列“假溢出”的缺陷
  • 将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,成为循环队列
  • 当队首指针Q.front = MaxSize - 1后,在前进一个位置就自动到0,这可以利用取余运算(%)实现
    • 初始时:Q.front = Q.rear = 0
    • 队首指针进1:Q.front = (Q.front + 1) % MaxSize
    • 队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize
    • 队列长度:(Q.rear + MaxSize - Q.front) % MaxSize
    • 出入队时:指针都按顺时针方向进1
    • 区分是队空还是队满
        1. 牺牲一个单元来区分,队头指针在队尾指针的下一位置作为队满标志,如图d2
        • 队满条件:(Q.rear + 1) % MaxSize == Q.front
        • 队空条件:Q.front == Q.rear
        • 队列中元素个数:(Q.rear - Q.front + MaxSize) % MaxSize
        1. 类型中增设表示元素个数的数据成员。
        • 队满条件:Q.size == MaxSize
        • 队空条件:Q.size == 0
        1. 类型中增设tag数据成员
        • 队满条件:tag == 1 && Q.front == Q.rear
        • 队空条件:tag == 0 && Q.front == Q.rear

队列的链式存储结构

1. 链队列

  • 实际上是一个同时带有队头指针和队尾指针的单链表

  • 头指针指向队头结点,尾指针指向队尾结点(与顺序队列不同)

  • 链队列的类型描述

    typedef struct {
        ElemType data;
        struct LinkNode *next
    } LinkNode;
    typedef struct {
        LinkNode *front, *rear
    } LinkQueue;
    
  • 队空条件:Q.front == NULL && Q.rear == NULL

  • 出队时,首先判断队列是否为空,若不为空,则取出队头元素,将其从链表中摘除,并让Q.front指向下一个结点(若该节点为最后一个结点,则置Q.front和Q.rear都为NULL)

  • 入队时,建立一个新结点,将新结点插入到链表的尾部,并让Q.rear指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)

  • 通常将链队列设计成一个带头结点的单链表,统一插入和删除

  • 链队列适用于数据元素变动比较大的情形,而且不存在队满和溢出的问题

posted @ 2020-04-18 21:46  枣子今天不吃枣  阅读(235)  评论(0编辑  收藏  举报