七、栈和队列

简介: 七、栈和队列

1、栈和队列的定义和特点



栈和队列是两种常用的,重要的数据结构,栈和队列是限定插入和删除只能在表的“端点”进行的特殊的线性表。


栈的特点是先进后出,由于栈具有这个特点,使得栈称为程序设计中的有用工具,如果问题求解的过程具有先进后出的特点,则求解的算法中必然需要利用到栈。如下述问题的算法设计,都需要用到栈:数值转换,表达式求解,括号匹配的检验,八皇后问题,行编辑程序,函数调用,迷宫求解,递归调用的实现。


Insert(S,n+1,e);//栈只能插入表尾部
Delete(S,n);  //栈只能删除表尾部

队列的特点是先进先出,由于队列的这个特点,使得队列称为程序设计中解决类似排队问题的有用工具:脱机打印输出;多用户系统中,多个用户排成队,分时循环使用CPU和主存;按用户的优先级排成多个队,每个优先级一个队列;实时控制系统中,信号按接收的先后顺序依次处理;网络电文传输,按到达时间先后顺序依次进行

Insert(Q,n+1,e);//队列只能插入表尾部
Delete(Q,1);  //队列只能删除表头部



栈和队列是线性表的子集,是插入和删除位置受限的线性表。




2、栈-stack


栈是仅在表尾进行插入、删除操作的线性表。表尾(即 a n a_n an端)称为栈顶Top;表头(即 a 1 a_1 a1端)称为栈底Base。

88c4d2c9a1c24afaa7a0da5a443083c5.png

插入到栈顶的操作,称为入栈;从栈顶删除最后一个元素的操作,称为出栈。


9cdca237f38a4fc3a707f6b3d8c1bdce.png


栈总结:

59c9de0e75b64394a59a45e14c7a1312.png




3、队列-queue


队列是一种先进先出(FIFO)的线性表。在表的一端插入(表尾),在另一端(表头)删除。

7855a35f1db345359bc179c23e2552c2.png


KaTeX parse error: Undefined control sequence: \qquda at position 1: \̲q̲q̲u̲d̲a̲队列总结:


f28d2ab27c384ea39c2147a53d027245.png



4、栈和队列的案例


4.1 进制转换-栈

十进制整数N向其他进制数d(二、八、十六)的转换是计算机实现的基本问题。转换的法则是:除以d倒取余数,该法则对应一个简单的算法原理:


n=(n  div  d)d+n  mod  d



其中,div为整除运算,mod为求余运算。


8d74167fd49c437db7aeb7a098673885.png

string BaseConversion(long long orgNum, int base)
{
  string res;
  if (orgNum == 0 || base == 0)
    return res;
  bool posiFlag = true;
  if (orgNum < 0)
    posiFlag = false;
  orgNum *= !posiFlag  ? -1:1;    //将初始数字全部转化为正数
  stack<int> tRes;    //记录结果栈
  while (orgNum)
  {
    tRes.push(orgNum % base);
    orgNum /= base;
  }
  if (!posiFlag)
    res.push_back('-');
  while (!tRes.empty())
  {
    res.push_back(tRes.top() + '0');
    tRes.pop();
  }
  return res;
}


4.2 检验括号是否匹配-栈


假设表达式中允许包含两种括号,圆括号和方括号,其嵌套的书序随意,但是括号之间左括号和右括号出现的嵌套顺序必须相同,同时,左右括号的数量必须匹配。


3fab0c8323b546609eedd2eb9662c8cb.png


算法思路如下:可以利用一个栈的结构保存每个出现的左括号,当遇到右括号时,从栈中弹出左括号,检验匹配情况。在检验的过程中,若遇到以下几种情况之一,就可以得出括号不匹配的结论:


(1) 当遇到某一个右括号时,栈已经空了,说明有括号多于左括号;


(2) 从栈中弹出的左括号与当前检验的右括号的类型不同,说明出现了类型交叉的情况;


(3) 算术表达式输入完毕,但栈中还有没有匹配的左括号,说明左括号多于右括号。


bool ParenthesisMatching(string Parenthesis)
{
  stack<char> rec;
  for (const auto e : Parenthesis)
  {
    if ((e == ')' || e == ']') && rec.empty())
      return false;
    if (!rec.empty() &&(
      (rec.top() == '(' && e == ')') ||
      (rec.top() == '[' && e == ']')))
    {
      rec.pop();
      continue;
    }
    if (!rec.empty() &&(
      (rec.top() == '(' && e == ']') ||
      (rec.top() == '[' && e == ')')))
      return false;
    rec.push(e);
  }
  if (!rec.empty()) return false;
  return true;
}


