深さ優先探索の使用を検討するタイミングについて考えた。
再帰的な処理は多少かけるが、深さ優先探索を実装することがうまくできないことが多いので、すこしまとめてみた。
どんな時に使えるのか
- 「ある物/配列要素に対し操作1,2,3...のいずれかを行う(選ぶ/選ばないなど)」のパターン全列挙
- いくつかの文字の中から一つ選んで文字列sの末尾に継ぎ足していくような操作
- ある地点Aからある地点Bにたどり着けるかどうか
など、ステップごとに状態やルートが分岐する場合に利用。答えはYes/Noで求められると一番楽だが、最小/最大を選ぶような問題にも普通に使える。
実装について気を付けたい点
再帰を用いるとコールスタックがオーバーフローを起こす可能性がある。また、時間計算量がノード数に比例する。スタックを用いる場合はスタックが大きくなるほどメモリ確保が難しくなってくるのでスタックを積みまくるとこっちはこっちで遅くなる。
でもどこを見れば「そうだ深さ優先でいこう」ってなるのさ?
ある要素について操作方法が数パターンしかなく、それを全要素/データについて行うことが可能/必要となる場合
- 配列のある要素を選ぶ/選ばない:2パターン
- ある数字列に0~9のいずれかを末尾にくっつける:現在の数字列sに対し10パターン
- 現在地点から上下左右に移動:現在地点に対し移動方法が4パターン
例題としてABC42-Cを深さ優先でやってみた
この問題だとNから順にインクリメントして条件に合致するものを探す方が実装としては一番楽だと思うが、DFSの練習にはそこそこいい問題だと思う。
数値として小さいものから作られるわけではないので、リストに全部足しておくか、最小値を変数として保持し、新たにできた条件を満たす値が最小値より小さいかどうかみるなど必要。
深さ優先探索で解ける問題は、ほかの方法でも解けることも多いので、DFSにこだわる必要はないが、ロジックや実装のシンプルさや、そこからくるデバッグの簡単さなどを考えれば悪くないと思う。
たくさん分岐パターンがある方法をこれでやろうとすると大変なので、その時は違う方法を考えたほうがいいと思う。
他に何か思いついたら追記/新記事などに起こしたい。