Data Structures (Stacks)
Data Structures (Stacks)
一. 何謂堆疊(Stacks)?
z 加入與刪除資料只在頂端(top)進行
加 入 p u sh 刪 除 p o p
頂 端 to p
資 料 n
資 料 2
資 料 1
堆 疊 = ( 資 料 1 , 資 料 2 , … .., 資 料 n )
二. 以陣列製作堆疊
最簡單之方法乃利用一維陣列。下面為一陣列堆疊宣告之例子;
N --1 N --1
p+1 Top=p+1
d
Top = p p
1
2 2
1 1
0 0
圖 一 : 加 入 資 料 於 陣 列 堆 疊 中
P o p 時 , 原 to p 值
為 p , 傳 回
N -1 N -1
s t a c k [ to p ] 後 , to p
值 減 1, 則 頂 端 位
Top =p p
置 to p = p - 1
p -1 T op = p -1
1
2 2
1 1
0 0
圖二:刪除堆疊頂端資料
因此刪除堆疊頂端資料之 pop()函數可簡述如下
1. 檢查堆疊是否空了,若是空堆疊則刪除失敗。
2. 否則傳回頂端資料後將 top 值減 1 ,即 top 向下移一格。
/**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
/*****************************************************************
* push 函式部分 ( 加入資料於堆疊內 )
*****************************************************************/
void push(int d) /*加入資料於堆疊內*/
{
if(top == N-1) {
printf("堆疊滿了\n"); /* 注意堆疊大小 */
exit(1); /* 加入失敗,執行結束 */
} /* end if */
stack[++top]=d;
} /* end of push 函數 */
/*****************************************************************
* pop 函式部分 (刪除堆疊的頂端資料) *
*****************************************************************/
int pop()
{
if(top == -1) /* 注意空堆疊情形*/
{ printf("堆疊空了\n");
exit(1); /*刪除失敗,執行結束 */
}
Data Structure: Stacks 3
return(stack[top--]);
}
/*****************************************************************
* 主程式部分
*****************************************************************/
void main()
{
int d, loop=1; /*loop 表迴圈控制變數*/
char input[5];
clrscr(); /*清除螢幕畫面*/
printf("*** 陣列堆疊 ***\n");
printf("可執行下列指令 : push, pop, +, -, exit\n\n");
do {
printf("==>");
scanf("%s",input);
if(strcmp(input,"push")==0) /*字串比較若相等則傳回之值*/
/* 此時作 push 動作 */
{ printf("輸入數值:");
scanf("%d",&d);
push(d);
} /* end of if(strcmp(…..)指令 */
指標形式亦可用來描述堆疊。
優點:在同一程式中可以供多種不同的堆疊使用,且避免全域變數之使用,
缺點:函數之呼叫變得更複雜。
/*****************************************************************
*****************************************************************/
{
(*top)++; /*指標指向頂端,增量增加 1*/
stack[*top]=d; /*儲存資料 d 於堆疊頂端 */
} /* end of push 函數 */
/*****************************************************************
*****************************************************************/
{ int d;
d = stack[*top];
(*top)--; /*減量減少 1*/
return(d);
}
統會以堆疊來儲存返回(return)之位址,當副程式 Z 做完後,會由堆疊彈回副
…. ….. ……
Call Y Call Z ……
Statement A Statement B …..
….. …. ….. Statement B 位址
return return Statement A 位址
五. 堆疊之應用二:代數運算式的求值計算
1. 運算式之組成
一個運算式(expression) 是由運算元(operand)、運算子(operator)及間
z 運算元(operand);0,1,2,3,….etc.
z 運算子(operator) ;+,-,*,/,**,<,<=, >=, ++, !=, ++, --, =, +=, ….. etc.
z 間隔符號(delimiter) ;(,)
1. 結合性由左而右(left to right),則由左而右執行。如:(+)、(-)
2. 結合性由右而左(right to left),則由右而左執行。如:次方($)
缺點:電腦無法一次依序讀取運算式。因運算式可能含有括號,且
運算子優先順序不同
p.s 此法又稱為反波蘭記號法;RPN。其優點在於不必使用括號,且
運算子不具優先權
規則如下:
z 由左而右讀進後序運算式的每個字元(Token)
z 判別字元,若為運算元(operand),則將其放入堆疊中。
z 判別字元,若為運算子(operand),則自堆疊中取出適當個數之運算元,
(單元運算子,取一個運算元;二元運算子,取兩個運算元),並執行運
算子所對應之計算,並將計算結果放入堆疊中。
z 若字串結束(end of string),堆疊內唯一值即為所要結果。
圖二; 常見運算子
Start
Initialize Stack
no
Uinary Binary
Pop 1 operand Binary or Unary? Pop 2 operands
Perform operation Perform operations
Push results to stack Push results to stack
圖三;後序運算式之流程圖
+ )之值
運算元)
# include <stdio.h>
# include <math.h>
# include <ctype.h>
# include <stdlib.h>
/* 陣列堆疊宣告 */
#define N 100
int stack[N];
/**********************************************************************
* push 函式部分 ( stack[] 宣告為全域變數 )
*****************************************************************/
{
if (*top == N-1)
{
printf("堆疊滿了\n"); /* 注意堆疊大小 */
exit(1); /* exit 定義在 stdlib.h 中 */
/* exit(0)表示正常,exit(1) 表示有誤*/
} /* end if */
else
{
(*top)++; /*指標指向頂端,增量增加 1*/
stack[*top]=d; /*儲存資料 d 於堆疊頂端 */
} /* end else */
} /* end of push 函數 */
{
if (*top == -1) /* 注意空堆疊情形*/
{ printf("堆疊空了\n");
exit(1); /*刪除失敗,執行結束 */
}
else
return(stack[(*top)--]);
}
/***************************************************************************
* oper 函數 ( 檢查有效二元運算子,並計算該運算運用在其後兩參數之結果) *
***************************************************************************/
/*****************************************************************
* eva_postfix 函數 (計算後序運算式值)
*****************************************************************/
double eval_postfix(char expr[])
{
/*****************************************************************
* 主程式部分
*****************************************************************/
void main()
{
char expr[N];
int position = 0;
expr[--position] = ‘\0’;
printf (“\n %s %s “, “ the original postfix expression is “, expr);
/* 列印原始後序運算式 */
printf(“ \n %f”, eval_postfix(expr));
/* 列印計算後之計算值 */
} /* end main*/
方法一:
算術運算式由中序變為後序可依下列三步驟進行:
1. 將式子中的運算單元適當的加以括號,此時須考慮運算子的運算優先順序。
2. 將所有的運算子移到其對應的右括號。
3. 將所有的括號去掉。
如將 A*B/C 化為後序表示式
(1) ( ( A * B ) / C)
(2) ( ( A * B ) / C) =>( ( A B ) * C ) /
(3) AB*C/
(1) ( ( (A- ( B / C ) ) + ( D * E ) ) - ( F % G ) )
(2) ( ( (A- ( B / C ) ) + ( D * E ) ) - ( F % G ) )
(3) ABC/-DE*+FG%-
Start
Initialize Stack
yes Print A
Operand?
no
yes Pop from Stack Stop
End of String?
until empty
no
yes
‘(‘ ? Push this token
no
yes Pop A from
‘)’ ?
stack and print
no A until ‘(‘
yes
ICP>ISP? Push A to Stack
no
Pop from Stack
圖四;中序運算式轉換為後序運算式之流程圖
規則如下:
z 由左而右讀進中序運算式的每個字元(Token)
z 判別字元,若為運算元(operand),則直接輸出。
將堆疊中之運算子自頂端逐一取出並輸出,直到堆疊頂端之運算子
取出對應之”(”為止,但”(”不須輸出。注意: ”)”永遠不會被放入堆
疊中。
出並輸出,值到堆疊空了。
(( A - ( B + C ))*D)$(E+F)
1 ( (
2 ( ((
3 A A ((
4 - A ((-
5 ( A ((-(
6 B AB ((-(
7 + AB ((-(+
8 C ABC ((-(+
9 ) ABC+ ((-
10 ) ABC+- (
11 * ABC+- (*
12 D ABC+-D (*
13 ) ABC+-D*
14 $ ABC+-D* $
15 ( ABC+-D* $(
16 E ABC+-D*E $(
17 + ABC+-D*E $(+
18 F ABC+-D*EF $(+
19 ) ABC+-D*EF+ $
20 ABC+-D*EF+$
precedence stack[N];
{
switch (token) /* 將讀進之 Token 量化並分類 */
{ case '(' : return left_paren; /* 此時 '(' 等於 left_paren = 0 */
case ')' : return right_paren; /* 此時 ')'等於 right_paren = 1 */
case '+' : return plus; /* 依此類推 */
case '-' : return minus;
case '*' : return times;
case '/' : return divide;
case '%' : return mode;
default : return operand;
} /* end switch*/
} /* end get_token 函數 */
/* 【infix_to_postfix 函數 】 (將中序式轉換為計後序運算式) */
{
int position=0; /* 目前讀取位元之位置 */
char c; /* 讀取一個字元 */
precedence token; /* 分類後之 token */
top = -1; /* initialize stack */
case right_paren :
while(stack[top] != left_paren)
/* 自堆疊頂端逐一取出並輸出,直到取出 '(' 為止 */
{
print_symbol(stack[top]);
pop(&top); /* 移除堆疊頂端之'(', 不須印出 */
} /* end while */
break;
default:
if (empty(&top)) /* empty stack, push the token directly */
push(token, &top);
else
{
while ((!empty(&top)) && (ISP[stack[top]] >= ICP[token]))
print_symbol(pop(&top));
push(token, &top); /* push token to stack */
} /* end else */
} /* end switch */
} /* end for */
} /* end infix_to_postfix */
a*(b+c/d–e)*f
\0
【習題】
1. 將下列中序運算式轉換後序式 2. 將下列後序運算式轉為中序式
(a.) a + ( b – c / d) * e (a.) a b + c-
(b.) (a - b + c ) / ( d – e / f * g) (b.) a b c - *
(c.) (a - b ) - ( c + d ) $ e * f (c.) a b – c + d e f - + $
(d.) (a - b ) $ (c * ( d – e ) + f) – g (d.) a b c d e - + $ * f g * -
3. 求下列後序式之值 4. 將下列中序運算式轉換前序式
(a.) 1 5 + 3 – 4 1 + 2 $ - (a.) a + ( b – c / d) * e
(b.) 6 2 4 + * 2 1 6 5 - + *\ (b.) (a - b + c ) / ( d – e / f * g)
(c.) (a - b ) - ( c + d ) $ e * f
(d.) (a - b ) $ (c * ( d – e ) + f) – g