4.3 表达式求值-栈


表达式求值是程序设计语言编译中的一个最基本的问题,它的实现也需要运用栈。这里介绍的算法是由运算符优先级确定运算顺序的对表达式求值的算法–算符优先算法。


表达式的组成:操作数(operand):常数、变量;运算符(operator):算术运算符,关系运算符和逻辑运算符;界限符(delimiter):左右括号和表达式结束符。


任何一个算术表达式都由操作数(常量、变量)、算术运算符(+,-,*,/)和界限符(括号、表达式结束符’#’,虚设的表达式起始符’#’)组成。后两者统称为算符。例如:# 3*(7-2)#


为了实现表达式的求值,需要设置两个栈:一个是运算符栈OPTR,用于寄存运算符,另一个称为操作数栈OPND,用于寄存运算数和运算结果。


求值的处理过程是从左到右扫描表达式的每一个字符,当扫描到的是运算数时,将其压入栈OPND。


当扫描到的是运算符时:


若这个运算符OPTR栈顶运算符的优先级高,则入栈OPTR,继续向后处理;


若这个运算符比OPTR栈顶运算符优先级低,则从OPND栈中弹出两个运算数,从栈OPTR中弹出栈顶运算符进行运算,并将运算结果压入栈OPND中。


继续处理当前字符,直到遇到结束符为止。



4.1 舞伴问题-队列


假设在舞会上,男士和女士各自排成一队。舞会开始时,依次从男队和女队的对头各出一人配成舞伴。如果两队的初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写一个算法模拟上述舞伴配对问题。


该问题有典型的先进先出的特点,可以用队列作为算法的数据结构。

a97f6336fc2d45e2a709d7867377f157.png


在这里插入图片描述 \qquad 首先构造两个队列;依次从将队头的元素出队配成舞伴;当某个队列为空,则另外一队等待的人即是下一轮舞曲第一个可以获得舞伴的人。



5、栈的表示和实现



栈的抽象数据类型表示如下所示:

f9dbec8e220243be97d244acd7585ac6.png


由于栈的本质是线性表,所以栈也有顺序存储和链式存储两种实现方式,站的书序存储叫做顺序栈;栈的链式存储叫做链栈。


5.1 顺序栈的表示和实现


栈的存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元一次存放自栈底到栈顶的数据元素栈底一般在低地址端。附设top指针,知识栈顶元素在顺序栈中的位置,一般top指针指示的是真正栈顶元素之上的下标地址;附设base指针,指示栈底元素在顺序栈中的位置;用stackSize表示栈可以使用的最大容量。

906929cc28d146a5af67042881145f4c.png

当  top==base时,表示栈空;当op−base==stacksize时表示栈满。上溢出(OverFlow): 栈已经满了,但是还要继续压入元素;下溢出(underFlow): 栈已经空了,但是还要弹出元素。


顺序栈的表示:

#define MAXSIZE 100
class sqStack
{
  SElemType *base;    //栈底指针
  SElemType *top;     //栈顶指针
  int stackSize;      //栈可用最大容量
};

顺序栈的初始化:

sqStack()
{//构造函数
  try{
    base = new SElemType[MAXSIZE];//申请栈空间
  }
  catch(...)
  {
    cout << "栈空间分配失败"<<endl;
    exit(-1);
  }
  top = base;//空栈,栈顶指针等于栈底指针
  stackSize = MAXSIZE;
}



顺序栈的销毁:

~sqStack()
{
  if(base)
  {
    delete base;
    base = top = nullptr;
    stackSize = 0;
  }
}


判断顺序栈是否为空:

bool StackEmpty()
{
  if(top == base)
    return true;
  else return false;
}

求顺序栈的长度:

int StackLength(sqStack &S)
{
  return S.top-S.base;
}

清空顺序栈:

void ClearStack()
{
  if(base)
    top = base;
}

顺序栈的入栈:

void StackPush(SElemType &e)
{
  if(base && //栈不为null
  top-base < stackSize)//栈不满
  {
    *top = e;
    ++top;
    //*top++ = e;  //上面两句可以合为一句
  }
  else
  {
    cout<<"栈没申请空间或者栈满了"<<endl;
    exit(-1);
  }
}

顺序栈的出栈:

bool Pop(SqStack &S, SElemType &e)
{
  if(S.top==S.base)
  {
    cout << "栈已经空了!" <<endl;
    return false;
  }
  e = *--S.top();
  return true;
}
相关文章
|
16天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
7天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
9天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
12天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
14天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
42 4
|
19天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
17 1
|
1月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
68 1
|
1月前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
16 0
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器