◆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; }
