栈是限定仅在表尾进行插入和操作的线性表;允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈,栈又称后进先出的线性表(即LIFO结构)

  1. 栈是特殊的线性表(限制了这个线性表的插入和删除位置),有前驱和后继关系,线性表的表尾是指栈顶,栈顶是固定的
  2. 栈的插入(push)操作叫进栈(压栈、入栈),删除(pop)操作叫出栈(弹栈)

顺序栈(栈的顺序存储结构)

  1. 当栈存在n个元素时top(代表栈顶位置)等于n-1,栈底为0,空栈的判定条件为top等于-1
  2. 进栈:先检查;栈顶指针+1;将e(要插入的元素)赋给栈顶空间
  3. 出栈:检查;将栈顶元素赋值给e;栈顶指针-1;返回e值;进出栈的时间复杂度都是O(1)
  4. 存在缺陷:必须事先确定数组的存储空间大小

两栈共享空间

这种数据结构通常用在两个栈的空间需求有相反关系的情况,即一个栈增长时另一个栈在缩短,例如买卖股票;若两个栈都增长,就会因栈满而溢出

思路:两个相同类型的栈(栈A栈B),它们是在数组(长度为n)的两端,向中间靠拢; 设t1和t2是两个栈的栈顶指针,t1等于-1时栈A为空,t2等于n时栈B为空; 两个指针之间相差1时,即t1+1==t2栈满

  1. push:检查是否栈满;加一个判断栈号的参数,根据栈号对栈进行栈顶指针+1或-1并赋值的操作
  2. pop: 判断栈号;检查;出栈

链栈(栈的链式存储结构)

  1. 把栈顶放在单链表的头部,头指针相当于栈顶指针,因此一般链栈不需要头结点;栈顶指针等于NULL时为空栈
  2. push:新结点分配内存;把当前栈顶元素赋给新结点的直接后继;再把栈顶指针指向新结点;计数+1
  3. pop: 把栈顶结点赋给临时变量t;栈顶指针下移一位;释放t;计数-1;进出栈的时间复杂度也是O(1)
  4. 若元素变化不可预料,最好使用链栈;如果是可控的使用顺序栈更好

递归

直接调用自己或通过一系列的调用语句间接地调用自己的函数叫递归函数

  1. 斐波那契数列定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
  2. 每个递归定义必须有一个退出递归的条件,否则会无穷递归
  3. 迭代用的循环结构,递归是选择结构;递归使程序更简洁,但大量递归会耗费额外的时间和内存,视情况选择
  4. 编译器使用栈实现递归,递归过程退回的顺序是它前行顺序的逆序
  5. 前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中;在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态

四则运算表达式求值

  1. 逆波兰(RPN)表示法:一种不需要括号的后缀表达法,让计算机不用括号就可以对优先级的运算关系进行处理
  2. 后缀表达式:所有的符号都是在要运算数字的后面出现,规则为:从左到右从遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号就将处于栈顶的两个数字出栈后进行运算,把结果进栈,直到栈为空
  3. 平日里所用的标准四则运算表达式也叫做中缀表达式
  4. 中缀转后缀:从左向右遍历,若是数字就输出(即成为后缀表达式的一部分),若符号为右括号优先级不高于栈顶符号则栈顶元素依次出栈并输出(如果优先级高直接进栈),并将当前符号进栈,直到最终输出后缀表达式为止
  5. 使计算机处理标准表达式要求:将中缀转化为后缀;将后缀进行运算(两者都是用栈来进出运算)

队列

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,即先进先出(FIFO结构);允许插入的一端称为队尾,允许删除的一端称为队头

循环队列(队列的顺序存储结构)

队列顺序存储存在不足:删除操作的大O为O(n),性能低;而且会出现假溢出现象;为了应对假溢出,把队列的头尾相接形成循环,这种顺序存储结构称为循环队列

  1. 队列的空与满的判断:
    • 使用一个标识变量(布尔型)进行识别
    • 队满时:(rear+1)%len==front(len为队列长;由于rear,front均为所用空间的指针,循环只是逻辑上的循环,所以需要求余运算)
  2. 计算队列长度公式:(尾-头+表长) % 表长

链队列(队列的链式存储结构)

队头指针指向链队列的头结点队尾指针指向终端结点,空队列时front和rear都指向头结点

  1. 入队:将新结点赋给原队尾结点的后继;队尾指针指向新结点(新结点后继为NULL)
  2. 出队:把要删除的队头结点的值赋给临时变量;更新头结点后继;判断对头队尾是否相等;释放要删除的结点
  3. 与循环队列比较:
    • 时间上:大O都是O(1),不过当出队入队频繁时,链队列会存在一些时间开销(细微的)
    • 空间上:循环队列必须固定长度导致有存储个数和空间浪费的问题;链队列的指针域虽产生一些空间开销,但灵活
    • 小结:在能确定队列最大值的情况用循环队列;无法预估时用链队列