栈和队列

栈和队列

栈(stack)

  • 栈是限定仅在表的一端进行插入或删除操作的线性表。
  • 因此,对栈来说,可以进行插入删除操作的一端称为栈顶 (top), 相应地另一端称为栈底 (bottom)。
  • 不含元素的空表称为空栈。
  • 栈又称为后进先出 (Last In First Out, LIFO) 的线性表。
  • 栈就像洗碗叠盘子取盘子一样。

栈的应用

  • 浏览器前进后退
  • 表达式计算
  • 括号匹配检测
  • 汉诺塔问题
  • 迷宫求解问题
  • 进制转换
  • 逆序输出

栈的基本操作

操作 初始条件 操作结果
InitStack(S) 构造一个空栈S
DestroyStack(S) 栈S已存在 栈S被销毁
ClearStack(S) 栈S已存在 将S清为空栈
StackEmpty(S) 栈S已存在 若栈 S 为空栈, 则返回 true, 否则返回 false
StackLength(S) 栈S已存在 返回S的元素个数, 即栈的长度
GetTop(S) 栈S已存在且非空 返回S的栈顶元素, 不修改栈顶指针
Push(S,e) 栈S已存在 插入元素e为新的栈顶元素
Pop(S,e) 栈S已存在且非空 删除S的栈顶元素,并用e返回其值
StackTraverse(S) 栈S已存在且非空 从栈底到栈顶依次对S的每个数据元素进行访问

栈的两种实现

顺序存储结构实现

需要设置栈顶指针,数组末尾作为栈顶方便添加删除操作;

判空条件:

  • 如果栈顶指针指向栈顶元素,栈顶指针 < 0 栈为空
  • 如果栈顶指针指向栈顶元素上一位,栈顶指针 == 0 栈为空

缺点存储空间需要提前设定,动态扩容有一定代价;

链式存储结构实现

需要设置栈顶部指针,用链表表头作为栈顶方便添加删除操作;

不需要头指针,表头就是栈顶,链表为空 == 栈空== 表头为null;

利用栈将递归转为非递归

队列(queue)

  • 队列是一种先进先出(First In First Out,FIFO)的线性表。
  • 它只允许在表的一端进行插入,而在另一端删除元素。这和日常生活中的排队是一致的,最早进入队列的元素最早离开。
  • 不含元素的空表称为空队列。
  • 在队列中,允许插入的一端称为队尾(rear), 允许删除的一端则称为队头(front)。

队列的应用

  • 排队系统

  • LRU cache

队列的基本操作

操作 初始条件 操作结果
InitQueue(Q) 构造一个空队列Q
DestroyQueue(Q) 队列Q已存在 队列Q被销毁
ClearQueue(Q) 队列Q已存在 将Q清为空队列
QueueEmpty(Q) 队列Q已存在 若队列 Q 为空队列, 则返回 true, 否则返回 false
QueueLength(Q) 队列Q已存在 返回Q的元素个数, 即队列的长度
GetHead(Q) 队列Q已存在且非空 返回Q的队头元素
EnQueue(Q,e) 队列Q已存在 插入元素e为新的队尾元素
DeQueue(Q,e) 队列Q已存在且非空 删除Q的队头元素,并用e返回其值
QueueTraverse(Q) 队列Q已存在且非空 从队头到队尾依次对Q的每个数据元素进行访问

队列的两种实现

顺序存储结构实现

顺序存储结构由于数组的特点在队尾进行插入为O(1),但是对头删除元素需要移动元素为O(n);

缺点存储空间需要提前设定,动态扩容有一定代价

循环队列可以用来解决数组的缺陷(出队需要移动大量元素|不移动出现假溢出):

  • 循环队列需要头指针(指向第一个元素)、尾指针(指向最后一个元素下一位)

  • 循环队列无法直接通过头指针==尾指针判断队列为空、已满

    • 用额外标志位(入队加一,出队减一)

      • front == rear 且 flag == 0 时对列为空;
      • front == rear 且 flag != 0 时对列已满;
    • 保留一位元素空间不使用

      • front == rear 时对列为空;
      • (rear+1)%capacity == front 时队列已满;
  • 循环队列计算元素个数:(rear-front+capacity)%capacity == size

  • 出队:front = (front+1)%capacity

  • 入队:rear = (rear+1)%capacity

链式存储结构实现

通常采用单链表实现:

  • 带有头结点

    • 初始化时front = rear = head;
    • 元素q入队:Q. rear->next = p;Q. rear=p;
    • 队列判空:front == rear (== head 可省略)
    • 获取对头元素:判断对列是否为空 非空返回Q. front->next;
    • 元素出队:
      • 判断对列非空
      • p=Q. front->next;
      • Q. front->next=p->next;
      • 如果 Q. rear == p(最后一个元素出队) ; Q. rear = Q. front(队尾指向对头);
  • 不带头结点

    • 初始化时front == rear == null;

    • 元素q入队:

      • 空队列入队:Q. front=p(指向对头);Q. rear=p(指向队尾);
      • 非空队列入队:Q. rear->next=p(连接新元素);Q. rear=p(rear指向队尾);
    • 队列判空:front == rear == null

    • 获取对头元素:判断对列是否为空 非空返回Q. front;

    • 元素出队:

      • 判断对列非空
      • p = Q. front;
      • Q. front = p->next;
      • 如果 Q. rear == p(最后一个元素出队) ; Q. rear = Q. front(队尾指向对头);
  • 总结:链表实现队列带不带头结点都可以,区别就是头结点是null还是占位节点

posted @ 2020-05-07 15:04  lygzcom  阅读(167)  评论(0)    收藏  举报