唐老師資料結構 專題四的筆記
首先是寫棧的習慣:一定要在建立完棧後 就立即寫上銷燬棧的函式 否則很容易遺忘銷燬 導致記憶體洩露
在唐老師講的符號配對檢測的程式時 有一個小細節 老師並沒有提出來 但是後來發現了 :
char* c = (char*)LinkStack_Pop(stack); //進行檢測 格外注意 一定要先c==NULL if( (c == NULL) || !match(*c,code[i]) ) { printf("is error"); ret = 0; break; }
這裡進行了標紅 就是先從堆疊裡取出了棧頂元素 然後進行判斷 這裡先進性了判斷c是否是NULL ,因為如果要是先呼叫MATCH函式 當*c 的時候 可能會由於c的指向地址是NULL
所以導致不發找到該地址 最終導致程式崩潰;
然後是計算器的執行:人類習慣的運算式是中綴表示式例如: 3+5、3+4+4、3*(3+3)*4;
而機器習慣的運算表示式是:35+,34+4+;等等
以下是唐老師給出的例子:
中綴表示式:字尾表示式:
5 + 3 =5 3 +
1 + 2 * 3
= 1 2 3 * +
9 + ( 3 – 1 ) * 5= 9 3 1 – 5 * +
而如何將人們習慣的中綴表示式轉成字尾表示式呢??這個老師給出了答案:
解決方案:
遍歷中綴表示式中的數字和符號對於數字:直接輸出 直接輸出
對於符號:
• 左括號 :進棧 進棧
• 符號 :與棧頂符號進行優先順序比較 與棧頂符號進行優先順序比較
• 棧頂符號優先順序低
• 棧頂符號優先順序不低:將棧頂符號彈出並輸出 將棧頂符號彈出並輸出, ,之後進棧 之後進棧
• 右括號:將棧頂符號彈出並輸出 將棧頂符號彈出並輸出, ,直到匹配左括號 直到匹配左括號
遍歷結束:將棧中的所有符號彈出並輸出 將棧中的所有符號彈出並輸出
而計算機又是如何對字尾表示式進行運算的呢??這個老師也給出了答案:
解決方案
遍歷字尾表示式中的數字和符號對於數字:進棧 進棧
對於符號:
• 從棧中彈出右運算元(記住先彈出的是有運算元)
• 從棧中彈出左運算元(後彈出的是左運算元)
• 根據符號進行運算
• 將運算結果壓入棧中
遍歷結束:棧中的唯一數字為計算結果
對於順序連結串列唐老師用的是連結串列的尾作為棧頂,而普通的連結串列做棧是用的是表頭作為棧頂;
我覺得對於順序連結串列表尾作為棧頂很正確,畢竟順序連結串列是陣列,如果以表頭作為棧頂,在進行插入的時候,要將所有資料都進行後移,這樣過於浪費執行時間
而用普通的連結串列作為棧時,用表頭或者標尾作為棧頂就沒有什麼區別,用表頭作為棧頂是,不論是取元素還是刪除元素時,直接將函式中需要操作的節點位置寫零即可,而標尾還要檢測長度;
對於第四專題四的課後習題裡唐老師讓將整個計算器的演算法不全,可以識別兩位數還有能夠自己進行中綴轉字尾,然後進行字尾計算;
這裡就需要設定兩個棧了:1:符號棧 2:數字棧
當為數字是,直接進入數字棧,而為符號時,就進入符號棧
而進入符號棧時,什麼情況入棧,什麼情況出棧和中綴轉字尾的方法一樣,當符號要進行出棧時,數字棧就要同時進行左右操作時出棧,然後進行運算,再將結果數字棧中;
這樣就可以實現中綴轉字尾,字尾進行計算了;
但這裡有一個小細節就是當數字入棧是一定要先把字元的數字轉成數字int型 然後在進行壓棧,否則如果在後面的計算結果進行壓棧時,如果結果是兩位數,然麼用字元表示就會出錯,然後也沒法將兩位數轉成數字(因為不確定生成的是幾位數)所以最好的方法就是在數字壓棧時就直接轉成int型的數字然後壓棧,就不會涉及到結果是幾位數了;
//判斷是否是數字
int isnumber(char c)
{
return ( c >= '0' ) && ( c <= '9' );
}
//輸出字元函式
void output(char c)
{
printf("%c",c);
}
//判斷是否是左括號
int isLeft(char c)
{
return (c == '(');
}
//判斷是否是右括號
int isRight(char c)
{
return (c == ')');
}
//判斷是否是操作符
int isOperator(char c)
{
return ( c == '+' ) || ( c == '-' ) || ( c == '*' ) || ( c == '/' );
}
//判斷操作符的優先順序
int priority(char c)
{
int ret = 0;
if( (c == '+') || (c == '-') )
{
ret = 1;
}
if( (c == '*') || (c == '/') )
{
ret = 2;
}
return ret;
}
//字元轉成數字
int value( char c )
{
return ( c - '0' );
}
int express(int left, int right, char op)
{
int ret = 0;
switch(op)
{
case '+':
ret = left + right;
break;
case '-':
ret = left - right;
break;
case '*':
ret = left * right;
break;
case '/':
ret = left / right;
break;
default:
break;
}
return ret;
}
//轉字尾與字尾計算結合體
int arithmic(const char* exp)
{
//儲存數字的棧
LinkStack* num = LinkStack_Creat();
//儲存運算子的棧
LinkStack* ope = LinkStack_Creat();
int i = 0;
int right = 0; //讀取數字棧時 儲存右值
int left = 0; //讀取數字棧時 儲存右值
int result = 0;
char name = '\0'; //存放符號棧出棧時的符號
int ret = 0;
while( exp[i] != '\0')
{
// 是數字
if( isnumber(exp[i]) )
{
int tem = 0;
int r = 0;
int q = 0;
while( isnumber(exp[i + r]))//這裡的迴圈是實現多位數字的運算
{
for(q=0; q<r; q++)
{
tem*=10;
}
tem += value(exp[i+r]);
r++;
}
i+=(r-1);
LinkStack_Push(num,(void*)tem);
// LinkStack_Push(num,(void*)value(exp[i]));
}
//是左括號
else if( isLeft(exp[i]) )
{
LinkStack_Push(ope,(void*)(int)exp[i]);
}
//是右括號
else if( isRight(exp[i]))
{
while( !isLeft( (char)(int)LinkStack_Top(ope) ) )
{
right = (int)LinkStack_Pop(num);
left = (int)LinkStack_Pop(num);
name = (char)(int)LinkStack_Pop(ope);
result = express(left,right,name);
printf("result=%d\n",result);
LinkStack_Push(num,(void*)result);
// LinkStack_Push(num,(void*)result);
}
//將左括號 推出棧
LinkStack_Pop(ope);
}
//是操作符
else if( isOperator(exp[i]) )
{
while( priority(exp[i]) <= priority((char)(int)LinkStack_Top(ope)) )
{
right = (int)LinkStack_Pop(num);
// printf("right:%d\n",right);
left = (int)LinkStack_Pop(num);
// printf("left:%d\n",left);
name = (char)(int)LinkStack_Pop(ope);
result = express(left,right,name);
// printf("left:%d\n",result);
LinkStack_Push(num,(void*)result);
}
LinkStack_Push(ope,(void*)(int)exp[i]);
}
else
{
printf("is error");
break;
}
i++;
}
//當字串掃描完畢後
if( LinkStack_Size(num)>0 && exp[i]=='\0' )
{
while( LinkStack_Size(ope) > 0 )
{
printf("size=%d\n",LinkStack_Size(num));
right = (int)LinkStack_Pop(num);
printf("right:%d\n",right);
left = (int)LinkStack_Pop(num);
printf("left:%d\n",left);
name = (char)(int)LinkStack_Pop(ope);
printf("%c\n",name);
result = express(left,right,name);
LinkStack_Push(num,(void*)(result));
// LinkStack_Push(num,(void*)result);
}
if( LinkStack_Size(num) > 1)
{
printf("is error");
// break;
}
ret = (int)LinkStack_Pop(num);
}
//一定要記得銷燬棧
LinkStack_Destroy(num);
LinkStack_Destroy(ope);
return ret;
}