(一)栈
1.栈的顺序存储结构
采用数组的形式
2.栈的链式存储结构
入栈和出栈采用头插法(考虑有没有头结点的情况)
3.栈的应用
1.栈实现左右括号匹配
思想:
1.碰到左括号,则将左括号入栈
2.碰到右括号,取出栈顶元素
3.重复步骤1、2,
若出现 以下两种情况,则说明匹配异常。:
一、碰到右括号但栈为空
二、遍历完所有括号,但栈不为空
2.栈实现后缀表达式求值
思想:只需要1个操作数栈
从左到右扫描表达式
1.若出现操作数,则操作数入栈
2.若出现运算符,则从操作数栈弹出两个操作数进行运算,将运算结果入栈
3.重复步骤1、2,扫描完成后最终操作数栈中保存的即为最终结果
3.栈实现中缀表达式转后缀表达式
思想:
从左到右扫描表达式
1.若为操作数则直接输出到后缀表达式中
2.若为'(' 则'('直接入栈 ;
若为')'则将栈顶元素不断出栈输出到后缀表达式中,直至碰到'('( '(' 也出栈,但不输出到后缀表达式中 );
若为其它运算符,则当栈为空/该运算符的优先级大于栈顶元素优先级时,入栈;否则不断出栈输出到后缀表达式中,直到栈顶运算符的优先级小于该运算符,让该运算符入栈。
总之,保持栈顶元素优先级比较高。
4.栈实现中缀表达式的计算
思想:
合并 中缀转后缀+后缀表达式的计算
从左到右扫描表达式
1.若为操作数,则进入操作数栈
2.若为运算符,则按照中缀转后缀中的运算符规则,弹出/压入运算符,弹出的运算符与操作数栈中的栈底两个数进行运算,将运算结果再压入栈中。
3.重复1、2直到扫描完成表达式
5.栈实现递归函数调用
思想:
函数调用时需要用一个递归工作栈,保存函数的返回地址、实参、局部变量,进入一次递归,则将这些信息入栈,退出函数调用时弹栈即可。
(二)队列
1.队列的顺序存储结构
讨论一般队列和循环队列。
一般的队列,数组下标front表示队列头(当队列为空时,front指向的队列头还没有装入元素),只有当出队时,front进行更新front++;数组下标rear表示队列尾的下一个元素,即入队时,直接让Q[rear] = x;rear++即可,只有当入队时rear才会进行更新,出队时rear不进行更新。
一般的队列采用数组的形式存储,这样的缺点时,如果一直出队,则front下标会一直增大,但front之前的空间保持空闲,造成空间浪费,因此采用循环队列。
循环队列:
入队:Q[rear] = x; rear = (rear+1) % maxsize;
出队: front = (front + 1) % maxsize;
判断是否为空还是为满: 稍微麻烦些,这是因为当队列为空的开始状态时 front=rear,当队列完全装满时,front=rear。因此不能直接根据front和rear的大小进行判断队列是否为空还是为满。有三种方式可以判断:
1.牺牲一个单元 这样为空可用 front==rear,为满可用 (rear+1)%maxsize==front来判断
2.设置队列的属性size 为空用size==0,为满用size==maxsize判断
3.设置tag, 删除操作设置tag=1,插入操作设置tag=0,队列为空用rear==front&&tag==1,队列为满用rear==front&&tag==0判断
2.队列的链式结构
队列的链式结构是带有头结点的链表,设置队头指针Q.front和队尾指针Q.rear,队头指针指向头结点,队尾指针就指向队尾元素。
入队时 在队尾后添加一个节点即可
出队时 出的是头结点后面的那个节点,出队时rear指针一般不动,除非出队以后队列变成了空指针,此时赋值Q.rear = Q.front
判断为空 即用Q.rear = Q.front进行判断
3.队列的应用
1.队列实现杨辉三角
核心:每个元素出完队列,保存该元素的值,与队头元素相加。