アルゴリズムとデータ構造・付録

--Cで記述したアルゴリズム集--

平田富夫 著 森北出版 1990年
これは 森北出版株式会社 より出版されている「アルゴリズムとデータ構造」 の付録である。この本(以下では本書という)では、アルゴリズムを記述する にあたってプログラミング言語Pascalを用いた。これは、Pascalが、プログラ ミングの方法を教える際の導入教育には好個の言語であると広く認識されており、 本書を用いてアルゴリズムやデータ構造を学ぶ読者の多くはPascalを最初のプロ グラミング言語として学んでいるであろうという考えからであった。 また、これまでのアルゴリズム関係のテキストでもアルゴリズムの記述にはPascal またはそれに似たプログラミング言語(たとえばPidgin AlgolやModula-2など)が 使われることが多かった。

一方、教育現場におけるUNIXオペレーティング・システムの普及は近年とくに めざましく、そのようなところではUNIXとの整合性と実用性の観点からC言語を プログラミングの導入教育に採用するところが多い。しかし、C言語を初めて 学んだばかりの読者にとって、Pascalで記述された本書のアルゴリズムを正確に 理解するのは難しいかもしれない。(C言語に習熟したあとであれば、想像力を 働かして本書のPascal記述のアルゴリズムを理解するのは容易であろうと思われ るが。) そこで、そのような読者のために本書のアルゴリズムをすべてC言語で記述しなお したのがこの付録である。ご活用いただければ幸いである。

この付録を使用するにあたっての注意を以下に述べておく。

  1. 本冊子の図番号は本書の図番号に一致している。
  2. 原則として、本書のPascal記述との対応が分かるようにC言語で置き換え 記述している。そのため、C言語特有の簡潔な表現を犠牲にしている箇所も多い。
  3. C言語における配列のインデックスは0からはじまるが、本書のPascal記述 との対応を考慮し、インデックス1からを使用している場合がある。たとえば、 本冊子の図2・9で、配列boxのサイズはSMAX(C言語での慣習に従い、定数smaxは 大文字になっている。)と宣言されているのでbox[0]からbox[SMAX-1]までが使用 可能であるが、本書のアルゴリズムに合わせてbox[0]は使わず、box[1]から使用して いる。
  4. 図3・10では、Segmentation errorを避けるために、新たにif文を挿入した 箇所がある。
  5. 図6・2のアルゴリズムを実際に計算機で実行するには、数学ライブラリが 必要である。コンパイル(またはリンケージ)の時にその旨を指示しなければ ならない。

1章, 5章,
2章, 6章,
3章, 7章,
4章, 8章


前ページに戻る

