◆3.17③ 试写一个算法,识别依次读入的一个以@
为结束符的字符序列是否为形如’序列1&序列2′模式
的字符序列。其中序列1和序列2中都不含字符’&',
且序列2是序列1的逆序列。例如,’a+b&b+a’是属该
模式的字符序列,而’1+3&3-1′则不是。
实现下列函数:
Status match(char *str);
/* 若str是属该模式的字符序列,*/
/* 则返回TRUE,否则返回FALSE */
Stack是一个已实现的栈。
可使用的相关类型和函数:
typedef char SElemType; // 栈Stack的元素类型
Status InitStack(Stack &s);
Status Push(Stack &s, SElemType e);
Status Pop(Stack &s, SElemType &e);
Status StackEmpty(Stack s);
Status GetTop(Stack s, SElemType &e);
Status match(char *str) /* 若str是属该模式的字符序列,*/ /* 则返回TRUE,否则返回FALSE */ { //文档没有说明字符串是以@结尾的 //也没有说栈的类型是SqStack,用Stack时编译出错 SqStack s; SElemType c; Status flag=1; InitStack(s); char *cur=str; while('&'!=*cur){ Push(s,*cur); ++cur; } //入栈 ++cur; if(!GetTop(s,c)&&*cur!='@'){flag=0;}//判断栈空 while(*cur!='@' ){ Pop(s,c); if(c!=*cur){flag=0;break;} ++cur; }//依次出栈匹配 if(GetTop(s,c)){flag=0;}//再次判断是否非空 return flag; }
3.18② 试写一个判别表达式中开、闭括号是否配对出现的算法。
实现下列函数:
Status MatchCheck(SqList exp);
/* 顺序表exp表示表达式; */
/* 若exp中的括号配对,则返回TRUE,否则返回FALSE */
/* 注:本函数不使用栈 */
顺序表类型定义如下:
typedef struct {
ElemType *elem;
int length;
int listsize;
} SqList; // 顺序表
Status MatchCheck(SqList exp) /* 顺序表exp表示表达式; */ /* 若exp中的括号配对,则返回TRUE,否则返回FALSE */ /* 注:本函数不使用栈 */ /*****************************/ // 此题top指针和cur指针要很仔细地运用,还有判错条件也要小心 { ElemType *cur,*top,*base; base=exp.elem;//模拟栈底 top=cur=base+1;//模拟栈顶(top)和当前元素(cur) if(')'==*cur){ return FALSE; } //判断第一个字符是否为右括号 if(0==exp.length){ return TRUE; }//判断是否为空顺序表 while(cur<=base+exp.length-1){//依次遍历 if('('==*cur){//当元素为左括号时 if(top!=cur){ *top=*cur; *cur='0'; } ++top; }//top始终指向第二个元素 //////////////////////////// else if(')'==*cur){ if(top==base){ return FALSE; } if('('==*(top-1)){ *(--top)='0'; *cur='0'; } } //////////////////////////// ++cur; } if('0'==*base){return TRUE;}//此处应为base,而不是top return FALSE; }
◆3.19④ 假设一个算术表达式中可以包含三种括号:圆括号”(” 和
“)”,方括号”["和"]“和花括号”{“和”}”,且这三种括号可按任意的
次序嵌套使用(如:…[…{…}…[…]…]…[…]…(…)…)。编写判别给定表达
式中所含括号是否正确配对出现的算法(已知表达式已存入数据元素
为字符的顺序表中)。
实现下列函数:
Status MatchCheck(SqList exp);
/* 顺序表exp表示表达式; */
/* 若exp中的括号配对,则返回TRUE,否则返回FALSE */
顺序表类型定义如下:
typedef struct {
ElemType *elem;
int length;
int listsize;
} SqList; // 顺序表
Stack是一个已实现的栈。
可使用的相关类型和函数:
typedef char SElemType; // 栈Stack的元素类型
Status InitStack(Stack &s);
Status Push(Stack &s, SElemType e);
Status Pop(Stack &s, SElemType &e);
Status StackEmpty(Stack s);
Status GetTop(Stack s, SElemType &e);
Status MatchCheck(SqList exp) /* 顺序表exp表示表达式; */ /* 若exp中的括号配对,则返回TRUE,否则返回FALSE */ { ElemType *cur=exp.elem; SElemType temp; SqStack s; InitStack(s); if(0==exp.length){return 1;} while(cur<=exp.elem+exp.length-1){ if('['==*cur||'('==*cur||'{'==*cur){ Push(s,*cur); } else{ GetTop(s,temp); if(StackEmpty(s)){//粗心,栈空时返回的是1而不是0 return 0; } else if(']'==*cur&&'['==temp){ Pop(s,temp); } else if(')'==*cur&&'('==temp){ Pop(s,temp); } else if('}'==*cur&&'{'==temp){ Pop(s,temp); } } ++cur; } if(StackEmpty(s))return 1;//粗心,栈空时返回的是1而不是0 return 0; }
3.20③ 假设以二维数组g(1..m,1..n)表示一个图像
区域,g[i,j]表示该区域中点(i,j)所具颜色,其值
为从0到k的整数。编写算法置换点(i0,j0)所在区域
的颜色。约定和(i0,j0)同色的上、下、左、右的邻
接点为同色区域的点。
实现下列函数:
void ChangeColor(GTYPE g, int m, int n,
char c, int i0, int j0);
/* 在g[1..m][1..n]中,将元素g[i0][j0] */
/* 所在的同色区域的颜色置换为颜色c */
表示图像区域的类型定义如下:
typedef char GTYPE[m+1][n+1];
Stack是一个已实现的栈。
可使用的相关类型和函数:
typedef int SElemType; // 栈Stack的元素类型
Status StackInit(Stack &s, int initsize);
Status Push(Stack &s, SElemType e);
Status Pop(Stack &s, SElemType &e);
Status StackEmpty(Stack s);
Status GetTop(Stack s, SElemType &e);
void ChangeColor(GTYPE g, int m, int n, char c, int i0, int j0) /* 在g[1..m][1..n]中,将元素g[i0][j0] */ /* 所在的同色区域的颜色置换为颜色c */ /* 说明:di=1,2,3,4分别为东南西北四个方向,表示的是下一位置在当前位置的哪 个方向! 因为数组的下标的性质,只要知道di就能够退回到上一个位置, 而如果第一个元素四周都没有同色元素之后(栈空),结束程序 */ { SqStack S; InitStack(S); int x,y,di; char color; if(i0<=0||i0>m||j0<=0||j0>n){ exit(OVERFLOW); } x=i0; y=j0; color=g[x][y]; do{ if(x>0&&y>0&&x<=m&&y<=n&&color==g[x][y]){ di=1;//设置方向为东 g[x][y]=c; Push(S,di); y=y+1;//当前位置切换为东边的元素 } else{ Pop(S,di); switch(di){//当前位置非同色或不合法,设置当前位置为上一位置 (回退) case 1:--y;break; case 2:--x;break; case 3:++y;break; case 4:++x;break; } ++di; if(di<=4){//若di>4,相当于只回退 switch(di){//切换当前位置到下一位置 case 1:++y;break;//东临 case 2:++x;break;//南 case 3:--y;break;//西 case 4:--x;break;//北 } Push(S,di); } } }while(!StackEmpty(S)); }
◆3.21③ 假设表达式由单字母变量和双目四则运
算算符构成。试写一个算法,将一个通常书写形式
且书写正确的表达式转换为逆波兰式。
实现下列函数:
char *RPExpression(char *e);
/* 返回表达式e的逆波兰式 */
Stack是一个已实现的栈。
可使用的相关类型和函数:
typedef char SElemType; // 栈Stack的元素类型
Status InitStack(Stack &s);
Status Push(Stack &s, SElemType e);
Status Pop(Stack &s, SElemType &e);
Status StackEmpty(Stack s);
SElemType Top(Stack s);
char *RPExpression(char *e) /* 返回表达式e的逆波兰式 */ //由于个人水平,折腾了很就才通过 //主要思路如下: //1 建立优先级表,符号表,栈中元素对应符号 //2 依此读取字符,判断是运算符还是数字,如果数字直接入后缀表达式栈, //如果是运算符,则判断与栈顶元素优先级关系,进行响应操作 /* 容易犯错的地方 1 优先级关系不正确 2 当*p优先级低于运算符栈顶时,出栈,但忘了继续比较 3 忘了字符串长度应该为length+1,因为最后'\0' 变量说明 OPTR 运算符栈 rpe 后缀表达式栈 opera 数组为运算符表 list 数组为优先级表 pre_optr 记录运算符栈元素的代表数字 */ { int i=0,j=0,op1,op2,pre=2,count=0; char *p=e,*head,temp,s[100]; Stack OPTR,rpe; int pre_optr[10]; char opera[7]={'+','-','*','/','(',')','#'}; int list[7][7]={{1,1,-1,-1,-1,1,1} ,{1,1,-1,-1,-1,1,1} ,{1,1,1,1,-1,1,1} ,{1,1,1,1,-1,1,1} ,{-1,-1,-1,-1,-1,0,-2} ,{1,1,1,1,-2,1,1} ,{-1,-1,-1,-1,-1,-2,0}}; InitStack(OPTR); InitStack(rpe); while(*p){ //计算出转换后表达式的长度 if('('!=*p&&')'!=*p){ ++count; } ++p; } p=e; Push(OPTR,'#'); pre_optr[j]=6; while(*p){//依此读入字符,进行相应处理 for(i=0;i<7;++i){ if(opera[i]==*p){ break; } } op2=i; if(7==op2){//判断是否为数字 Push(rpe,*p); } else{ op1=pre_optr[j]; pre=list[op1][op2]; if(-2==pre){ return ERROR; } else if(1==pre){//优先级大于*p if(')'!=*p){ while(1==list[op1][op2]){ Pop(OPTR,temp); --j; Push(rpe,temp); op1=pre_optr[j]; } pre_optr[++j]=op2;//记录栈顶符号 Push(OPTR,*p); } else{//如果为右括号 while(Top(OPTR)!='('){ Pop(OPTR,temp); Push(rpe,temp); --j; } Pop(OPTR,temp); --j; } } else if(0==pre){//等于 Pop(OPTR,temp); } else if(-1==pre){//小于 Push(OPTR,*p); pre_optr[++j]=op2;//记录栈顶符号 } } ++p; }//运行到串尾; while(Top(OPTR)!='#'){ Pop(OPTR,temp); Push(rpe,temp); }//符号栈中所有元素出栈----转换完成 while(!StackEmpty(rpe)){//栈元素逆转---- Pop(rpe,temp); Push(OPTR,temp); } p=head=(char *)malloc(count+1); while('#'!=Top(OPTR)){//将栈元素写入字符串 Pop(OPTR,temp); *p=temp; ++p; } *p='\0'; return head; }
3.24③ 试编写如下定义的递归函数的递归算法:
g(m,n) = 0 当m=0,n>=0
g(m,n) = g(m-1,2n)+n 当m>0,n>=0
并根据算法画出求g(5,2)时栈的变化过程。
实现下列函数:
int G(int m, int n) /* if m<0 or n<0 then return -1. */ { if(m<0||n<0){ return -1; } if(0==m){ return 0; } else { return G(m-1,2*n)+n; //刚开始竟然很白痴地写成G(m-1,2n) - -|| } }
3.25④ 试写出求递归函数F(n)的递归算法,
并消除递归:
F(n) = n+1 当n=0
F(n) = nF(n/2) 当n>0
实现下列函数:
int F(int n) /* if n<0 then return -1. */ { if(n<0){ return -1; } if(0==n){ return 1; } else{ return n*F(n/2); } }
◆3.28② 假设以带头结点的循环链表表示队列,并且
只设一个指针指向队尾元素结点(注意不设头指针),
试编写相应的队列初始化、入队列和出队列的算法。
实现下列函数:
Status InitCLQueue(CLQueue &rear);
Status EnCLQueue(CLQueue &rear, ElemType x);
Status DeCLQueue(CLQueue &rear, ElemType &x);
带头结点循环链队列CLQueue的类型为以下LinkList类型:
typedef struct LNode{
ElemType data;
struct LNode *next;
} LNode, *LinkList;
typedef LinkList CLQueue;
Status InitCLQueue(CLQueue &rear) { rear=(CLQueue)malloc(sizeof(LNode)); if(!rear){ return FALSE; } rear->next=rear; return TRUE; } Status EnCLQueue(CLQueue &rear, ElemType x) { CLQueue head,p; head=rear->next; p=(CLQueue)malloc(sizeof(LNode)); if(!p){ return FALSE; } p->data=x; p->next=head; rear->next=p; rear=p; return OK; } Status DeCLQueue(CLQueue &rear, ElemType &x) { CLQueue head,p; head=rear->next; if(rear==rear->next){//这里粗心了,忘记考虑队列空的情况 return ERROR; } p=head->next; x=p->data; head->next=p->next; free(p); return OK; }
3.29③ 如果希望循环队列中的元素都能得到利用,
则需设置一个标志域tag,并以tag的值为0或1来区
分,尾指针和头指针值相同时的队列状态是”空”还
是”满”。试编写与此结构相应的入队列和出队列的
算法,并从时间和空间角度讨论设标志和不设标志
这两种方法的使用范围(比如,当循环队列容量较
小而队列中每个元素占的空间较多时,那一种方法
较好?)。
实现下列函数:
Status EnCQueue(CTagQueue &Q, QElemType x);
Status DeCQueue(CTagQueue &Q, QElemType &x);
本题的循环队列CTagQueue的类型定义如下:
typedef char QElemType;
typedef struct {
QElemType elem[MAXQSIZE];
int tag;
int front;
int rear;
} CTagQueue;
Status EnCQueue(CTagQueue &Q, QElemType x) { if(Q.tag){ return ERROR; } Q.elem[Q.rear]=x; if(Q.rear==MAXQSIZE-1){ Q.rear=0; } else{ ++Q.rear; } if(Q.rear==Q.front){ Q.tag=1; } return OK; } Status DeCQueue(CTagQueue &Q, QElemType &x) { if(Q.front==Q.rear&&0==Q.tag){ return ERROR; } else{ x=Q.elem[Q.front]; if(Q.front!=MAXQSIZE-1){ ++Q.front; } else{ Q.front=0; } if(Q.front==Q.rear){ Q.tag=0; } } return OK; }
◆3.30② 假设将循环队列定义为:以域变量rear
和length分别指示循环队列中队尾元素的位置和内
含元素的个数。试给出此循环队列的队满条件,并
写出相应的入队列和出队列的算法(在出队列的算
法中要返回队头元素)。
实现下列函数:
Status EnCQueue(CLenQueue &Q, QElemType x);
Status DeCQueue(CLenQueue &Q, QElemType &x);
本题的循环队列CLenQueue的类型定义如下:
typedef char QElemType;
typedef struct {
QElemType elem[MAXQSIZE];
int length;
int rear;
} CLenQueue;
Status EnCQueue(CLenQueue &Q, QElemType x) { if(MAXQSIZE==Q.length){ return ERROR; } if(MAXQSIZE-1!=Q.rear){ ++Q.rear; Q.elem[Q.rear]=x; } else{ Q.rear=0; Q.elem[Q.rear]=x; } ++Q.length; return OK; } Status DeCQueue(CLenQueue &Q, QElemType &x) { if(!Q.length){ return ERROR; } if(Q.rear+1>=Q.length){ x=Q.elem[Q.rear+1-Q.length]; } else{ x=Q.elem[MAXQSIZE-Q.length+Q.rear+1]; } --Q.length; return OK; }
◆3.31③ 假设称正读和反读都相同的字符序列为
“回文”,例如,’abba’和’abcba’是回文,’abcde’
和’ababab’则不是回文。试写一个算法判别读入的
一个以’@'为结束符的字符序列是否是”回文”。
实现下列函数:
Status Palindrome(char *word);
/* 利用栈和队列判定字符序列word是否是回文 */
可使用栈Stack和队列Queue及其下列操作:
Status InitStack(Stack &S);
Status Push(Stack &S, ElemType x);
Status Pop(Stack &S, ElemType &x);
Status StackEmpty(Stack S);
Status InitQueue(Queue &Q);
Status EnQueue(Queue &Q, ElemType x);
Status DeQueue(Queue &Q, ElemType &x);
Status QueueEmpty(Queue Q);
Status Palindrome(char *word) /* 利用栈和队列判定字符序列word是否是回文 */ { Stack S; char temp; InitStack(S); char *p=word,*q=word; while(*p!='@'){ Push(S,*p); ++p; } while(!StackEmpty(S)){ Pop(S,temp); if(temp!=*q){ break; } ++q; } if(StackEmpty(S)){ return TRUE; } return FALSE; }