栈
定义
只允许在端进行插入或删除(操作受限的线性表)
后进先出简写:LIFO
卡特兰数:有n个不同元素进栈,出栈元素不同排列的个数为1𝑛+1𝒞n2𝑛
(比如5种元素,用上述公式就是42种出栈排列)
链栈实际就是单链表,单链表对表头操作等同于对栈顶操作
队列
定义
只允许在一段进行插入另一端进行删除(操作受限的线性表)
先行先出简写:FIFO
静态的队列和栈实际就是定义一个元素数组+指针的结构体(不需要*指针)
PS:以下几行基于队尾指针指向队尾元素下一个位置
队列的判空看队头指针和对位指针是不是指向一个值(Q.rear==Q.front)
队尾指针指向MaxSize不是代表队列满,需要对队尾指针取模(因为可能有的元素从队头出了)(这样也叫循环队列)
队尾指针的后一个位置等于队头指针表示队列满了,这种方法需要牺牲一个空间
队列元素个数的计算:(rear+MaxSize-front)%MaxSize
如果题目不允许浪费存储空间,那么可以有这些方法来判断队空队满①定义一个Size记录队列,来判断是否队满②定义一个tag来记录最近一次的操作是插入还是删除,如果插入导致rear==front,则队满,如果是删除导致rear==front,则队空
PS:以上几行基于队尾指针指向队尾元素下一个位置
PS:以下几行基于队尾指针指向队尾元素位置
判空:(rear+1)%MaxSize==front
判满:(rear+2)%MaxSize==front(规定头指针前一个存储单元不能存放元素,牺牲一个存储单元,也可以像上面一样加一个tag或者size的方法)
PS:以上几行基于队尾指针指向队尾元素位置
双端队列
允许从两边插入、删除。还有变种,比如输入受限的双端队列(操作受限的线性表)
一般双端队列产生的出入队列个数也可以用卡特兰数算
括号匹配问题
思想:栈
最后出现的左括号最先被匹配(LIFO)
每出现一个右括号,就“消耗”一个左括号
算法实现:顺序检索括号,遇到左括号压入栈中,遇到右括号弹出栈顶左括号进行匹配
表达式求值问题
思想:栈
一般算数表达式组成部分:操作数、运算符、界限符
逆波兰表达式=后缀表达式;波兰表达式=前缀表达式(这两种式子可以省去界限符)
例子:中缀表达式(a+b-c*d);后缀表达式(ab+cd*-);前缀表达式(-+ab*cd)
中缀转后缀的时候原则(照理来讲可以转为很多种,但计算机实现的话只有一种标准答案):先乘除后加减,括号优先。左优先
后缀表达式的计算(手算):从左往右扫描,每遇到一个计算符,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数
算法实现(后缀表达式,计算机算后缀的比中缀快哦):从左往右扫描元素,若扫描到操作数压入栈,若扫描到运算符则弹出两个栈顶元素执行相应运算(注:先弹出的数在符号右边)并把运算结果压到栈顶,最后栈里剩下的一个元素,就是计算结果
前缀表达式算法实现,相比后缀。区别一:要从右往左扫描元素;区别二:先弹出的数在符号左边
中缀表达式转后缀表达式(机算):从左到右处理元素①遇到操作数,直接加入后缀表达式②遇到界限符。遇到“(”直接入栈,遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止③遇到运算符。一次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。④处理完后,把所有栈内元素依次从栈顶弹出
中缀表达式的计算(机算):把中缀转后缀和后缀计算结合起来
栈的应用——递归
函数调用特点:LIFO
函数调用时,需要一个栈存储:1、调用返回地址;2、实参;3、局部变量
递归表达式的设计:递归体+递归出口
缺点:太多层递归可能导致栈溢出、可能包含重复运算
队列的应用
树的层次遍历
图的广度优先遍历
操作系统中的应用:FCFS(先来先服务)eg:CPU资源的分配、打印数据缓冲区
特殊矩阵的压缩存储
二维数组有行优先存储和列优先存储,具有随机存储特点,a[i][j]在a[M][N]的存储地址=LOC+(i*M+j)*sizeof(ElemType)
普通矩阵二维即可,特殊数组:对称矩阵、三角矩阵、三对角矩阵、稀疏矩阵可以压缩
对称矩阵的压缩:策略:只存储主对角线+下三角区。按行优先原则将各元素存入一维数组中。
总共需要存储1+𝑛∗𝑛2个空间。key:按行优先原则,ai,j是第几个元素?若是下三角,则K=i(i-1)/2+j;若是上三角,根据对称性质K=j(j-1)/2+i-1
三角矩阵的压缩:策略:和对称矩阵一样先存一半,然后最后一个存储单元存储常量C
三对角矩阵(带状矩阵)的压缩:策略:行优先只存带状部分
稀疏矩阵的压缩:策略:①顺序存储——三元组<行,列,值>②链式存储——十字链表法