_______________________________________________________ int gcd(int m, int n) { if (n==0) return(m); else return(gcd(n, m % n)); } ________________________________________________________ 図 1・1 最大公約数を計算する関数 _______________________________________________________ { a=1; if (a>2) if (a==3) a=2; else a=3; else a=4; printf("%d",a); } _______________________________________________________ 図 1・5 前ページに戻る _______________________________________________________ struct element { char data; struct element *next; }; _______________________________________________________ 図 2・3 ポインター型によるリストの実現 ____________________________________________________________ struct element *new() { void *malloc(); return((struct element *) malloc(sizeof(struct element))); } struct element *create() { struct element *p, *new(); p=new(); p->next=NULL; return(p); } void insert(struct element *l, int k, char item) { struct element *p, *new(); if (k>1) insert(l->next,k-1,item); else { p=new(); p->data=item; p->next=l->next; l->next=p; } } void delete(struct element *l, int k) { if (k>1) delete(l->next,k-1); else l->next=l->next->next; } char access(struct element *l, int k) { if (k>1) return(access(l->next,k-1)); else return(l->next->data); } int member(struct element *l, int x) { while(l->next!=NULL && l->next->data!=x) l=l->next; return((l->next!=NULL)? 1:0); } ____________________________________________________________ 図 2・4 リストの基本操作 _______________________________________________________ struct stack { char box[SMAX]; int top; }; void create(struct stack *s) { s->top=0; } void push(struct stack *s, char item) { s->box[++s->top]=item; } void pop(struct stack *s) { --s->top; } int empty(struct stack *s) /* FALSE=0 TRUE=1 */ { return(s->top==0); } char top(struct stack *s) { return(s->box[s->top]); } _______________________________________________________ 図 2・9 スタックの基本操作 _______________________________________________________ struct queue { char box[QMAX]; int front,rear; }; void create(struct queue *q) { q->front=1; q->rear=0; } void insert(struct queue *q, char item) { q->box[++q->rear]=item; } void delete(struct queue *q) { ++q->front; } int empty(struct queue *q) /* FALSE=0 TRUE=1 */ { return(q->rear < q->front); } top(q) struct queue *q; { return(q->box[q->front]); } _______________________________________________________ 図 2・12 キューの基本操作 ____________________________________________________________ struct heap { int box[HMAX]; int size; }; void swap(int *u,int *v) { int temp; temp=*u; *u=*v; *v=temp; } void create(struct heap *h) { h->size=0; } void insert(struct heap *h,int item) { int i; i=++h->size; h->box[i]=item; while(i>1 && h->box[i] < h->box[i/2]){ swap(&h->box[i], &h->box[i/2]); i/=2; } } int findmin(struct heap *h) { return(h->box[1]); } void deletemin(struct heap *h) { int i,k; i=1; h->box[1] = h->box[h->size]; --h->size; while (2*i < h->size){ k=2*i; if (k < h->size && h->box[k] > h-> box[k+1]) k++; if (h->box[i] <= h->box[k]) break; swap(&h->box[i],&h->box[k]); i=k; } } ____________________________________________________________ 図 2・15 ヒープの基本操作 前ページに戻る ________________________________________________________ int member(int x) { int m,l,r; l=1; r=n; do { m=(l+r)/2; if (x<a[m]) r=m-1; else l=m+1; } while(l<=r && x!=a[m]); return(x==a[m]); } ___________________________________________________________ 図 3・1 2分探索 ___________________________________________________________ struct vertex { int data; struct vertex *l,*r; } *root; (a) struct vertex *new() { void *malloc(); return((struct vertex *) malloc(sizeof(struct vertex))); } struct vertex *create() { struct vertex *p, *new(); p=new(); p->data=0; p->r=NULL; return(p); } (b) int member(int x) { struct vertex *p; p=root; do { if (x < p->data) p=p->l; else p=p->r; } while(p!=NULL && x!=p->data); return(p!=NULL); } (c) ____________________________________________________________ 図 3・3 2分探索木上の基本操作 I ________________________________________________________ void insert(int x) { struct vertex *p,*f,*new(); p=root; do { f=p; if (x < p->data) p=p->l; else p=p->r; } while (p!=NULL); p=new(); p->data=x; p->l=p->r=NULL; if (x < f->data) f->l=p; else f->r=p; } _______________________________________________________ 図 3・4 2分探索木上の基本操作 II ________________________________________________________ void delete(int x) { struct vertex *f,*p,*q; p=root; do { f=p; if (x < p->data) p=p->l; else p=p->r; } while(x!=p->data); if (p->l==NULL || p->r==NULL){ if (p->r==NULL) q=p->l; else q=p->r; if (f->l==p) f->l=q; else f->r=q; } else { q=p->r; f=q; while(q->l!=NULL){ f=q; q=q->l; } p->data=q->data; if (q==f) p->r=q->r; else f->l=q->r; } } ________________________________________________________ 図 3・5 2分探索木上の基本操作 III ____________________________________________________________ struct vertex { int data; int red; struct vertex *l,*r; } *root; (a) struct vertex *new() { void *malloc(); return((struct vertex *) malloc(sizeof(struct vertex))); } struct vertex *create() { struct vertex *p, *new(); p=new(); p->data=0; p->r=NULL; p->red=0; return(p); } (b) ____________________________________________________________ 図 3・7 2色木の実現 ____________________________________________________________ void rotate(struct vertex *p,struct vertex *f,struct vertex *g) { if (f->l==p){ f->l=p->r; p->r=f; } else { f->r=p->l; p->l=f; } if (g->l==f) g->l=p; else g->r=p; } ____________________________________________________________ 図 3・9 手続き rotate(p,f,g) ____________________________________________________________ void redden(struct vertex *p,struct vertex *f,struct vertex *g,struct vertex *gg) { p->red=1; if (p->l!=NULL && p->r!=NULL){ p->l->red=0; p->r->red=0; } if (f->red==1){ if ((g->l==f)==(f->l==p)){ rotate(f,g,gg); f->red=0; g->red=1; } else { rotate(p,f,g); rotate(p,g,gg); p->red=0; g->red=1; } } root->r->red=0; } (a) void insert(int x) { struct vertex *p,*f,*g,*gg, *new(); p=root; do { gg=g; g=f;f=p; if (x < p->data) p=p->l; else p=p->r; if (p!=NULL && p->l!=NULL && p->r!=NULL) if (p->l->red==1 && p->r->red==1) redden(p,f,g,gg); } while (p!=NULL); p=new(); p->data=x; p->l=p->r=NULL; if (x < f->data) f->l=p; else f->r=p; redden(p,f,g,gg); } (b) ____________________________________________________________ 図 3・10 2色木上の基本操作 ____________________________________________________________ void calctable() { int i,j,k,l,m; float temp; for (i=0;i<=n;i++){ c[i+1][i]=0; w[i+1][i]=B[i]; } for (l=0;l<=n-1;l++){ for (i=1;i<=n-l;i++){ j=i+l; w[i][j]=w[i][j-1]+A[j]+B[j]; temp=1000.0; for (k=i;k<=j;k++){ if (temp > c[i][k-1]+c[k+1][j]){ temp=c[i][k-1]+c[k+1][j]; m=k; } } c[i][j]=w[i][j]+c[i][m-1]+c[m+1][j]; r[i][j]=m; } } } ____________________________________________________________ 図 3・15 最適2分探索木のコストを計算する手続き ____________________________________________________________ struct vertex *maketree(int i,int j) { struct vertex *p, *new(); int m; if (i>j) return(NULL); else { p=new();m=r[i][j]; p->data=a[m]; p->l=maketree(i,m-1); p->r=maketree(m+1,j); return(p); } } ____________________________________________________________ 図 3・17 最適2分探索木の作成 _______________________________________________________ struct element *a[m]; int h(int x) { return(x % m); } void createhash() { struct element *create(); int i; for (i=0;i<=m-1;i++) a[i]=create(); } void inserthash(int x) { insert(a[h(x)],1,x); } void deletehash(int x) { deleteall(a[h(x)],x); } int memberhash(int x) { return(member(a[h(x)],x)); } _______________________________________________________ 図 3・19 ハッシングの基本操作 _______________________________________________________ void preorder(struct vertex *p) { if(p!=NULL){ printf("%d ",p->data); preorder(p->l); preorder(p->r); } } (a) void inorder(struct vertex *p) { if(p!=NULL){ inorder(p->l); printf("%d ",p->data); inorder(p->r); } } (b) void postorder(struct vertex *p) { if(p!=NULL){ postorder(p->l); postorder(p->r); printf("%d ",p->data); } } (c) ________________________________________________________ 図 3・20 2分木を走査する手続き ________________________________________________________ void printtree(struct vertex *p) { int i; if(p!=NULL){ d++; printtree(p->r); for (i=1;i<=d;i++) printf(" "); printf("%5d\n",p->data); printtree(p->l); d--; } } main() { d=0; printtree(root->r); } _______________________________________________________ 図 3・21 2分木の印刷を行なう手続き 前ページに戻る _______________________________________________________ struct queue q[m]; void qprint(struct queue *q) { while(!empty(q)){ printf("%d ",top(q)); delete(q); } } void bucketsort() { int i; for (i=0;i<=m;i++) create(&q[i]); for (i=0;i<=n;i++) insert(&q[a[i]],a[i]); for (i=0;i<=m;i++) qprint(&q[i]); } ________________________________________________________ 図 4・1 バケットソート ______________________________________________________ void selectionsort() { int i,j,min; for(j=1;j<=n-1;j++){ min=j; for (i=j+1; i<=n;i++) if (a[min]>a[i]) min=i; swap(&a[j],&a[min]); } } ______________________________________________________ 図 4・2 選 択 法 _______________________________________________________ void insertionsort() { int i,j,temp; for (i=2;i<=n;i++){ temp=a[i]; j=i; while (j>1 && a[j-1]>temp){ a[j]=a[j-1]; j=j-1; } a[j]=temp; } } _______________________________________________________ 図 4・3 挿 入 法 _______________________________________________________ void bubblesort() { int i,j,sorted; j=n; do { sorted=1; j=j-1; for (i=1;i<=j;i++) if (a[i]>a[i+1]){ swap(&a[i],&a[i+1]); sorted=0; } } while (!sorted); } _______________________________________________________ 図 4・4 バブルソート ____________________________________________________________ void merge(int p,int n) { int i,j,k,h,b[AMAX]; h=n/2; i=p; j=p+h; for (k=p;k<p+n;k++) if (j==p+n || (i<p+h && a[i]<=a[j])) b[k]=a[i++]; else b[k]=a[j++]; for (k=p;k<p+n;k++) a[k]=b[k]; } void msort(int p,int n) { int h; if (n>1){ h=n/2; msort(p,h); msort(p+h,n-h); merge(p,n); } } main() { msort(1,n); } ____________________________________________________________ 図 4・6 マージソート ____________________________________________________________ void partition(int p,int q,int *jp,int *ip) { int i,j,s; i=p; j=q; s=a[p]; while (i<=j){ while (a[i]<s) ++i; while (a[j]>s) --j; if (i<=j){ swap(&a[i],&a[j]); ++i;j--; } } *jp=j; *ip=i; } void qsort(int p,int q) { int i,j; if (p<q){ partition(p,q,&j,&i); qsort(p,j); qsort(i,q); } } main() { qsort(1,n); } ____________________________________________________________ 図 4・9 クイックソート ________________________________________________________ void quicksort() { struct stack s; int p,q,i,j; create(&s); push(&s,n);push(&s,1); while (empty(&s)==0){ p=top(&s);pop(&s); q=top(&s);pop(&s); partition(p,q,&j,&i); if (p<j){ push(&s,j);push(&s,p); } if (i<q){ push(&s,q);push(&s,i); } } } ________________________________________________________ 図 4・10 クイックソート(非再帰版) _______________________________________________________ void heapsort() { struct heap h; int i; create(&h); for (i=1;i<=n;i++) insert(&h,a[i]); for (i=1;i<=n;i++){ a[i]=findmin(&h); deletemin(&h); } } _______________________________________________________ 図 4・11 ヒープソート _______________________________________________________ void heapify(int i,int j) { int k; k=2*i; if (k<=j){ if (k!=j && a[k]>a[k+1]) k++; if (a[i]>a[k]){ swap(&a[i],&a[k]); heapify(k,j); } } } void makeheap() { int i; for (i=n;1<=i;i--) heapify(i,n); } main() { int i; makeheap(); for (i=n;2<=i;i--){ swap(&a[1],&a[i]); heapify(1,i-1); } } _______________________________________________________ 図 4・12 ヒープソート(改良版) 前ページに戻る _______________________________________________________ void stringmatching() { int i=1,j=1; while(i<=m && j<=n) if (p[i]==t[j]){ i++;j++; } else { j=j-i+2;i=1; } if (i==m+1) printf("Found at %d",j-m); else printf("Not found"); } _______________________________________________________ 図 5・2 素朴なアルゴリズム ____________________________________________________________ void kmp() { int i=1,j=1; compf(); while (i<=m && j<=n) if (i==0 || p[i]==t[j]){ i++;j++; } else i=f[i]; if (i==m+1) printf("Found at %d",j-m); else printf("Not found"); } ____________________________________________________________ 図 5・4 クヌース・モーリス・プラットのアルゴリズム _______________________________________________________ void compf() { int i=0,j=1; f[1]=0; while (j<m) if (i==0 || p[i]==p[j]) f[++j]=++i; else i=f[i]; } ________________________________________________________ 図5・5 失敗関数の計算 ______________________________________________________ void bm() { int i,j; i=j=m; compd(); compdd(); while (i>=1 && j<=n) if (p[i]==t[j]){ i--; j--; } else { j=j+max(d[t[j]],dd[i]); i=m; } if (i==0) printf("Found at %d",j+1); else printf("Not found"); } ______________________________________________________ 図 5・6 ボイヤー・ムーアのアルゴリズム ______________________________________________________ void compd() { int i; for (i=0;i<=255;i++) d[i]=m; for (i=1;i<=m;i++) d[p[i]]=m-i; } ______________________________________________________ 図 5・7 d[c]の計算 ______________________________________________________ void compdd() { int i,j; for(i=1;i<=m;i++) dd[i]=2*m-i; /* case 1 */ i=m; j=m+1; f[m]=m+1; while (i>0) if (j==m+1 || p[i]==p[j]) f[--i]=--j; else { dd[j]=min(dd[j],m-i); j=f[j]; } /* case 2 */ for (i=1;i<=m;i++){ if (i>j) j=f[j]; dd[i]=min(dd[i],m+j-i); } } _______________________________________________________ 図 5・9 dd[i]の計算 前ページに戻る ____________________________________________________________ typedef struct {float r,i;} complex; complex x[AMAX]; int n; void butterfly(int n,complex x[],complex y[],complex z[]) { complex plus(),minus(),multiply(),w(); int k; for(k=0;k<=n/2-1;k++){ y[k]=plus(x[k],x[k+n/2]); z[k]=multiply(minus(x[k],x[k+n/2]),w(n,k)); } } void shuffle(int n,complex x[],complex y[],complex z[]) { int k; for (k=0;k<=n/2-1;k++){ x[2*k]=y[k]; x[2*k+1]=z[k]; } } void fft(int n,complex x[]) { int k; complex y[AMAX],z[AMAX]; if (n>1){ butterfly(n,x,y,z); fft(n/2,y); fft(n/2,z); shuffle(n,x,y,z); } } main() { fft(n,x); } ____________________________________________________________ 図 6・2 FFTのアルゴリズム 前ページに戻る ____________________________________________________________ void dfs(int v) { int w; dfnum[v]=c++; for (w in adjlist[v]){ if (dfnum[w]==0){ 辺(v,w)をTに加える; dfs(w); } } } main() { c=1;T=φ; for (v in V) dfnum[v]=0; while(dfnum[v]==0となるvがある) dfs(v); } ____________________________________________________________ 図 7・5 深さ優先の探索を行う手続き ____________________________________________________________ void bfs(int v) { int u,w; bfnum[v]=c++; create(&q);insert(&q,v); /* キューの初期化 */ do { u=top(&q);delete(&q); for (w adjlist[u]){ if (bfnum[w]==0){ bfnum[w]=c++; insert(&q,w); } } } while (!empty(&q)); } main() { c=1; for (v in V) bfnum[v]=0; while (bfnum[v]==0となるvがある) bfs(v); } ____________________________________________________________ 図 7・7 幅優先の探索を行う手続き ____________________________________________________________ void dfsb(int v) { int w; dfnum[v]=c++; L[v]=dfnum[v]; for (w in adjlist[v]){ if (dfnum[w]==0){ push(&s,(v,w));/* 辺(v,w)をスタックにプッシュ */ father[w]=v; dfsb(w); if (L[w]>=dfnum[v]){ 辺(v,w)が出力されるまでスタック から辺をポップし出力する; } L[v]=min(L[v],L[w]); } else if (w!=father[v] && dfnum[w]<dfnum[v]){ push(&s,(v,w)); /* 逆辺(v,w)をプッシュ */ L[v]=min(L[v],dfnum[w]); } } } main() { c=1;create(&s); for (v in V) dfnum[v]=0; dfsb(v1); } ____________________________________________________________ 図 7・10 グラフの2連結成分を見つけるアルゴリズム _______________________________________________________ 1 void kruskal() 2 { 3 int i; 4 重さの小さい順に辺を並べた列をe1,e2,...,e3とする; 5 i=0; T=φ ; 6 while (|T|!=n-1){ 7 i++; 8 if (T {ei}が閉路をふくまない) T=T {ei}; 9 } 10 } _______________________________________________________ 図 7・13 クルスカルのアルゴリズム _______________________________________________________ void create() { int i; for (i=1;i<=n;i++){ p[i]=i; rank[i]=0; } } void union(int s1,int s2) { if (rank[s1]<rank[s2]) p[s1]=s[2]; else p[s2]=s[1]; if (rank[s1]==rank[s2]) ++rank[s1]; } void find(int i) { if (p[i]==i) return(i); else return(find(p[i])); } _______________________________________________________ 図 7・16 unionとfindを行う手続き _______________________________________________________ 8 if (find(u)!=find(v)){ T=T {ei}; union(find(u),find(v)); } _______________________________________________________ 図 7・17 図 7・13の8行目の実現 ________________________________________________________ void dijkstra() { T=V-{s}; D[s]=0; for (v in V-{s}) D[v]=w[s][v]; while (T!= φ ){ Tの頂点でD[v]の値いが最小となるものをuとする; T=T-{u}; for (v in T) D[v]=min(D[v],D[u]+w[u][v]); } } ________________________________________________________ 図 7・18 ダイクストラのアルゴリズム ________________________________________________________ void warshall-floyd() { int i,j,k; for (i=1;i<=n;j++) for (j=1;j<=n;j++) d[i][j]=w[i][j]; for (k=1;k<=n;k++) for (i=1;i<=n;i++) for (j=1;j<=n;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } ________________________________________________________ 図7・21 ワーシャル・フロイドのアルゴリズム ____________________________________________________________ void findpath() { visit[s]=1;reached=0; for (v V-{s}) visit[v]=0; for (v V) d[v]=INFINITY; create(&q);insert(&q,s); /* キューの初期化 */ do { v=top(&q);delete(&q); for (w adjlist[v]){ if (!visit[w] && f[v][w]<c[v][w]){ visit[w]=1; insert(&q,w); p[w]=v; d[w]=min(d[v],c[v][w]-f[v][w]); if (w==t) reached=1; } } } while (!empty(&q) && !reached); } void augment() { v=t; if (reached) do { f[p[v]][v]+=d[t]; f[v][p[v]]-=d[t]; v=p[v]; } while (v!=s); } main() { do { findpath(); augment(); } while (!empty(&q)); } ____________________________________________________________ 図7・25 フォード・フルカーソンのアルゴリズム 前ページに戻る ____________________________________________________________ void costcomp() { int d,i,j,k; for (i=1;d<=n;i++) mii=0; for (d=1;d<=n-1;d++) for (i=1;i<=n-d;i++){ j=i+d; mij=MIN(mik + mk+1 j + PiPkPj); i<=k<j } } ____________________________________________________________ 図 8・1 行列の積の演算コストを求めるアルゴリズム ____________________________________________________________ void SEARCH(int v) { visit[v]=++level; for ((v,w) E) if (!visit[w]) SEARCH(w); visit[v]=0; --level; } main { for (v in V)) visit[v]=0; level=0; SEARCH(A); } (a) if (!visit[w] && (w!=C || visit[B])) SEARCH(w); (b) ____________________________________________________________ 図 8・2 ハミルトン閉路を列挙する手続き 前ページに戻る