2005年11月16日
アルゴリズムパズル(リンクリスト)の解答編
- 固定リンク
- コメント (1)
- トラックバック (1)
- カテゴリー:技術メモ
アクセス数が通常の2倍以上になっている。数日前のエントリー「アルゴリズムパズル(リンクリスト)」が大人気だ。既にトラックバックも4つほどついていて、解答も明らかになっている。別解まで出てきて大盛況だ。
以前書いた "Duff's Device" も面白いと思ったのだが、こちらへの反応は薄かった。パズルじゃなかったからか?
自分で解答を書こうと思っていたが、既に猪俣健太郎さんによる「アルゴリズムパズル(リンクリスト)に答える」に自分が予定していたより丁寧で分かりやすいものがある。これが、夕食を食べながら僕らがたどり着いた解答だった(出張中、皆で夕食を食べている時に出された問題だったのだ)。メイン部分を引用すると、
ポインタaとbを用意して、aは1ずつ、bは2ずつインクリメントする(次の要素に進む)。
もしループしているなら、aがループによって以前の要素に戻ってくる前に必ずbと同じ位置を指すときが来る。
ループしてなければbが終端に到達して終わり。
更に図を使った説明もあるので、興味のある方はリンク先を参照ください。
そして、やねうらおさんの「単方向linked listの循環参照判定をO(n)で行なう」には別解が示されている。これは思いつかなかったなあ。
bool isCyclic(list*p){
int n=1,m=n;
list*q=null;
while(p!=null) {
if(p==q) return true;
if(--m ==0) { q=p; n<<=1; m=n; }
p = p->next;
}
return false;
}
ポインタを2つ(p, q)用意する。qを固定して、pを1つづつ、一定長分だけ動かす。その途中でpがqと同じところを指せば循環参照検出。pが終端に達すれば循環参照無し。
その一定長でどちらの結論も出ない場合、qをpの現在位置に持ってきて(←ここが頭いいなあ)、「一定長」を更に長くして同じことを繰り返す。この擬似コードでは、一定長の増やし方は2のn乗でやっているが(n<<1 の部分)、効率を気にしなければ増やし方はどんな方法でも良いはずだ。
ytqwertyさんの「循環参照した短方向リンクリストを O(n) で検出する」にもこのコードについての説明がある。笑える解答と一緒に。
どちらの解も、ポインタの動きをアニメーションで表現すれば分かりやすくなる。やねさんの解答の方が見た目は面白い動きをするだろう。ああ、どうでもいい話だ。
さらにどうでもいい話として、猪俣さんの解答の方が優れている部分は、夕食の場で口だけで説明するのが簡単だということ。やねさんの解答をあの場で思いついても「解けた」ことにはならなかったかもしれない。説明が大変だからなあ。
最後に、無粋ながら、やねさんの記事中で気になったことが1つ。
私が考えついたほうのアルゴリズムは独特で、効率も上のと似たりよったりだから(むしろ、私のアルゴリズムのほうがメモリアクセスが少ないぶん、速い意味がある)
確かにやねさんのアルゴリズムのほうがメモリアクセスが少ない。しかし、ノードの数をnとした場合、比較の回数は最悪のケースで2n回となる。それに比べて、猪俣さんのアルゴリズムではn回で済む。細かい話だが、他人の引用以外に言うことがほとんど残っていないので、1つぐらい何か言っておきたかったのだ。以上!
- 固定リンク
- コメント (1)
- トラックバック (1)
- カテゴリー:技術メモ

Trackback on "アルゴリズムパズル(リンクリスト)の解答編"
以下1件のトラックバックはこのページのエントリー"アルゴリズムパズル(リンクリスト)の解答編"を参照しています。
このエントリーのトラックバックURL:

ポインタの動きをアニメーションで表現って面白そうですね。
ベンチマークで実際に速度を調べてみるのも面白そう。
やねさんの見つからない(追いつかない)たびに倍々(n<<=1)にマーカー(q)が移動する方法だと、ノード数(n)が1024,2048,4096あたりを超えるとき処理量が増加し、それ以外はO(n)的です。増加といってもいまどきのPCなら高速に答えが出るとても小さな処理時間けどね。少しずつ大きくなる斜めになった階段風だけど全体的に見るとO(n)です。