写在前面的话:
适应范围:《数据结构》复习总结系列适用于考研、期末考试、考前复习,小白新手
本系列参考书目: 《数据结构:c语言版》(严蔚敏)
第三章 栈和队列-栈
本章继续对线性结构进行讨论,栈和队列作为特殊的线性表,引起操作受限的特点,命题形式较为灵活。
因此需要掌握栈和队列的特点,熟练掌握栈的两种存储结构的基本操作实现算法,特别应注意栈满和栈空的条件;熟练掌握循环队列和链队列的基本操作实现算法,特别注意队满和队空的条件。同时需要理解递归算法执行过程中栈的状态变化过程和理解表达式求值方法。
【考点】 ①栈和队列的定义和特点
②栈的表示和操作的实现
③队列的表示和操作的实现
【本章大纲】
【目录】
一、栈的定义与特点
1.1 栈的定义
【定义】如图中所示,只能在表的一端(栈顶)进行插入和删除运算的线性表。
1.2 栈的结构
【逻辑结构】作为只能对一端进行操作的线性表,其结构仍然遵循线性表的一对一关系
【存储结构】使用顺序存储-顺序表或链式存储-链表存储均可。
1.3 栈的特点
【特点】只能在栈顶运算,且访问结点时依照后进先出(LIFO)。
【术语】通常情况下,数据元素进栈也成为压栈PUSH(),数据元素出栈也称为弹栈POP()。
二、栈的顺序存储
2.1顺序栈表示相关概念
顺序栈是指利用顺序存储结构实现的栈,即使用一组连续的的存储蓝宇依次存放自栈底到栈顶的数据元素。
同时附设指针top指栈顶元素所在顺序栈中的位置,通常top=0时,表示为空栈,即栈中没有元素;另设指针base指栈底元素所在顺序栈的位置,通常base==top时,表示空栈(因数组下标从0开始,如按top=0进行设定将会有很大不便)。
另外,顺序栈和顺序表相同,其受最大空间容量的限制。
2.2 顺序栈的存储结构表示
【算法描述】
#define MAXSIZE 100
typedef struct
{
SElemType *base; //栈底指针
SElemType *top; //栈顶指针
int stacksize; //栈可用的最大容量
}SqStack;
【注】base指针始终指向栈底元素,base=NULL表示此栈不存在;
top指针始终指向栈顶元素,top初值指向栈底。元素入栈时,top++;数据元素出栈,top--;一般情况下,栈非空时,指针top栈顶元素的上一个元素。
2.3 顺序栈中基本操作的实现
2.3.1 顺序栈的初始化
【算法思想】①为顺序栈动态分配-个最大容量为MAXSIZE的数组空间,使base 指向这段空间的基地址,即栈底。
②栈顶指针top初始为base,表示栈为空。
③stacksize 置为栈的最大容量MAXSIZE
【算法描述】
Status InitStack( SqStack &S )
{
S.base =new SElemType[MAXSIZE]; //动态分配一个最大容量为MAXSIZE的空间
if( !S.base ) return OVERFLOW; //分配失败
S.top = S.base; //top=base,初始话,为空栈
S.stackSize = MAXSIZE; //设置栈的最大容量
return OK;
}
2.3.2 顺序栈的入栈(⭐⭐⭐)
【算法思想】①判断栈是否满,若满则返回ERROR。
②将新元素压人栈顶,栈顶指针加1。
【算法描述】
Status Push( SqStack &S, SElemType e)
{
if( S.top - S.base== S.stacksize ) // 栈满
return ERROR;
*S.top=e; //元素e压入栈顶
S.top++; //栈顶指针+1
return OK;
}
2.3.3 顺序栈的出栈(⭐⭐⭐)
【算法思想】①判断栈是否空,若空则返回ERROR。
②栈顶指针减1,栈顶元素出栈。
【算法描述】
Status Pop( SqStack &S, SElemType &e)
{
if( S.top == S.base ) // 判断是否栈空
return ERROR;
S.top--; //栈顶指针减一
e=*S.top; //获取栈顶元素e
return OK;
}
2.3.4取栈顶元素
【算法思想】①判断是否空栈,若空则返回错误
【算法描述】
Status GetTop( SqStack S, SElemType &e)
{
if( S.top == S.base ) return ERROR; // 判断是否栈空
e = *( S.top – 1 );
return OK;
}
2.4 共享栈(考研知识范畴)
【特点】①如下图中所示,共享栈由两个栈组出,双栈共享一个栈空间。
②将编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。
当第0号栈的栈顶指针top[0]等于-1时该栈为空,当第1号栈的栈顶指针top[1]等于m时该栈为空。
两个栈均从两端向中间增长(如下图所示) 将编号为0和1的两个栈存放于一个数组空间V[m]中,
栈底分别处于数组的两端。
【优点】灵活性强,减少溢出机会。
【栈的判断】①栈空:top[i] == base[i] i表示栈的编号
②栈满:top[0]+1==top[1] 或top[1]-1==top[0]
三、栈的链式存储
3.1链栈表示相关概念
链栈是指利用链式存储结构实现的栈,是操作受限的单链表。因为栈的主要操作是在栈底顶插入和删除,显然以链表的头部作为栈顶最为方便,故没有必要附加头结点,栈顶指针就是链表的头指针。(对于栈常使用顺序栈进行操作)
3.2 链栈的存储结构表示
【算法描述】
typedef struct StackNode
{
SElemType data; //存储结点的数值
struct StackNode *next; //结点指针
} StackNode, *LinkStack;
3.3 链栈中基本操作的实现
3.3.1 链栈的初始化
【算法思想】构造一个空栈,并将栈顶指针设置空。
【算法描述】
Status InitStack(LinkStack &S )
{
S=NULL; //栈顶指针置空
return Ok;
}
3.3.2 链栈的入栈
【算法思想】①为入栈元素e分配空间,用指针p指向。
②将新结点数据域置为e。
③将新结点插入栈顶。
④修改栈项指针为p。
【算法描述】
Status Push(LinkStack &S , SElemType e){
p=new StackNode; //生成新结点p
p->data=e; //将新结点数据域置为e
p->next=S; //将新节点插入栈顶
S=p; //修改栈顶指针为p
return OK;
}
3.3.3 链栈的出栈
【算法思想】①判断栈是否为空,若空则返回ERROR。
②将栈顶元素赋给e。
③临时保存栈顶元素的空间,以备释放。
④修改栈顶指针,指向新的栈顶元素。
⑤释放原栈顶元素的空间。
【算法描述】
Status Pop (LinkStack &S,SElemType &e)
{
if (S==NULL) return ERROR; //判断是否栈空
e = S-> data; //将栈顶元素赋值给e
p = S; //p保存栈顶元素空间
S = S-> next; //修改栈顶指针
delete p; //释放栈顶元素空间
return OK;
}
3.3.4 取栈顶元素
【算法思想】当栈非空时,此操作返回当前栈顶元素的值,栈顶指针S保持不变。
【算法描述】
SElemType GetTop(LinkStack S)
{
if (S==NULL) //判断是否栈空
exit(1);
else
return S–>data; //返回栈顶元素
}
四、栈与递归
4.1 递归
递归是指若在-一个函数、过程或者数据结构定义的内部又直接(或间接)出现定义本身的应用,则称它们是递归的,或者是递归定义的。
如同下面的故事一样,我得到金、银、铜、铁、木五个宝箱,我想打开金箱子,却没有打开这个箱子的钥匙。
在金箱子上面写着一句话:“打开我的钥匙装在银箱子里。”于是我来到银箱子前,发现还是没有打开银箱子的钥匙。银箱子上也写着一句话:“打开我的钥匙装在铜箱子里。”于是我再来到铜箱子前,发现还是没有打开铜箱子的钥匙。铜箱子上也写着一句话:“打开我的钥匙装在铁箱子里。”于是我又来到了铁箱子前,发现还是没有打开铁箱子的钥匙。铁箱子上也写着一句话:“打开我的钥匙装在木箱子里。我来到木箱子前,打开了木箱,并从木箱里拿出铁箱子的钥匙,打开了铁箱,从铁箱里拿了出铜箱的钥匙,打开了铜箱,再从铜箱里拿出银箱的钥匙打开了银箱,最后从银箱里取出金箱的钥匙,打开了我想打开的金箱子。一个啰嗦的故事。
同时数学中的问题也常用到递归,如常谈到的2阶斐波那契数列
4.2 递归与栈
当在一个函数的运行期间调用另一个函数时,在运行该被调用函数之前,需先完成三项任务: ①将所有的实在参数、返回地址等信息传递给被调用函数保存; ②为被调用函数的局部变量分配存储区; ③将控制转移到被调用函数的入口。
从被调用函数返回调用函数之前,应该完成下列三项任务: ① 保存被调函数的计算结果;② 释放被调函数的数据区; ③依照被调函数保存的返回地址将控制转移到调用函数
【例题】设单链表的表头指针为L,结点结构由data和next两个域构成,其中data域为字
符型。试设计算法判断该链表的全部n个字符是否中心对称。例如xyx、xyyx 都是中
心对称。
【答案参考】
/*算法思想:使用栈来判断链表中的数据是否中心对称。让链表的前一半元素依次进栈。
在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,
若相等,则将链表中的下一个元素与栈中再弹出的元素比较,直至链表到尾。
这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中的--个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。*/
int dc(LinkList L, int n) {
int i;
char s[n/2] ; //s字符栈
p=L->next; //p是链表的工作指针,指向待处理的当前元素
for(i=0;i<n/2;i++) { //链表前一-半元素进栈
s[i]-p->data;
p=p->next;
}
i--; //恢复最后的i值
if (n82==1) //若n是奇数,后移过中心结点
p-p->next;
while (p! -NULL&&s[i]--p->data) { // 检测是否中心对称
i--; //i充当栈顶指针
p=p->next;
}
if(i==-1) //栈为空栈
return 1; //1代表链表中心对称
else
return 0; //0代表链表不中心对称
}