3.2 栈典型题例
【例3-3】若某堆栈的输入序列为1,2,3,…,n-1,n,输出序列的第1个元素为n,问第i个输出元素是什么?
【分析与解答】堆栈的输入序列为1,2,3,…,n-1,n,堆栈的特性是后进先出,输出序列的第1个元素为n,那么第2个元素为n-1,第3个元素为n-2,依次类推,第i个输出元素是n-i+1。
【例3-4】简述以下算法的功能。
void Reverse (SeqStack s)
{
n=0;
while (!StackEmpty (s))
{ a[n]=Pop(s); n++; } for(i=0;i<n;i++) Push(s,a[i]); } /*Reverse*/ void Remove (LinkStack s,ElementType e) { while (!StackEmpty (s)) { d=Pop(s); if (d!=e) Push(t,d); } while (!StackEmpty (t)) { d=Pop(t); Push(s,d); } } /* Remove */
【分析与解答】算法Reverse中,堆栈s的元素从栈底到栈顶的序列设为e1,e2,…,en,while循环将s的元素出栈,存入a数组,a数组中各元素的值如表3-1所示,for循环将a数组元素依a[0],a[1],…,a[n-1](即en,en-1,…,e1)的顺序入s栈,从栈底到栈顶的序列为en,en-1,…,e1。因此,该算法借助数组a实现了堆栈s的逆置。
表3-1 数组a中元素的值
算法Remove中,第1个while循环将s的元素(除元素e)出栈进入t栈,t栈是s栈不包括e元素的逆置;第2个while循环则将t的元素出栈后又进入s栈,s栈是t栈的逆置,最终s经过两次逆置又恢复成原来的元素序列(不包括元素e),因此该算法借助堆栈t将堆栈s中的数据元素e删除。
【例3-5】图3-5所示是一组n条带头结点的链表表示的堆栈(n个链栈),top[0],top[1],…, top[n-1]分别是各堆栈栈顶指针,试给出该组堆栈的数据类型定义,并写一算法对这组堆栈初始化。
【分析与解答】数据类型(链表中结点类型StackNode与链指针类型LinkStack)定义如下。
#define MaxSize 栈顶指针数组的最大长度 typedef struct snode { ElementType data; struct snode *next; }StackNode; typedef StackNode *LinkStack; / * LinkStack为指向StackNode的指针类型* /
图3-5 一组n条带头结点的链表示的堆栈
构成n个栈顶指针的数组数据类型定义如下。
LinkStack top[MaxSize];
堆栈组初始化算法如下。
void StackInit(LinkStack top[],int n) { for (i=0;i<n;i++) { p=(LinkStack)malloc(sizeof(StackNode));/*生成堆栈头结点*/ p->next=NULL;/*堆栈头结点指针为空*/ top[i]=p;/*各数组元素为指向头结点的头指针*/ } }/* StackInit */
【例3-6】写一算法,将十进制数转换为十进制以下进制的数。
【分析与解答】将十进制数转换成其他进制数有一种简单的方法,即利用公式:
N=(N div d)*d+N mod d
其中div为整除运算,mod为求余运算,N是十进制数,d表示要转换到的目标进制(其他进制)。公式中十进制数N转换为d进制数的过程就是,N除以d取其余数,最先得到的余数就是最低位,依次到最高位。
例如,将十进制数67转换为二进制数,其转换过程如下。
67 div 2=33余数为1
33 div 2=16余数为1
16 div 2=8余数为0
8 div 2=4余数为0
4 div 2=2余数为0
2 div 2=1余数为0
1 div 2=0余数为1
结果为余数的逆序1000011,即(67)10=(1000011)2。
算法描述如下。
int Conversion(int n,int d) /*将十进制数n转换为d进制(十进制以下进制)数*/ { SeqStack s; result=0; s.top=-1; while (n) { Push(s,n%d); /*余数入栈*/ n=n/d; } while (!StackEmpty (s)) result=result*10+Pop(s); return(result); } /*conversion */
【例3-7】一个简单的行编辑程序的功能是接收用户的终端输入,并允许用户对输入的错误及时修改。约定“#”为退格符,表示前一个输入的字符无效,“@”为退行符,表示当前行的输入无效。若用户的终端输入为while##le (s1#!=0),则对应的有效输入为while(s!=0)。写一模拟行编辑程序的算法。
【分析与解答】可借用两个堆栈s、t来模拟行编辑程序的工作过程。
(1)堆栈s用来接收用户输入的普通字符,如果输入的是特殊字符“#”或“@”,则对s栈做一次出栈或清空操作。
(2)当一行字符输入结束后,将s栈中的字符依次出栈并压入t栈,t栈中存放有s栈字符的逆序列。
(3)将t栈中的字符依次出栈存放在数组str中,数组str中存放有t栈字符的逆序列,即s栈字符的正序列,并且每行字符都存入str。
(4)当输入结束时,输出字符数组str的值,也就输出了用户输入、编辑后的有效字符。
void LineEdit() /*行编辑程序模拟算法*/ { s=StackInit (); t=StackInit (); strlen=0; ch=getchar(); while(ch!=EOF) /*EOF表示输入结束符*/ { while(ch!=EOF && ch!='\n') /*接收一行字符*/ { switch(ch) { case '#' : ch=Pop(s);break; case '@' : StackClear (s);break;/*将s栈置空*/ default : Push(s,ch);break; /*输入字符存入s栈*/ } ch=getchar(); } if (ch=='\n') Push(s,ch);/*换行符进栈*/ while(!StackEmpty(s)) { x=Pop(s); Push(t,x);/* t栈存放s栈中字符序列的逆序*/ } while(!Empty(t)) { x=Pop(t); str[strlen++]=x; } if (ch!=EOF) ch=getchar(); } str[strlen]='\0'; printf(str); } /* LineEdit */