Transcript ch04堆疊與佇列
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔, 特別摘錄重點提供給您授課專用。 說明: 1、本教具為非賣品,不得作為商業之用。 2、本教具僅授權使用原著作為授課教材之教師作為教學或研究等學術用途。 3、本教具未授權提供學生任何拷貝、影印、引用、翻印等行為。 4、教師若需申請網站或內容授權,可透過您的博碩業務協助處理,謝謝。 博碩文化: 總公司:台北縣汐止市新台五路一段94號6樓A棟 電話:(02) 2696-2869 分機 313 傳真:(02) 2696-2867 網址:www.drmaster.com.tw 客服信箱:[email protected] 出書提案信箱 [email protected] 資料結構 請老師填入姓名主講 課本:圖解資料結構 博碩文化出版發行 第四章 堆疊與佇列 課前指引 資料結構學科中,堆疊(Stack)與佇列是兩種相當典型的抽象資 料型態,也是一種有特定進出規則的線性串列應用,主要特性 是限制了資料插入與刪除的位置和方法。本章中將會介紹堆疊 與佇列的概念與特性,及在計算機領域的各種相關應用。 章節大綱 4-1 堆疊簡介 4-2 算術運算式的表示法 4-3 佇列 備註:可依進度點選小節 4-1 堆疊簡介 堆疊(Stack) 是一群相同資料型態的組合,並擁有後進先出 (Last In,Frist Out)的特性,所有的動作均在堆疊 頂端進行。 5 4-1 堆疊簡介 堆疊在計算機領域的應用 包括遞迴的呼叫及返回、二元樹及森林的走訪 運算、呼叫副程式及返回處理、算術式的轉換 和求值、中央處理單元(CPU)的中斷處理 (Interrupt Handling)與所謂的堆疊計算機(Stack Computer)等等。 6 4-1 堆疊簡介 堆疊基本工作運算 在程式設計中該如何製作一個堆疊呢?因為它 只是一種抽象資料型態,不論用陣列或鏈結串 列都可以,最重要是要有「後進先出」的精神 ,並符合五種基本工作運算: 7 4-1 堆疊簡介 陣列實作堆疊 以陣列結構來製作堆疊的好處是製作與設計的 演算法都相當簡單,但因為如果堆疊本身是變 動的話,陣列大小並無法事先規劃宣告,太大 時浪費空間,太小則不夠使用。 C的相關演算法如下: int isEmpty() /*判斷堆疊是否為空堆疊 */ { if(top==-1) return 1; else return 0; } 8 4-1 堆疊簡介 int push(int data) /* 存放頂端資料,並傳回新堆疊 */ { if(top>=MAXSTACK) { printf("堆疊已滿,無法再加入\n"); return 0; } else { stack[++top]=data; /*將資料存入堆疊*/ return 1; } } int pop() { if(isEmpty()) /*判斷堆疊是否為空,如果是則傳回-1*/ return -1; else return stack[top--]; /*將資料取出後,再將堆疊指標往下移*/ } 9 #include <stdio.h> #include <stdlib.h> #define MAXSTACK 100 /*定義最大堆疊容量*/ int stack[MAXSTACK];/*堆疊的陣列宣告*/ int top=-1;/*堆疊的頂端*/ /*判斷是否為空堆疊*/ int isEmpty() { if(top==-1) return 1; else return 0; } /*將指定的資料存入堆疊*/ int push(int data) { if(top>=MAXSTACK) { printf("堆疊已滿,無法再加入\n"); return 0; } else { stack[++top]=data; /*將資料存入堆疊*/ return 1; } } /*從堆疊取出資料*/ 4-1 堆疊簡介 範例 4.1.1 請利用陣列結構來設計一C程式,利用迴圈來控 制準備推入或取出的元素,並模擬堆疊的各種 工作運算,此堆疊最多可容納100個元素,其中 必須包括推入(push)與彈出(pop)函數,及最後 輸出所有堆疊內的元素。 10 int pop() { if(isEmpty()) /*判斷堆疊是否為空,如果是則傳回-1*/ return -1; else return stack[top--]; /*將資料取出後,再將堆疊指標往下移*/ } /*主程式*/ int main() { int value; int i; do { printf("要推入堆疊,請輸入1,彈出則輸入0,停止操作則輸入-1: "); scanf("%d",&i); if(i==-1) break; else if (i==1) { printf("請輸入元素值:"); scanf("%d",&value); push(value); } else if(i==0) printf("彈出的元素為%d\n",pop()); 4-1 堆疊簡介 11 } while(i!=-1); printf("============================\n"); while(!isEmpty()) /*將資料陸續從頂端彈出*/ printf("堆疊彈出的順序為:%d\n",pop()); printf("==========================\n"); system("pause"); return 0; } 4-1 堆疊簡介 12 4-1 堆疊簡介 串列實作堆疊 鍵結串列來製作堆疊的優點是隨時可以動態改 變串列長度,能有效利用記憶體資源,不過缺 點是設計時,演算法較為複雜。 C的相關演算法如下: struct Node /*堆疊鏈結節點的宣告*/ { int data; /*堆疊資料的宣告*/ struct Node *next;/*堆疊中用來指向下一個節點*/ }; int isEmpty()/*判斷是否為空堆疊*/ { if(top==NULL) return 1; else return 0; } 13 4-1 堆疊簡介 void push(int data) /*將指定的資料存入堆疊*/ { Linked_Stack new_add_node; /*新加入節點的指標*/ / *配置新節點的記憶體*/ new_add_node=(Linked_Stack)malloc(sizeof(Stack_Node)); new_add_node->data=data;/*將傳入的值指定為節點的內容*/ new_add_node->next=top;/*將新節點指向堆疊的頂端*/ top=new_add_node;/*新節點成為堆疊的頂端*/ } int pop()/*從堆疊彈出資料*/ { Linked_Stack ptr; /*指向堆疊頂端的指標*/ int temp; if(isEmpty()) /*判斷堆疊是否為空,如果是則傳回-1*/ { printf("===目前為空堆疊===\n"); return -1; } else 14 4-1 堆疊簡介 { ptr=top;/*指向堆疊的頂端*/ top=top->next;/*將堆疊頂端的指標指向下一個節點*/ temp=ptr->data;/*彈出堆疊的資料*/ free(ptr);/*將節點佔用的記憶體釋放*/ return temp;/*將從堆疊取出的資料回傳給主程式*/ } } 15 #include <stdio.h> #include <stdlib.h> struct Node /*堆疊鏈結節點的宣告*/ { int data; /*堆疊資料的宣告*/ struct Node *next;/*堆疊中用來指向下一個節點*/ }; typedef struct Node Stack_Node;/*定義堆疊中節點的新型態*/ typedef Stack_Node *Linked_Stack;/*定義串列堆疊的新型態*/ Linked_Stack top=NULL;/*指向堆疊頂端的指標*/ int isEmpty(); int pop(); void push(int data); /*判斷是否為空堆疊*/ /*主程式*/ int main() { int value; int i; do { printf("要推入堆疊,請輸入1,彈出則輸入0,停止操作則輸入-1: "); scanf("%d",&i); if(i==-1) break; 4-1 堆疊簡介 範例 4.1.2 請利用串列結構來設計一C程式,利用迴圈來控 制準備推入或取出的元素,其中必須包括推入 (push)與彈出(pop)函數,及最後輸出所有堆疊 內的元素。 16 else if (i==1) { printf("請輸入元素值:"); scanf("%d",&value); push(value); } else if(i==0) printf("彈出的元素為%d\n",pop()); } while(i!=-1); printf("============================\n"); while(!isEmpty()) /*將資料陸續從頂端彈出*/ printf("堆疊彈出的順序為:%d\n",pop()); printf("==========================\n"); system("pause"); return 0; 4-1 堆疊簡介 } int isEmpty() { if(top==NULL) return 1; else return 0; } /*將指定的資料存入堆疊*/ void push(int data) { Linked_Stack new_add_node; /*新加入節點的指標*/ 17 /*配置新節點的記憶體*/ new_add_node=(Linked_Stack)malloc(sizeof(Stack_Node)); new_add_node->data=data;/*將傳入的值指定為節點的內容*/ new_add_node->next=top;/*將新節點指向堆疊的頂端*/ top=new_add_node;/*新節點成為堆疊的頂端*/ 4-1 堆疊簡介 } /*從堆疊取出資料*/ int pop() { Linked_Stack ptr; /*指向堆疊頂端的指標*/ int temp; if(isEmpty()) /*判斷堆疊是否為空,如果是則傳回-1*/ { printf("===目前為空堆疊===\n"); return -1; } else { ptr=top;/*指向堆疊的頂端*/ top=top->next;/*將堆疊頂端的指標指向下一個節點*/ temp=ptr->data;/*取出堆疊的資料*/ free(ptr);/*將節點佔用的記憶體釋放*/ return temp;/*將從堆疊取出的資料回傳給主程式*/ } } 18 #include <stdio.h> #include <stdlib.h> #include <string.h>/* 包含字串處理函數 */ int push(char*, int); /* 置入堆疊資料 */ int pop(); /* 取出堆疊資料 */ int show(); /* 顯示堆疊資料 */ struct student { char name[20]; int score; struct student *next; }; typedef struct student s_data; s_data *top = NULL; int main() { struct student int select, score; { char name[20]; do char name[20]; { int score; printf("(1)存入 (2)取出 (3)走訪 (4)離開 => "); scanf("%d", &select); struct student *next; switch (select) { }; case 1: printf("姓名 成績:"); 4-1 堆疊簡介 範例 4.1.3 請設計一C程式,使用malloc()函數來配置串列 堆疊中新元素的記憶體空間,本題中元素為學 生姓名及成績的結構資料,並進行堆疊資料的 存入、取出與走訪動作。元素結構如下: 19 scanf("%s %d", name, &score); push(name, score); break; case 2: pop(); break; case 3: show(); break; 4-1 堆疊簡介 } } while (select != 4); system("pause"); return 0; } int push(char* name, int score)/* 存入新元素 */ { s_data *new_data; new_data = (s_data*) malloc(sizeof(s_data)); /* 配置空間給新元素 */ strcpy(new_data->name, name); /* 設定新元素的資料內容 */ new_data->score = score; new_data->next = top; /* 將新元素的next指向原堆疊頂端 */ top = new_data; /* 設定新元素為堆疊頂端 */ } int pop() /* 取出新元素 */ { s_data *freeme; 20 if(top != NULL) /* 如果堆疊不為空 */ { printf("姓名:%s\t成績:%d......取出\n", top->name, top->score); freeme = top; /* 設定freeme指標,待會要釋放它所指向的位置 */ top = top->next; /* 設定新堆疊頂端 */ free(freeme); /* 釋放freeme指向的記憶體空間 */ } else printf("堆疊已空!\n"); } int show()/* 走訪堆疊陣列 */ { s_data *ptr; ptr = top; if (ptr == NULL) /* 如果指向空位址,表示堆疊為空 */ printf("堆疊已空\n"); else { while (ptr != NULL) /* 由上往下走訪堆疊 */ { printf("姓名:%s\t成績:%d\n", ptr->name, ptr->score); ptr = ptr->next; } } } 4-1 堆疊簡介 21 4-1 堆疊簡介 堆疊類別樣板實作 設計類別時,將資料型態以樣版參數取代,於 使用時再指定資料型態,這個類別稱為類別樣 版(Class Template)。 在程式中,將會依據宣告物件時所指定的資料 型態,來建立適用該資料型態的類別。 類別樣版的宣告格式如下: template <class 樣版形式參數1, class 樣版形式參數2,…> class 類別名稱 { // 類別內敘述區塊 }; 22 #include <iostream> #include <cstdlib> using namespace std; template <class Type> // 定義鏈結串列中的節點 struct Node { Type data; // 紀錄資料 Node* next;// 紀錄下一筆節點的位址 }; template <class Type> class LinkedList // 鏈結串列類別 { private: Node<Type>* first; // 指到第一個節點的指標 public: LinkedList() // 建構子 { first = NULL; } void addNode(Type data); // 加入節點 void display(); // 顯示所有的節點23 }; template<class Type> void LinkedList<Type>::addNode(Type data) 4-1 堆疊簡介 範例 4.1.4 請設計一C++程式,使用樣板類別來實作一個鏈 結串列,它還包含一個樣板結構作為節點的資 料結構,用來儲存不同資料節點以及保留下一 個節點的位址,鏈結串列類別會將所有節點連 接起來。 23 { 4-1 堆疊簡介 Node<Type>* newNode = new Node<Type>; // 新增一個節點 newNode->data = data; // 紀錄資料 newNode->next = first; // 指到前一個節點 first = newNode; // 指到新的節點 } template<class Type> void LinkedList<Type>::display() { Node<Type>* currentNode = first; // 由第一個節點開始顯示 while( currentNode != NULL ) { cout << currentNode->data << " -> "; currentNode = currentNode->next; } } int main() { LinkedList<double> dblList; // 建立一個儲存double型態資料的鏈結串列 double num; // 紀錄輸入的資料 char ch; // 紀錄使用者的選擇 do{ cout << endl <<"請輸入一個數字 : "; cin >> num; 24 dblList.addNode( num ); cout << "繼續輸入(y / n)?"; cin >> ch; }while( ch != 'n' ); cout << endl; dblList.display(); cout << endl << endl; system("pause"); return 0; 4-1 堆疊簡介 // 顯示所有的資料 } 25 #include <iostream> #include <cstdlib> using namespace std; // 設定類別樣版的型態參數Type的預設值為整數int,非型態參數的型別為整數int,預設值為5 template <class Type = int, int size = 5> // 類別樣版宣告 class Stack { private: Type st[size];// 宣告一陣列作為堆疊的儲存空間 int top; // 堆疊資料頂端的索引 public: Stack() { top = -1; } void push(Type data); // 將資料放入堆疊 Type pop();// 將資料由堆疊中取出 }; template < class Type, int size > void Stack< Type, size > :: push ( Type data ) { st[ ++top ] = data; } template < class Type, int size > 4-1 堆疊簡介 範例 4.1.5 請設計一類別樣版Stack,將以堆疊資料結構的方 式來儲存所輸入不同型態的資料。 其中在類別中的成員函數,只要是與輸入資料型 態有關的部份(如push()與pop()函數的參數變數資 料),都使用樣版形式參數取代。要在程式中建立 不同資料型態的堆疊,只須在宣告物件時指定資 料型態即可。 26 Type Stack<Type, size> :: pop() { return st[ top-- ]; } int main() { Stack<> stk_1;// 宣告一堆疊物件,並使用其預設值 Stack<char*, 4> stk_2; // 宣告堆疊物件,其型態為字串,大小為4 stk_1.push( 11 ); stk_1.push( 22 ); stk_1.push( 33 ); cout << "stack_1 [1] = " << stk_1.pop() << endl; cout << "stack_1 [2] = " << stk_1.pop() << endl; cout << "stack_1 [3] = " << stk_1.pop() << endl; cout << endl; stk_2.push( "第一名" ); stk_2.push( "第二名" ); stk_2.push( "第三名" ); cout << "stack_2 [1] = " << stk_2.pop() << endl; cout << "stack_2 [2] = " << stk_2.pop() << endl; cout << "stack_2 [3] = " << stk_2.pop() << endl; cout << endl; system("pause"); return 0; } 4-1 堆疊簡介 27 4-1 堆疊簡介 老鼠走迷宮(1/3) 老鼠行進時,必須遵守以下三個原則: 一次只能走一格。 遇到牆無法往前走時,則退回一步找找看是否有其他的路可以走。 走過的路不會再走第二次。 在建立走迷宮程式前,我們先來了解如何在電 腦中表現一個模擬迷宮的方式。 這時可以利用二維陣列MAZE[row][col],並符 合以下規則: MAZE[i][j]=1 表示[i][j]處有牆,無法通過 =0 表示[i][j]處無牆,可通行 MAZE[1][1]是入口,MAZE[m][n]是出口 28 4-1 堆疊簡介 使用10x12二維陣列的 模擬迷宮地圖表示圖 29 4-1 堆疊簡介 老鼠走迷宮(2/3) 利用鏈結串列來記錄走過的位置,並且將走過 的位置的陣列元素內容標示為2,然後將這個位 置放入堆疊再進行下一次的選擇。 下圖是以小球來代表迷宮中的老鼠: 在迷宮中搜 尋出口 終於找到 迷宮出口 30 4-1 堆疊簡介 老鼠走迷宮(3/3) 利用C演算法來加以描述: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if(上一格可走) { 加入方格編號到堆疊; 往上走; 判斷是否為出口; } else if(下一格可走) { 加入方格編號到堆疊; 往下走; 判斷是否為出口; } else if(左一格可走) { 加入方格編號到堆疊; 31 4-1 堆疊簡介 往左走; 判斷是否為出口; 16 17 18 19 } else if(右一格可走) 20 21 { 加入方格編號到堆疊; 往右走; 判斷是否為出口; 22 23 24 25 26 27 } else { 從堆疊刪除一方格編號; 從堆疊中取出一方格編號; 往回走; 28 29 30 } 32 #include <stdio.h> #include <stdlib.h> #define EAST MAZE[x][y+1] /*定義東方的相對位置*/ #define WEST MAZE[x][y-1] /*定義西方的相對位置*/ #define SOUTH MAZE[x+1][y] /*定義南方的相對位置*/ #define NORTH MAZE[x-1][y] /*定義北方的相對位置*/ #define ExitX 8 /*定義出口的X座標在第八列*/ #define ExitY 10 /*定義出口的Y座標在第十行*/ struct list { int x,y; struct list* next; }; typedef struct list node; typedef node* link; int MAZE[10][12] = {2,1,1,1,1,0,0,0,1,1,1,1, /*宣告迷宮陣列*/ 1,0,0,0,1,1,1,1,1,1,1,1, 1,1,1,0,1,1,0,0,0,0,1,1, 1,1,1,0,1,1,0,1,1,0,1,1, 1,1,1,0,0,0,0,1,1,0,1,1, 1,1,1,0,1,1,0,1,1,0,1,1, 1,1,1,0,1,1,0,1,1,0,1,1, 1,1,1,0,1,1,0,0,1,0,1,1, 1,1,0,0,0,0,0,0,1,0,0,1, 1,1,1,1,1,1,1,1,1,1,1,3}; 4-1 堆疊簡介 範例 4.1.6 請設計一C/C++程式,使用串列堆疊來找出老鼠 走迷宮的路線,其中0表示牆,2表示入口,3表示 出口,6表示老鼠走過的路線。 33 link push(link stack,int x,int y) { link newnode; newnode = (link)malloc(sizeof(node)); if(!newnode) { printf("Error!記憶體配置失敗!\n"); return NULL; } newnode->x=x; newnode->y=y; newnode->next=stack; stack=newnode; return stack; } link pop(link stack,int* x,int* y) { link top; if(stack!=NULL) { top=stack; stack=stack->next; *x=top->x; *y=top->y; free(top); 4-1 堆疊簡介 34 } 4-1 堆疊簡介 else *x=-1; return stack; } int chkExit(int x,int y,int ex,int ey) { if(x==ex&&y==ey) { if(NORTH==1||SOUTH==1||WEST==1||EAST==2) return 1; if(NORTH==1||SOUTH==1||WEST==2||EAST==1) return 1; if(NORTH==1||SOUTH==2||WEST==1||EAST==1) return 1; if(NORTH==2||SOUTH==1||WEST==1||EAST==1) return 1; } return 0; } int main() { int i,j,x,y; link path = NULL; 35 x=1; /*入口的X座標*/ y=1; /*入口的Y座標*/ printf("[迷宮的地模擬圖(0表示牆,2表示入口,3表示出口]\n"); /*印出迷宮的路徑圖*/ for(i=0;i<10;i++) { for(j=0;j<12;j++) printf("%2d",MAZE[i][j]); printf("\n"); } while(x<=ExitX&&y<=ExitY) { MAZE[x][y]=6; if(NORTH==0) { x -= 1; path=push(path,x,y); } else if(SOUTH==0) { x+=1; path=push(path,x,y); } else if(WEST==0) { y-=1; 4-1 堆疊簡介 36 path=push(path,x,y); } else if(EAST==0) { y+=1; path=push(path,x,y); } else if(chkExit(x,y,ExitX,ExitY)==1) /*檢查是否走到出口了*/ break; else { MAZE[x][y]=2; path=pop(path,&x,&y); } } printf("---------------------------\n"); printf("[6表示老鼠走過的路線]\n"); /*印出老鼠走完迷宮後的路徑圖*/ printf("---------------------------\n"); for(i=0;i<10;i++) { for(j=0;j<12;j++) printf("%2d",MAZE[i][j]); printf("\n"); } system("pause"); return 0; 4-1 堆疊簡介 37 4-1 堆疊簡介 八皇后問題 這也是一種常見的堆疊應用實例。 可以將其應用在4*4的棋盤,就稱為4-皇后問題 ;應用在8*8的棋盤,就稱為8-皇后問題。應用 在N*N的棋盤,就稱為N-皇后問題。 要解決N-皇后問題(在此我們以8-皇后為例),首 先當於棋盤中置入一個新皇后,且這個位置不 會被先前放置的皇后吃掉,就將這個新皇后的 位置存入堆疊。 38 4-1 堆疊簡介 底下分別是4-皇后及8-皇后在堆疊存放的 內容及對應棋盤的其中一組解。 4皇后 8皇后 39 4-2 算術運算式的表示法 算術運算式的表示法 1. 中序法(Infix):運算子在兩個運算元中間,例 如A+B、(A+B)*(C+D)等都是中序表示法。 2. .前序法(Prefix):運算子在運算元的前面,例 如+AB、*+AB+CD等都是前序表示法。 3. 後序法(Postfix) :運算子在運算元的後面, 例如AB+、AB+CD+*等都是後序表示法。 40 4-2 算術運算式的表示法 中序轉為前序與後序 1.括號轉換法 以下是C/C++運算子中運算子的優先順序: 41 4-2 算術運算式的表示法 練習以括號把下列中序式轉成前序及後序式: 6+2*9/3+4*2-8 中序→前序(Infix→Prefix) – (1).先把運算式依照運算子優先順序以括號括起來。 – (2).針對運算子,把括號內的運算子取代所有的左括號, 以最近者為優先。 – (3).將所有右括號去掉,即得前序式結果。 – 前序式:-++6/*293*428 42 4-2 算術運算式的表示法 中序→後序(infix→postfix) – (1).先把運算式依照運算子優先順序以括號括起來。 – (2).針對運算子,把括號內的運算子取代所有的右括號,以 最近者為優先。 – (3). 將所有左括號去掉,即得後序式結果。 – 後序式:629*3/+42*+8- 範例 4.2.1 請將中序式A/B**C+D*E-A*C,利用括號法轉換 成前序式與後序式。 43 4-2 算術運算式的表示法 解答 首先請按照前面的括號法說明,將中序式括號後,可以得 到下列式子,並移動運算子來取代左括號: 最後去掉所有右括號,可得下式: →前序式:-+/A**BC*DE*AC 接著要轉換成後序法也一樣,將中序式分別括號完後,移 動運算子來取代右括號: 最後再去掉所有左括號,可得下式: →後序式:ABC**/DE*+AC*- 44 4-2 算術運算式的表示法 2. 堆疊法 中序→前序(Infix→Prefix) – (1).由右至左讀進中序運算式的每個字元(token)。 – (2).如果讀進的字元為運算元,則直接輸出到前序式中 – (3).如果遇到'(',則彈出堆疊內的運算子,直到彈出到一個')' ,兩者互相抵銷止。 – (4). ")"的優先權在堆疊內比任何運算子都小,任何運算子都 可壓過它,不過在堆疊外卻是優先權最高者。 – (5)當運算子準備進入堆疊內時,並須和堆疊頂端的運算子比 較,如果外面的運算子優先權大於或等於頂端的運算子則推 入,如果較小就彈出,直到遇到優先權較小者或堆疊為空時 ,就把外面這個運算子推入。 – (6)中序式讀完後,如果運算子堆疊不是空,則將其內的運算 子逐一彈出,輸出到前序式。 45 4-2 算術運算式的表示法 以下我們將練習把中序式(A+B)*D+E/(F+A*D)+C以 堆疊法轉換成前序式。首先請右至左讀取字元,並將 步驟繪出表格如下: 46 4-2 算術運算式的表示法 中序->後序(Infix->Postfix) – (1).由左至右讀進中序運算式的每個字元(token)。 – (2).如果讀進的字元為運算元,則直接輸出輸出到後序式中 。 – (3).如果遇到')',則彈出堆疊內的運算子,直到彈出到一個 '(',兩者互相抵銷止。 – (4). "("的優先權在堆疊內比任何運算子都小,任何運算子都 可壓過它,不過在堆疊外卻是優先權最高者。 – (5)當運算子準備進入堆疊內時,並須和堆疊頂端的運算子 比較,如果外面的運算子優先權大於頂端的運算子則推入 ,如果較小或等於就彈出,直到遇到優先權較小者或堆疊 為空時,就把外面這個運算子推入。 – (6)中序式讀完後,如果運算子堆疊不是空,則將其內的運 算子逐一彈出,輸出到後序式。 47 4-2 算術運算式的表示法 以下我們將練習把中序式(A+B)*D+E/(F+A*D)+C以 堆疊法轉換成後序式。首先請左至右讀取字元,並將 步驟繪出表格如下: 48 4-2 算術運算式的表示法 範例 4.2.2 將下面的中序法轉成前序與後序算術式:(以 下皆用堆疊法) A/B↑C+D*E-A*C 解答 中序轉前序 49 4-2 算術運算式的表示法 中序轉後序 50 #include <stdio.h> #include <stdlib.h> #define MAX 50 char infix_q[MAX]; int compare(char stack_o, char infix_o); void infix_to_postfix(); /*運算子優先權的比較,若輸入運算子小於堆疊中運算子*/ /*,則傳回值為1,否則為0 */ /*主函數宣告*/ int main () { int i=0; for (i=0; i<MAX; i++) infix_q[i]='\0'; printf("\t------------------------------------------\n"); printf("\t中序運算式轉成後序運算式\n"); printf("\t可以使用的運算子包括:^,*,+,-,/,(,)等 \n"); printf("\t------------------------------------------\n"); printf("\t請開始輸入中序運算式: "); infix_to_postfix(); printf("\n"); printf("\t--------------------------------------------\n"); system("pause"); return 0; } 4-2 算術運算式的表示法 範例 4.2.3 請設計一C程式,使用堆疊法來將所輸入的中序 運算式轉換為後序式。 51 int compare(char stack_o, char infix_o) { /*在中序表示法佇列及暫存堆疊中,運算子的優先順序表,*/ /*其優先權值為INDEX/2 */ char infix_priority[9] ; char stack_priority[8] ; int index_s=0, index_i=0; infix_priority[0]='q';infix_priority[1]=')'; infix_priority[2]='+';infix_priority[3]='-'; infix_priority[4]='*';infix_priority[5]='/'; infix_priority[6]='^';infix_priority[7]=' '; infix_priority[8]='('; stack_priority[0]='q';stack_priority[1]='('; stack_priority[2]='+';stack_priority[3]='-'; stack_priority[4]='*';stack_priority[5]='/'; stack_priority[6]='^';stack_priority[7]=' '; while (stack_priority[index_s] != stack_o) index_s++; while (infix_priority[index_i] != infix_o) index_i++; return ((int)(index_s/2) >= (int)(index_i/2) ? 1 : 0); } void infix_to_postfix() { int rear=0, top=0, flag=0,i=0; 4-2 算術運算式的表示法 52 char stack_t[MAX]; for (i=0; i<MAX; i++) 4-2 算術運算式的表示法 stack_t[i]='\0'; gets(infix_q); i=0; while(infix_q[i]!='\0') { i++; rear++; } infix_q[rear] = 'q'; printf("\t後序表示法 : "); stack_t[top] = 'q'; for (flag = 0; flag <= rear; flag++)\ { switch (infix_q[flag]) { /*輸入為),則輸出堆疊內運算子,直到堆疊內為(*/ case ')': while(stack_t[top]!='(') printf("%c",stack_t[top--]); top--; break; /*輸入為q,則將堆疊內還未輸出的運算子輸出*/ 53 case 'q': while(stack_t[top]!='q') printf("%c",stack_t[top--]); break; /*輸入為運算子,若小於TOP在堆疊中所指運算子,*/ /*則將堆疊所指運算子輸出,若大於等於TOP在堆疊*/ /*中所指運算子,則將輸入之運算子放入堆疊 */ case '(': case '^': case '*': case '/': case '+': case '-': while (compare(stack_t[top], infix_q[flag])==1) printf("%c",stack_t[top--]); 4-2 算術運算式的表示法 stack_t[++top] = infix_q[flag]; break; /*輸入為運算元,則直接輸出*/ default : printf("%c",infix_q[flag]); break; } } } 54 4-2 算術運算式的表示法 前序與後序轉為中序 1.括號轉換法 前序→中序(Prefix->Infix) – 適當的以「運算子+運算元」方式括號。依次將每個運算子 ,以最近為原則取代後方的右括號,最後再去掉所有左括 號。例如:將-+/A**BC*DE*AC轉為中序法,結果是 A/B**C+D*E-A*C: 後序→中序(Postfix->Infix) – 適當的以「運算元+運算子」方式括號,依次將每個運算子 ,以最近為原則取代前方的左括號,最後再去掉所有右括 號。例如將ABC↑/DE*+AC*-轉為中序法,結果是 A/B↑C+D*E-A*C。 55 4-2 算術運算式的表示法 2.堆疊法 1.若要將前序式轉為中序式,由右至左讀進運算式的每 個字元(token);若是要將後序式轉換成中序式,則讀 取方向改成由左至右。 2.辨別讀入字元,若為運算元則放入此堆疊中。 3.辨別讀入字元,若為運算子則從堆疊中取出兩個字元 ,結合成一個基本的中序運算式(<運算元><運算子>< 運算元>)後,再把結果放入堆疊。 4.在轉換過程中,前序和後序的結合方式是不同的,前 序式的順序是是<運算元2><運算子><運算元1>而後序 式是<運算元1><運算子><運算元2>,如下圖所示: 前序轉中序:<OP2><運算子><OP1> 後序轉中序:<OP1><運算子><OP2> 56 4-2 算術運算式的表示法 中序表示法求值 由中序表示法來求值,請依照以下五個步驟: 1.建立兩個堆疊,分別存放運算子及運算元。 2.讀取運算子時,必須先比較堆疊內的運算子優先權,若堆疊內 運算子的優先權較高,則先計算堆疊內運算子的值。 3.計算時,取出一個運算子及兩個運算元進行運算,運算結果直 接存回運算元堆疊中,當成一個獨立的運算元。 4.當運算式處理完畢後,一步一步清除運算子堆疊,直到堆疊空 了為止。 5.取出運算元堆疊中的值就是計算結果。 現在就以上述五個步驟,來求取中序表示法2+3*4+5 的值。 57 4-2 算術運算式的表示法 運算式必須使用兩個堆疊分別存放運算子及運 算元,並依優先順序進行運算: 步驟1 依序將運算式存入堆疊,遇到兩個運算子時比較優先 權再決定是否要先行運算: 步驟2 遇到運算子*,與堆疊中最後一個運算子+比較,優先 權較高故存入堆疊: 58 4-2 算術運算式的表示法 步驟3 遇到運算子+,與堆疊中最後一個運算子*比較,優先 權較低,故先計算運算子*的值。取出運算子*及兩個 運算元進行運算,運算完畢則存回運算元堆疊: 步驟4 把運算子+及運算元5存入堆疊,等運算式完全處理後 ,開始進行清除堆疊內運算子的動作,等運算子清理 完畢結果也就完成了: 59 4-2 算術運算式的表示法 步驟5 取出一個運算子及兩個運算元進行運算,運算完畢存 入運算元堆疊: 完成 取出一個運算子及兩個運算元進行運算,運算完畢存 入運算元堆疊,直到運算子堆疊空了為止。 60 4-2 算術運算式的表示法 前序法的求值運算 實作前序運算式+*23*45如何使用堆疊來運算的 步驟: 步驟1 從堆疊中取出元素: 步驟2 從堆疊中取出元素,遇到運算子則進行運算,結果存 回運算元堆疊: 61 4-2 算術運算式的表示法 步驟3 從堆疊中取出元素: 步驟4 從堆疊中取出元素,遇到運算子則從運算元取出兩個 運算元進行運算,運算結果存回運算元堆疊: 完成 把堆疊中最後一個運算子取出,從運算元取出兩個運 算元進行運算,運算結果存回運算元堆疊。最後取出 運算元堆疊中的值即為運算結果。 62 4-2 算術運算式的表示法 後序法的求值運算 實作後序表示法23*45*+的求值運算: 步驟1 直接讀取運算式,遇到運算子則進行運算: – 放入2及3後取回*,這時取回堆疊內兩個運算元進行運算, 完畢後放回堆疊中。 2*3=6 63 4-2 算術運算式的表示法 步驟2 接著放入4及5遇到運算子*,取回兩個運算元進行運 算,運算完後放回堆疊中: 4*5=20 完成 最後取回運算子,重複上述步驟。 6+20=26 64 4-3 佇列 佇列 是一種「先進先出」(First In, First Out)的資 料結構,和堆疊一樣都是一種有序串列的抽象 資料型態。 佇列的兩端都會有資料進出的動作,所以必須 記錄佇列的前端與後端,如下圖所示使用front 與rear這兩個註標來模擬佇列的運作: 65 4-3 佇列 佇列基本工作運算 五種基本工作運算: 66 4-3 佇列 陣列實作佇列 與堆疊不同之處是需要擁有兩種基本動作加入 與刪除,而且使用front與rear兩個註標來分別指 向佇列的前端與尾端,缺點是陣列大小並無法 事先規劃宣告。 首先我們需要宣告一個有限容量的陣列,並以 下列圖示說明: #define MAXSIZE 4 int queue[MAXSIZE]; /* 佇列大小為4 */ int front=-1; int rear=-1; 67 4-3 佇列 1.當開始時,我們將front與rear都預設為-1,當 front=rear時,則為空佇列。 2.加入dataA,front=-1,rear=0,每加入一個元素 ,將rear值加1: 3.加入dataB、dataC,front=-1,rear=2: 4.取出dataA,front=0,rear=2,每取出一個元素, 將front值加1: 68 4-3 佇列 5.加入dataD,front=0,rear=3,此時當 rear=MAXSIZE-1,表示佇列已滿。 6. 取出dataB,front=1,rear=3 從以上實作的過程,我們可以將相關以陣列操 作佇列的C的相關演算法如下: #defineMAX_SIZE 100 /* 佇列的最大容量 */ int queue[MAX_SIZE]; int front=-1; int rear=-1; /* 空佇列時,front=-1,rear=-1 */ /* front及rear皆為全域變數 */ 69 4-3 佇列 void enqueue(int item) /*將新資料加入Q的尾端,傳回新佇列*/ ﹛ if (rear==MAX_SIZE-1) printf(“%s”,"佇列已滿!"); else ﹛ rear++; queue(rear)=item; ﹜/* 加新資料到佇列的尾端 */ ﹜ void dequeue(int item) /*刪除佇列前端資料,傳回新佇列*/ ﹛ if (front==rear) printf(“%s”,"佇列已空!"); else ﹛ front++; item=Queue[front]; } } /* 刪除佇列前端資料 */ 70 4-3 佇列 void FRONT_VALUE(int *Queue) /*傳回佇列前端的值*/ ﹛ if (front==rear) printf(“%s”," 這是空佇列"); else printf("%s", queue[front]); } /* 傳回佇列前端的值 */ 71 #include <stdio.h> #include <stdlib.h> #include <conio.h> #define MAX 10 /*定義佇列的大小*/ int main() { int front,rear,val,queue[MAX]={0}; char choice; front=rear=-1; while(rear<MAX-1 && choice!='e') { printf("[a]表示存入一個數值[d]表示取出一個數值[e]表示跳出此程式: "); choice=getche(); switch(choice) { case 'a': printf("\n[請輸入數值]: "); scanf("%d",&val); rear++; queue[rear]=val; break; case 'd': if(rear>front) { front++; 4-3 佇列 範例 4.3.1 請設計一C程式,來實作佇列的工作運算,加入 資料時請輸入"a",要取出資料時可輸入"d",將 會直接印出佇列前端的值,要結束請按"e"。 72 printf("\n[取出數值為]: [%d]\n",queue[front]); queue[front]=0; 4-3 佇列 } else { printf("\n[佇列已經空了]\n"); exit(0); } break; default: printf("\n"); break; } } printf("\n------------------------------------------\n"); printf("[輸出佇列中的所有元素]:"); if(rear==MAX-1) printf("[佇列已滿]\n"); else if (front>=rear) { printf("沒有\n"); printf("[佇列已空]\n"); } else 73 4-3 佇列 { while (rear>front) { front++; printf("[%d] ",queue[front]); } printf("\n"); printf("------------------------------------------\n"); } printf("\n"); system("pause"); return 0; } 74 4-3 佇列 環狀佇列 是一種環形結構的佇列,它仍是以一種Q(0:n-1)的 線性一維陣列,同時Q(0)為Q(n-1)的下一個元素, 可以用來解決無法判斷佇列是否滿溢的問題。 指標front永遠以逆時鐘方向指向佇列中第一個元 素的前一個位置,rear則指向佇列目前的最後位置 。一開始front和rear均預設為-1,表示為空佇列 ,也就是說如果front=rear則為空佇列。另外有: rear(rear+1) mod n front(front+1) mod n 75 4-3 佇列 上述方式僅允許佇列最多只能存放n-1個資料(亦 即犠牲最後一個空間),當rear指標的下一個是 front的位置時,就認定佇列已滿,無法再將資 料加入,如下圖便是填滿的環狀佇列外觀: 76 4-3 佇列 底下我們將整個過程以下圖來說明。 77 4-3 佇列 78 4-3 佇列 而當rear=front時,則可代表佇列已空。所以在 enqueue和dequeue的兩種工作定義和原先佇列 工作定義的演算法就有不同之處了。必須改寫 如下: /* 環狀佇列的加入演算法 */ viod AddQ (int item) { rear=(rear+1)%MAX_SIZE; if (front==rear ) printf(“%s”, "佇列已滿! "); else queue[rear]=item; } /* 環形佇列的刪除演算法 */ void dequeue(int item) { if (front==rear) printf(“%s”, "佇列是空的!"); else { front=(front+1)%MAX_SIZE; item=Queue[front]; } } 79 #include <stdio.h> int main(void) { int front,rear,val,queue[5]={0}; front=rear=-1; while(rear<5&&val!=-1) { printf("請輸入一個值以存入佇列,欲取出值請輸入0。(結束輸入-1):"); scanf("%d",&val); if(val==0) { if(front==rear) { printf("[佇列已經空了]\n"); break; } front++; if (front==5) front=0; printf("取出佇列值 [%d]\n",queue[front]); queue[front]=0; } else if(val!=-1&&rear<5) { if(rear+1==front||rear==4&&front<=0) 4-3 佇列 範例 4.3.2 請設計一C程式,來實作環形佇列的工作運算, 當要取出資料時可輸入"0",要結束時可輸入"-1" 。 80 { 4-3 佇列 printf("[佇列已經滿了]\n"); break; } rear++; if(rear==5) rear=0; queue[rear]=val; } } printf("\n佇列剩餘資料:\n"); if (front==rear) printf("佇列已空!!\n"); else { while(front!=rear) { front++; if (front==5) front=0; printf("[%d]",queue[front]); queue[front]=0; } } printf("\n"); system("pause"); return 0; } 81 4-3 佇列 範例 4.3.3 上述環形佇列演算法中,造成了任何時候佇列 中最多只允許MAX_SIZE-1個元素。有沒有方法 可以改進呢?試說明之與寫出修正後演算法。 解答 只要多使用一個旗標TAG來判斷,當TAG==1時表示, 佇列是滿的;TAG==0時,表示佇列是空的。修正後演 算法如下: 82 4-3 佇列 /* 環狀佇列的加入修正演算法 */ viod AddQ (int item) { rear=(rear+1)%MAX_SIZE; if(front==rear && TAG==1) printf(“%s”, "佇列已滿! "); else queue[rear]=item; if(front==rear) TAG=1; } /* 環形佇列的刪除修正演算法 */ void dequeue(int item) { if(rear==front && TAG==0) printf(“%s”, "佇列是空的!"); else { front=(front+1)%MAX_SIZE; item=Queue[front]; if (front==rear) TAG=0; } } 83 4-3 佇列 串列實作佇列 在宣告佇列類別中,除了和佇列類別中相關的 方法外,還必須有指向佇列前端及佇列尾端的 指標,即front及rear。例如我們以學生姓名及成 績的結構資料來建立佇列串列的節點,及front 與rear指標宣告如下: struct student { char name[20]; int score; struct student *next; }; typedef struct student s_data; s_data *front =NULL; s_data *rear = NULL; 84 4-3 佇列 在佇列串列中加入新節點,等於加入此串列的 最後端,而刪除節點就是將此串列最前端的節 點刪除。C的加入與刪除運算法如下: int enqueue(char* name, int score) { s_data *new_data; new_data = (s_data*) malloc(sizeof(s_data)); /* 配置記憶體給新元 素 */ strcpy(new_data->name, name); /* 設定新元素的資料 */ new_data->score = score; if (rear == NULL) /* 如果rear為NULL,表示這是第一個元素 */ front = new_data; else rear->next = new_data; /* 將新元素連接至佇列尾端 */ rear = new_data; /* 將rear指向新元素,這是新的佇列尾端 */ new_data->next = NULL; /* 新元素之後無其它元素 */ } 85 4-3 佇列 int dequeue() { s_data *freeme; if (front == NULL) puts("佇列已空!"); else { printf("姓名:%s\t成績:%d ....取出\n", front->name, front->score); freeme = front; /* 設定將要釋放的元素指標 */ front = front->next; /* 將佇列前端移至下一個元素 */ free(freeme); /* 釋放所取出的元素之記憶體 */ } } 86 #include <stdio.h> #include <stdlib.h> #include <string.h> int enqueue(char*, int); /* 置入佇列資料 */ int dequeue(); /* 取出佇列資料 */ int show(); /* 顯示佇列資料 */ struct student { char name[20]; int score; struct student *next; }; typedef struct student s_data; 4-3 佇列 範例 4.3.3 請利用串列結構來設計一C程式,串列中元素節 點仍為學生姓名及成績的結構資料。 本程式還能進行佇列資料的存入、取出與走訪 動作: s_data *front =NULL; struct student s_data *rear = NULL; { int main() char name[20]; { int score; score; int select, struct student *next; char name[20]; do }; { typedef struct student s_data; printf("(1)存入 (2)取出 (3)顯示 (4)離開 => "); scanf("%d", &select); 87 switch (select) { case 1: printf("姓名 成績:"); scanf("%s %d", name, &score); enqueue(name, score); break; case 2: dequeue(); break; case 3: show(); break; } } while (select != 4); system("pause"); return 0; 4-3 佇列 } int enqueue(char* name, int score) { s_data *new_data; new_data = (s_data*) malloc(sizeof(s_data)); /* 配置記憶體給新元素 */ strcpy(new_data->name, name); /* 設定新元素的資料 */ new_data->score = score; 88 if (rear == NULL) /* 如果rear為NULL,表示這是第一個元素 */ front = new_data; else rear->next = new_data; /* 將新元素連接至佇列尾端 */ rear = new_data; /* 將rear指向新元素,這是新的佇列尾端 */ new_data->next = NULL; /* 新元素之後無其它元素 */ } int dequeue() { s_data *freeme; if (front == NULL) puts("佇列已空!"); else { printf("姓名:%s\t成績:%d ....取出\n", front->name, front->score); freeme = front; /* 設定將要釋放的元素指標 */ front = front->next; /* 將佇列前端移至下一個元素 */ free(freeme); /* 釋放所取出的元素之記憶體 */ } } int show() { s_data *ptr; ptr = front; 4-3 佇列 89 if (ptr == NULL) puts("佇列已空!"); else { puts("front -> rear"); while (ptr != NULL) /* 由front往rear走訪佇列 */ { printf("姓名:%s\t成績:%d\n", ptr->name, ptr->score); ptr = ptr->next; } } } 4-3 佇列 90 4-3 佇列 雙向佇列 為一有序串列,加入與刪除可在佇列的任意一 端進行, 請看下圖: 通常在一般的應用上,雙向佇列的應用可以區 分為兩種:第一種是資料只能從一端加入,但 可從兩端取出,另一種則是可由兩端加入,但 由一端取出。 91 4-3 佇列 C/C++的節點宣告、加入與刪除運算法 struct Node { int data; struct Node *next; }; typedef struct Node QueueNode; typedef QueueNode *QueueByLinkedList; QueueByLinkedList front=NULL; QueueByLinkedList rear=NULL; 92 4-3 佇列 void enqueue(int value) /*方法enqueue:佇列資料的加入*/ { QueueByLinkedList node; /*建立節點*/ node=(QueueByLinkedList)malloc(sizeof(QueueNode)); node->data=value; node->next=NULL; /*檢查是否為空佇列*/ if (rear==NULL) front=node;/*新建立的節點成為第1個節點*/ else rear->next=node;/*將節點加入到佇列的尾端*/ rear=node;/*將佇列的尾端指標指向新加入的節點*/ } 93 4-3 佇列 int dequeue(int action)/*方法dequeue:佇列資料的取出*/ { int value; QueueByLinkedList tempNode,startNode; /*從前端取出資料*/ if (!(front==NULL) && action==1) { if(front==rear) rear=NULL; value=front->data;/*將佇列資料從前端取出*/ front=front->next;/*將佇列的前端指標指向下一個*/ return value; } /*從尾端取出資料*/ else if(!(rear==NULL) && action==2) { startNode=front;/*先記下前端的指標值*/ value=rear->data;/*取出目前尾端的資料*/ /*找尋最尾端節點的前一個節點*/ tempNode=front; while (front->next!=rear && front->next!=NULL) 94 4-3 佇列 { front=front->next; tempNode=front; } front=startNode;/*記錄從尾端取出資料後的佇列前端指標*/ rear=tempNode;/*記錄從尾端取出資料後的佇列尾端指標*/ /*下一行程式是指當佇列中僅剩下最節點時, 取出資料後便將front及rear指向null*/ if ((front->next==NULL) || (rear->next==NULL)) { front=NULL; rear=NULL; } return value; } else return -1; } 95 #include <stdio.h> #include <stdlib.h> struct Node { int data; struct Node *next; }; typedef struct Node QueueNode; typedef QueueNode *QueueByLinkedList; QueueByLinkedList front=NULL; QueueByLinkedList rear=NULL; /*方法enqueue:佇列資料的存入*/ void enqueue(int value) { QueueByLinkedList node; /*建立節點*/ node=(QueueByLinkedList)malloc(sizeof(QueueNode)); node->data=value; node->next=NULL; /*檢查是否為空佇列*/ if (rear==NULL) front=node;/*新建立的節點成為第1個節點*/ else rear->next=node;/*將節點加入到佇列的尾端*/ rear=node;/*將佇列的尾端指標指向新加入的節點*/ 4-3 佇列 範例 4.3.4 請利用串列結構來設計一輸入限制的雙向佇列C 程式,我們只能從一端加入資料,但取出資料 時,將分別由前後端取出。 96 } int dequeue(int action)/*方法dequeue:佇列資料的取出*/ { int value; QueueByLinkedList tempNode,startNode; /*從前端取出資料*/ if (!(front==NULL) && action==1) { if(front==rear) rear=NULL; value=front->data;/*將佇列資料從前端取出*/ front=front->next;/*將佇列的前端指標指向下一個*/ return value; } /*從尾端取出資料*/ else if(!(rear==NULL) && action==2) { startNode=front;/*先記下前端的指標值*/ value=rear->data;/*取出目前尾端的資料*/ /*找尋最尾端節點的前一個節點*/ tempNode=front; while (front->next!=rear && front->next!=NULL) { front=front->next; tempNode=front; 4-3 佇列 97 } 4-3 佇列 front=startNode;/*記錄從尾端取出資料後的佇列前端指標*/ rear=tempNode;/*記錄從尾端取出資料後的佇列尾端指標*/ /*下一行程式是指當佇列中僅剩下最節點時, 取出資料後便將front及rear指向null*/ if ((front->next==NULL) || (rear->next==NULL)) { front=NULL; rear=NULL; } return value; } else return -1; } int main() { int temp,item; char ch; printf("以鏈結串列來實作雙向佇列\n"); printf("====================================\n"); do { printf("加入請按 a,取出請按 d,結束請按 e:"); ch=getche(); 98 printf("\n"); if(ch=='a') { printf("加入的元素值:"); scanf("%d",&item); enqueue(item); } else if(ch=='d') { temp=dequeue(1); printf("從雙向佇列前端依序取出的元素資料值為:%d\n",temp); temp=dequeue(2); printf("從雙向佇列尾端依序取出的元素資料值為:%d\n",temp); } else break; } while(ch!='e'); system("pause"); return 0; } 4-3 佇列 99 4-3 佇列 優先佇列 為一種不必遵守佇列特性-FIFO(先進先出)的 有序串列,其中的每一個元素都賦予一個優先 權(Priority),加入元素時可任意加入,但有最高 優先權者(Highest Priority Out First, HPOF)則最 先輸出。 例如假設有4個行程P1,P2,P3,P4,其在很短的 時間內先後到達等待佇列,每個行程所執行時 間如下表所示: 100 4-3 佇列 在此設定每個P1、P2、P3、P4的優先次序值分 別為2,8,6,4(此處假設數值越小其優先權越低; 數值越大其優先權越高),以下就是以甘特圖 (Gantt Chart)繪出優先權排程(Priority Scheduling, PS)的排班情況: 以PS方法排班所繪出的甘特圖如下: 101 本章結束 Q&A討論時間 102