Algorithms and Data Structures on C スタックと待ち行列 Algorithms and Data Structures on C この回の要点 • スタック – スタックの構造 • 最後に入れたものが、最初に取り出される(LIFO) • 日常に良くある「ちょっと置いといて・・・」の概念 – C言語の配列による実装 • スタックオーバーフロー • スタックアンダーフロー – 逆ポーランド記法電卓 • 待ち行列 – 待ち行列の構造 • 最初に入れたものが、最初に取り出される(FIFO) • 日常に良くある「行列」の概念 – C言語の配列による実装 • リングバッファの利用 Algorithms and Data Structures on C スタック(棚) • データの挿入と削除が、リストの先頭だけ – 生協のトレイ • 生協の人は、置き場の一番上に新しいトレイを置く • 使う人は、置き場の一番上のトレイを取る • LIFO(Last In First Out) 置く 取る 使う人 生協の人 置き場 スタック Algorithms and Data Structures on C 身近なスタック • 日常生活の中にもスタックがある – とりあえず置いといて・・・、の概念 本を読む 辞書を引く 来客対応 電話に出る 心の中の スタック 積む 本 積む 辞書 本 積む 来客 辞書 本 取る 辞書 本 取る 本 取る Algorithms and Data Structures on C スタックの構造 • 箱が上に積み上がっていく構造 – – – – 最初の要素→スタックの底(bottom) 最後の要素→スタックの頂上(top) データの挿入→スタックに積む(push) データの削除→スタックから取り出す(pop) Push Pop Top Bottom Algorithms and Data Structures on C スタックの利用 • 機械語のPush・Pop命令 – スタックポインタ(SP)の示すアドレスにデータを保存・読 み出し – サブルーチンコールの戻り値の自動保存・復元 • Cの関数やJavaのメソッド呼び出し – 引数をスタックに積んで渡す スタック Push (FF) ・ ・ ・ SP SP SP FF 3A 24 4 3 Pop (3A) ・ ・ ・ int add(int a,int b){ return a+b; } int main(){ int ans=add(3,4); } Algorithms and Data Structures on C 配列によるスタックの実現 • 最大サイズは固定(=M) – stack[0]がスタックの底(bottom) – stack[M-1]がスタックの最大 – stack[sp-1]がスタックの頂上(top) • • • • sp :スタックポインタ 最初は sp=0 Pushするたびに sp=sp+1(但し sp=M以外) Popするたびに sp=sp-1(但し、sp=0以外) stack[] sp spは常に空の箱を指す # # # # # # # # # # # 0 1 2 3 4 M-1 Algorithms and Data Structures on C SimpleStack.cc /*** 簡単なスタック ***/ #include <stdio.h> #include <stdlib.h> // スタック用変数 const int size=100; int stack[size]; int sp=0; // スタックサイズ // スタック // スタックポインタ // プッシュ void push(int n){ if(sp==size){ fprintf(stderr,"Error: stack overflow.¥n"); exit(1); } 代入してから spを増やす stack[sp++]=n; } Algorithms and Data Structures on C SimpleStack.cc // ポップ int pop(){ if(sp==0){ fprintf(stderr,"Error: stack underflow.¥n"); exit(1); } return stack[--sp]; } // 表示 void print(){ printf("%d:",sp); for(int i=0;i<sp;i++) printf("%d ",stack[i]); printf("¥n"); } spを減らしてから 取り出す Algorithms and Data Structures on C SimpleStack.cc // メイン int main(void){ print(); push(1); push(2); push(3); push(4); push(5); print(); pop(); pop(); print(); push(6); push(7); push(8); print(); pop(); pop(); pop(); pop(); pop(); print(); pop(); pop(); print(); } Algorithms and Data Structures on C 簡単なスタックの例(結果) $ ./SimpleStack 0: 5:1 2 3 4 5 3:1 2 3 6:1 2 3 6 7 8 1:1 Error: stack underflow. $ 1つしか要素が入っ ていないスタックか ら2つの要素を 取り出そうとした Algorithms and Data Structures on C ヘッダファイルを使った実装 • スタックだけを実装する – 他のソースコードから利用可能にする • 2つのファイルに分ける – StackInt.h • 整数スタック型の宣言 • 関数のプロトタイプ宣言 – StackInt.cc • 関数の実装 • 利点 – スタックを使いたいとき、その都度実装する必要がない – オブジェクト指向的な利用法 Algorithms and Data Structures on C StackInt.h #ifndef __StackInt__h #define __StackInt__h /*** *** 整数スタック ***/ #include <stdio.h> #include <stdlib.h> // 整数スタック型の宣言 typedef struct { int *stack; // スタック int sp; // スタックポインタ int size; // 要素数 } StackInt; 続く Algorithms and Data Structures on C StackInt.h 第1引数にスタック サイズを指定する // プロトタイプ宣言 StackInt *makeStackInt(int s); void free(StackInt *si); void push(StackInt *si,int n); int pop(StackInt *si); int peek(StackInt *si); int getSize(StackInt *si); void print(StackInt *si); #endif // __StackInt__h // // // // // // // スタックの生成 スタックの解放 プッシュ ポップ ピーク 要素数 内部の表示 第1引数に必ず StackInt* を指定する (どのスタックを 操作するかを指定) Algorithms and Data Structures on C StackInt.cc /*** StackIntの実装 ***/ #include "StackInt.h" // スタックの生成 StackInt *makeStackInt(int s){ StackInt *si=(StackInt*)malloc(sizeof(StackInt)); si->size=s; si->stack=(int*)malloc(si->size*sizeof(int)); si->sp=0; StackInt* StackInt スタック実体 return si; si stack } // スタックの解放 void free(StackInt *si){ free(si->stack); free((void*)si); } sp size si->size 続く Algorithms and Data Structures on C StackInt.cc // プッシュ void push(StackInt *si,int n){ if(si->sp==si->size){ fprintf(stderr,"Error: stack overflow.¥n"); exit(1); } si->stack[si->sp++]=n; } // ポップ int pop(StackInt *si){ if(si->sp==0){ fprintf(stderr,"Error: stack underflow.¥n"); exit(1); } return si->stack[--si->sp]; } 続く Algorithms and Data Structures on C StackInt.cc // ピーク int peek(StackInt *si){ if(si->sp==0){ fprintf(stderr,"Error: stack underflow.¥n"); exit(1); } return si->stack[si->sp-1]; } // 要素数 int getSize(StackInt *si){ return si->sp; } 続く Algorithms and Data Structures on C StackInt.cc // 内部の表示 void print(StackInt *si){ printf("%d:",si->sp); for(int i=0;i<si->sp;i++) printf("%d ",si->stack[i]); printf("¥n"); } Algorithms and Data Structures on C 利用法 • 整数スタックを使用したいソースコード – #include “StackInt.h” – StackInt型とプロトタイプ関数が使用可能 • リンク – StackInt.oをリンクする • CygwinのbashでのMakefile CC = g++ all:StackTest SimpleStack StackTest:StackTest.o StackInt.o StackTest.o:StackTest.cc StackInt.h StackInt.o:StackInt.cc StackInt.h SimpleStack:SimpleStack.cc Algorithms and Data Structures on C StackTest.cc /*** 配列によるスタックの実装例 ***/ #include "StackInt.h" ヘッダの読み込み // メイン int main(int argc,char **argv){ StackIntの生成 StackInt *si=makeStackInt(100); print(si); push(si,1); push(si,2); push(si,3); push(si,4); push(si,5); print(si); pop(si); pop(si); 第1引数に必ず print(si); StackInt* を指定する push(si,6); push(si,7); push(si,8); print(si); pop(si); pop(si); pop(si); pop(si); pop(si); print(si); pop(si); pop(si); print(si); StackIntの破棄 free(si); } Algorithms and Data Structures on C 逆ポーランド記法(RPN) • Reverse Polish Notation • 演算子を操作対象の後に書く記法 – – – – 後置記法 (Postfix Notation) 括弧( ) が不要 デミリタ(区切り文字)が必要 計算機で演算を行う場合に便利 • スタックを利用すると簡単 1+2 (1+2)*(4+6) (1+2)*(3+4*(2-3)) 通常記法 1 2 + 1 2 + 4 6 + * 1 2 + 3 4 2 3 - * + * 逆ポーランド記法 Algorithms and Data Structures on C スタックによる逆ポーランド電卓 • 入力トークンを1つずつ読み、 – 数字ならスタックに積む – 演算子なら、スタックから2つ取り出して計算して、その結 果をスタックに積む 1 2 + 4 6 + * (1+2)*(4+6)=? 1 2 + 4 6 + * 6 2 1 1 3 4 4 10 3 3 3 30 Algorithms and Data Structures on C NRPCalc.cc /*** *** 逆ポーランド記法電卓 ***/ #include "StackFloat.h" floatを要素とするスタック (StackIntを参考に) // メイン int main(int argc,char **argv){ // 起動メッセージ printf("*** Reverse Polish Notation Calculator ***¥n"); // スタックを準備する StackFloat *sf=makeStackFloat(100); 続く Algorithms and Data Structures on C NRPCalc.cc // 入力待ちループ int end_flag=0; double n1,n2; char buf[100]; while(!end_flag){ if(scanf("%s",buf)!=1) break; switch(buf[0]){ case '+': n2=pop(sf); n1=pop(sf); push(sf,n1+n2); case '-': n2=pop(sf); n1=pop(sf); push(sf,n1-n2); case '*': n2=pop(sf); n1=pop(sf); push(sf,n1*n2); case '/': n2=pop(sf); n1=pop(sf); push(sf,n1/n2); case '=': printf("%f¥n",peek(sf)); break; case 'p': print(sf); break; case 'q': end_flag=1; break; default : push(sf,atof(buf)); } } } // スタックを開放する free(sf); break; break; break; break; Algorithms and Data Structures on C 逆ポーランド電卓の実行結果 1+2=? $ ./RPNCalc 1 2 + = 3.0 4と5と6を追加 → リスト 4 5 6 p 4: 3.0 4.0 5.0 6.0 (5+6)*4 → リスト + * p 2: 3.0 44.0 (0-1)*44+3 → リスト 0 1 - * + p 1: -41.0 -41+? + 1つしか要素が入っ Error: stack underflow ていないスタックか $ ら2つの要素を 取り出そうとした Algorithms and Data Structures on C 待ち行列(Queue) • 挿入が一方の端、削除がもう一方の端 – 生協のレジの行列 • 客は行列の最後尾に並ぶ • レジが終わると、行列の先頭の客が抜ける • FIFO(First In First Out) 待ち行列 レジ Algorithms and Data Structures on C 待ち行列の構造 • パイプに詰め込まれた積み木の構造 – – – – 詰め込むの端→末尾(rear) 取り出す端→先頭(front) 要素の追加→待ち行列に入れる(enqueue) 要素の削除→待ち行列から取り出す(dequeue) dequeue enqueue front rear Algorithms and Data Structures on C 待ち行列の利用 • マルチタスク処理 – 複数のタスクを1つのCPUで処理するには、それ ぞれのタスクを待ち行列に入れて、CPUが1つず つそれを取り出して処理する。 • 印刷処理 – 1つのプリンタで複数の印刷物を印刷する場合は、 各印刷データは印刷キューに入れられ、プリンタ が1つずつ順番に取り出して印刷する。 Algorithms and Data Structures on C 配列による待ち行列の実現 先頭 front 末尾 こちらへ移動していく rear ・・・ ・・・ 先頭が抜ける たびに移動 無限に長い配列? 待ち行列 長さ=待っている要素数 末尾に来る たびに移動 Algorithms and Data Structures on C リングバッファによる待ち行列の実現 • 無限に長い配列は現実には不可能 • リングバッファを使用する – 有限長の配列の先頭と末尾をつないだもの – 配列 q[] で、q[M-1] の次に q[0] があると考える – 具体的には、配列サイズMの剰余をインデックスとする q[M-2] リング バッファ q[M-1] q[0] q[1] 待ち行列 q[2] front q[3] rear Algorithms and Data Structures on C リングバッファの利用法 • 初期状態:front=1,rear=0 • 取り出し: – frontの位置の要素を取り出してからfrontを移動→front++ – q[(front++)%M]にアクセス • 詰め込み: – rearを移動してからrear次の位置に入れる→++rear – q[(++rear)%M]にアクセス • %Mを行うことで、リングバッファになる q[] 0 1 2 ・・・ ・・・ q[front%M] q[rear%M] %M M-1 Algorithms and Data Structures on C リングバッファの注意 • front=rear+1の状態は空?、フル? – 待ち行列の長さを保持することによって判断できる – 追加・削除時の制限 0 1 2 ・・・ length=0 ・・・ M-1 取り出せない! front=rear+1 0 1 2 ・・・ front length=M rear ・・・ M-1 詰め込めない! front=rear+1 Algorithms and Data Structures on C QInt.h #ifndef __QInt__h #define __QInt__h /*** *** 整数用キュー ***/ #include <stdio.h> #include <stdlib.h> // 整数キュー型の宣言 typedef struct { int *q; int size; int front,rear; int length; } QInt; // // // // キュー キューサイズ キューポインタ キューに実際に入っているデータ数 続く Algorithms and Data Structures on C QInt.h // プロトタイプ宣言 QInt *makeQInt(int s); void free(QInt *qi); void enq(QInt *qi,int n); int deq(QInt *qi); int peek(QInt *qi); int getSize(QInt *qi); void print(QInt *qi); #endif // __QInt__h // // // // // // // 整数キューの生成 キューの破棄 エンキュー デキュー ピーク 要素数 内部の表示 Algorithms and Data Structures on C QInt.cc /*** *** QIntの実装 ***/ #include "QInt.h" // 整数キューの生成 QInt *makeQInt(int s){ QInt *qi=(QInt*)malloc(sizeof(QInt)); qi->size=s; qi->q=(int*)malloc(qi->size*sizeof(int)); qi->front=1; qi->rear=0; qi->length=0; return qi; } 続く Algorithms and Data Structures on C QInt.cc 0 // エンキュー void enq(QInt *qi,int n){ if(qi->length==qi->size){ fprintf(stderr,"Error: ....¥n"); exit(1); } 1 2 ・・・ num=0 rear front enqueue num=1 enqueue処理 } // デキュー int deq(QInt *qi){ if(qi->length==0){ fprintf(stderr,"Error: ....¥n"); exit(1); } dequeue処理 } 続く enqueue num=2 enqueue num=3 dequeue num=2 Algorithms and Data Structures on C QInt.cc // ピーク int peek(QInt *qi){ if(qi->length==0){ fprintf(stderr,"Error: queue underflow.¥n"); exit(1); } return qi->q[qi->front]; } // 要素数 int getSize(QInt *qi){ return qi->length; } 続く Algorithms and Data Structures on C QInt.cc // 内部の表示 void print(QInt *qi){ printf("%d:",qi->length); for(int i=0;i<qi->length;i++) printf("%d ",qi->q[(qi->front+i)%qi->size]); printf("¥n"); } // キューの解放 void free(QInt *qi){ free(qi->q); free((void*)qi); } Algorithms and Data Structures on C QTest.cc /*** キューのテスト ***/ #include "QInt.h" int main(int argc,char **argv){ QInt *qi=makeQInt(100); print(qi); enq(qi,1); enq(qi,2); enq(qi,3); enq(qi,4); print(qi); deq(qi); deq(qi); print(qi); enq(qi,5); enq(qi,6); print(qi); deq(qi); deq(qi); deq(qi); print(qi); deq(qi); deq(qi); print(qi); free(qi); } Algorithms and Data Structures on C 簡単な待ち行列の例(結果) $ ./TestQueue 0: 4: 1 2 3 4 2: 3 4 4: 3 4 5 6 1: 6 Error: queue underflow $ 1つしか要素が入っ ていない待ち行列か ら2つの要素を 取り出そうとした Algorithms and Data Structures on C 課題161114 • QInt.ccのenq()とdeq()の不足しているコードを示せ。 • QIntを用いて、10個の整数をキューに入れ、それを取り出して表示する プログラムQTest2.ccを作成せよ。実行結果を示せ。 • 作成方法: • ソースコードを入力し、不足部分を補い、コンパイル・実行し、その 結果を示すこと。 • レポートはワードで作成すること。 • レポートの最初に学籍番号と氏名を明記すること。 • ワードのファイル名は、”scXXXXXX-al161114.docx” とすること。 • 完成したソースファイルを、ワード文書中に貼り付けて示せ。 • 提出方法: • メールで。メールの本文中にも学籍番号を氏名を明記すること。 • 提出先:fuchida@ibe.kagoshima-u.ac.jp • メールタイトル:”アルゴリズム課題161114” ←厳守! • 期限:2016年11月20日(日) 24:00 Algorithms and Data Structures on C スタックと待ち行列 終了
© Copyright 2024 Paperzz