栈的特点:后进先出
栈的应用:数制转换、表达式求值、括号匹配的检验、八皇后问题、行编辑程序、函数调用、
迷宫求解、递归调用的实现
队列的特点:先进先出
队列的应用:脱机打印、用户优先级排队等
二者本质上都是线性表,只不过它们的删除和插入位置被限定,栈是队尾插入队尾删除,队列是队尾插入队头删除
栈Stack
简称LIFO结构
仅在表尾操作的线性表,表尾(an端)称作栈顶Top,表头(a1端)称作栈底Base
入栈:插入到表尾(栈顶)
出栈:从栈顶删除
既可以用顺序存储结构也可以用链式存储结构,一般用顺序栈
e.g.:
- 十进制N转换成其他进制d
- 括号匹配的检验:左括号进栈,右括号匹配,匹配成功则出栈
- 表达式求值(算符优先算法)
顺序栈
利用一组连续的存储单元依次存放在栈顶到栈底的数据元素,栈底为低地址端
设置top指针指向栈顶元素之上的下标地址,base指针为栈底元素,用stacksize表示栈的最大容量即数组最多能放多少个数据
空栈的标志:top和base都指向数组中第一个元素即base==top
栈满的标志:top-base==stacksize
上溢:栈满还要压入 下溢:栈空还要弹出
- 顺序栈的类型定义
- 顺序栈的初始化:分配一块空间,将base和top都指向0号位置
- 判断栈是否为空
- 求顺序栈的长度:求两个指针的差
- 清空顺序栈:栈还保留,但是没有元素,即使top也指向栈底
- 销魂顺序栈:释放空间,栈不保留,两个指针都设置为空,大小设置为0
- 顺序栈的入栈:首先判断是否栈满,元素存储到top指针所指位置,然后top指针上移(*表示取这一块空间,然后把e存进去)
- 顺序栈的出栈:先判断栈是否为空,获取栈顶元素,栈顶指针减一
链栈
只能在链表头部操作,链表的头指针指向栈顶
- 链栈的初始化:构造一个空栈,头指针S指向它
- 判断链栈是否为空:头指针S为空
- 链栈的入栈:栈顶插入,把要插入的节点用指针p指向,节点的next域指向S,将S指向新节点
- 链栈的出栈:将要删除节点的数据保存,用p指针指向,然后将头指针S向下移动,释放p指向的节点
- 取栈顶元素:直接将头指针S指向的节点的data域的数据取出即可
栈与递归
队列queue
简称FIFO结构
既可以用顺序存储也可以用链式存储,循环顺序队列更常见
e.g.:
- 舞伴问题:
循环顺序队列
- 队列的顺序表示:这里的头尾指针不是真的指针,而是用来存数组下标的
队空:front==rear
真溢出:front=0,rear=MAXSIZE 假溢出:front!=0,rear=MAXSIZE
即循环队列,base[0]接在base[MAXSIZE-1]之后,实现方法:利用模运算
采用模运算后,会出现 队空和队满标志相同的问题
解决:另外设置一个标志区分队空和队满,或者用count记录元素个数,或者少用一个元素空间
少用一个元素空间则队尾指针加一再模都会和队头指针重合,因此判断队满
- 队列的初始化:分配一块空间,将队列的两个指针置0
- 求队列的长度:如果用rear减去front有可能会出现负数情况,所以要加上MAXSIZE后对MAXSIZE取模
- 循环队列入队:首先判断是否队满,先将要入队的元素放入rear指针指向的位置,然后rear指针往后移
- 循环队列出队:首先判断是否队空,将队头的元素保存,然后front指针往后移
- 取队头元素:直接取front指针所指即可
链队
当用户无法估计队列的长度时,采用链队,理论上只要内存够大,存多少元素都行
采用如下递归定义,自己定义自己,并采用两种方式定义,一种是定义节点QNode,一种是定义指向节点的指针*QuenePtr。队列比链表麻烦一些需要两个指针,一个头指针一个尾指针
空队列:头指针和尾指针都指向头节点
- 链队列的初始化:开辟一块空节点,让头指针和尾指针都指向它,然后把节点的指针域置空
- 销毁链队列:从头节点开始将所有节点释放。首先用p指针指向头节点的下一个节点,(不能先将头节点释放再用p指针指向,因为这样子下一节点的地址就找不到了)然后释放头节点,再将头指针指向下一节点,以此类推
- 链队列将元素e入队:在内存中找到元素e的节点空间,用p指向该节点,数据域设置为e,指针域设置为空,将尾指针Q.rear指向该节点
- 链队列出队:用p指针指向首元节点的下一节点记录地址,然后释放首元节点,将头指针指向下一节点。要考虑特殊情况:若p指向了尾节点,删除后要将尾指针Q.rear指向头节点
- 求链队列的队头元素: