あわわ。〆切明日だ

というかほとんど今日だ。
成人式やら同窓会やらと、情報特別演習とかいう授業のレポートが終わらなくてカツカツでついぞさっきやっと終わらせた。


〆切が朝10時な課題。
むしろ前日〆切、って言ってくれた方が助かるというね。
10時とか気づいたらオーバーしてるべ。


ということで、今回も課題をといてみてみた。

とりあえず課題を確認。

メモした記憶があるのに残ってなかった。何故。
友人に尋ねてどうにか判明。


課題31 : 引数を重複を削除して表示する。
%./a.out a ab abc ab abc
a
ab
abc
と言った感じカナ。出力の順番は問わない。

課題32 : 引数を多い順から5個表示する。
%./a.out a b c d e f b c d e f c d e f d e f e f
f
e
d
c
b
と言った感じカナ。

課題33 : 引数を(バケットソートを使って)多い順に表示する。
%./a.out a b c d e f b c d e f c d e f d e f e f
f
e
d
c
b
a
と言った感じカナ。各引数の出現数は16回を上限とする。


ではではGoGo!!

リストを使った課題でございます。
こんな(↓)構造体を使います。

struct node {
	char* data;
	struct node* next;
};

まぁ、細かいとこは別に一緒でなくてもいいけど。
これだと毎回毎回"struct node"って書かないといけんのでめんどい。
ので

typedef struct node {
	char* data;
	struct node* next;
} node;

とかやっておけば"node"だけでおっけ。

で、リストを使う基本をいくつか。


まず、リストは頭付きリスト*1が便利。
リストだけを単純に使うより、先頭に「無意味な」アイテムを一個作るだけで実装が数段らくになる。
で、まぁ、先頭をfirstとか名前にしておいて、最初はnextをNULLにしておく。

node first;
first.next = NULL

みたいな感じで。(firstをポインタで宣言した場合は"first->next"を使うべし。


で、ループは基本こんな感じで。

node* p;
for( p = first.next; p != NULL; p ) {
	/* pに対してなにか処理 */
}

とか、


あと挿入。

p = (node*)malloc( sizeof( node ) );
p->next = first.next;
p->data = hoge;
first.next = p;

みたいな感じで。
順番とか気にしなくていい場合はfirst使って先頭に打ち込むと便利。
末尾に挿入は走査しないといけんし、直前の要素を記憶させとかなあかんし(順方向の場合は)、色々面倒。
ちなみにfirstとか用意しないと、最初の一個は特別に処理しないといけなかったりとかする。


で、各課題毎のアプローチ。

課題31

簡単な実装は多分

  • for( i = 1; i < argc; i++ )でargvをみていく。
  • argv[i]に対して、リストにあるかどうかをチェック!!
    • あったらスルー(リストに追加しない
    • なかったらリストに追加(リストのnodeを新しく作る。

という感じカナ。

多分20行ちょい。


リストにある/ないの判断はフラグ一個用意してもいいけど、ループを終えた後のpをチェックすると便利。

for( p = first.next; p != NULL; p ) {
	if( strcmp( ... ) == 0 ) {
		break;
	}
}

の様な場合、ループを向けた直後のpは
探したいデータが見つかった場合には、そのnodeへのポイントが、
見つからなかったらNULLが、
入っていることになるので、リストにある/ないは"p == NULL"が使える。

課題32

多い順に5個をもってこなならんのでやや面倒。
さきの31の課題をちょいちょいと変更していくといいカモ。


とりあえず多い順に、ってことなので出現数をしらべないといけない。
ということで、構造体に"count"とか加えるといい感じ。

typedef struct node {
	char* data;
	struct node* next;
	int count;
} node;

で、変数増やしても数えなきゃ意味が無いので、↑の「あったらスルー」の部分でカウントアップするようにする。
ループ抜けた直後のpがちょうど探してる要素へのポインタなので利用するとよし。
あとうっかり間違いが多いのは、nodeを作成する際に"p->count = 0;"としてしまうこと。
countは1で初期化しましょう。


で、数えられたら↑から5個もってくるだけ。
一番安直で簡単なのは、

  • "for( p = first.next; p != NULL; p = p->next )"でリスト全体を眺めて最大値を持ってくる
  • 最大のやつのcountを0で上書きしてしまう。

を5回繰り返す方法。
効率は悪いかもしれんが楽だし、とくに問題があるわけでもないのでよいとしましょう。

課題33

ソート。
それもバケットソート。
オーダーがO(N)という夢のソートバケットソート。
「クイック」ソートなんかよりもよっぽど速いのさ。爆速。爆速。


ということで、前半は↑の課題32と同じでおっけー。
数を数えます。
で、全部の出現個数が別ったところでバケットソート。


出現回数の上限が16と決められてるので16個のバケツを用意すればおっけーです(正確には15個。「0回出現」は「無い」ので
ただ、数字のバケットソートと違ってそう安易に実装できないのが難点。
リスト用に用意したnodeをそのまま使うと良い感じ。(というかバケツの中もリストになるので。

node buckets[ 16 ];	//16個の頭
/* 頭の初期化をこの辺に書く。 */
for( p = first.next; p != NUL; p = p->next ) {
	node* pp = (node*)malloc( sizeof( node ) );	//新しいノードを作って
	pp->data = p->data;
	pp->count = p->count;
	pp->next = buckets[ pp->count ].next;		//バケツに打ち込む。
	buckets[ p->count ].next = pp;
}

みたいな感じでやっていけばいいのカナ?
実際に提出したのとは別の方法で書いてるんで、正しいかどうかわからんというね(ぉぃ
コンパイラにも通してないし。まぁ、こんな雰囲気で。


まぁ、毎度のごとく、これ意味アンのか?てきなエントリですが...ではでは

*1:頭付きリスト : ググっても「そんな単語はねぇ」みたいなこと言われる...正式名称は何?