C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

ログ内検索
  • キーワードを複数指定する場合は 半角スペース で区切ってください。
  • 検索条件は、(AND)=[A かつ B] (OR)=[A または B] となっています。
  • [返信]をクリックすると返信ページへ移動します。
キーワード/ 検索条件 /
検索範囲/ 強調表示/ ON (自動リンクOFF)
結果表示件数/ 記事No検索/ ON
大文字と小文字を区別する

No.20183 の関連記事表示

<< 0 >>
■20183  Re[4]: 再帰によるスタックオーバーフロー?
□投稿者/ siokoshou -(2008/06/07(Sat) 09:55:22)
    スタックオーバーフローが起きるのは、checkloopで経路の最初の始点としか比較していないためと思います。
    「EEE AAA, AAA BBB, BBB CCC, CCC AAA」のルートをたどり、次に「AAA BBB」の辺があるので、このルート内でサイクルになっていますが、最初の始点以外を記録して比較していないため、サイクルを検出できず、無限ループとなります。
    
    また、一つ目のサイクルしか見つけれないという別の問題もあります。
    たとえば、「AAA BBB, BBB CCC, CCC AAA」は見つけれるが、もう一つ「AAA BBB, BBB HHH, HHH CCC, CCC AAA」があったときにこれを見つけれません。
    
    さらに、単にルートが長い時、やはりスタックオーバーフローが起きるかもしれません。でも、面倒なので再起は取り除いていない
    コードを書いてみました。細かいところまで再現できないので、理解して改造してください。LINQ使ってます。
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace DetectCycle
    {
        class Program
        {
            static void Main()
            {
                var table = new[] {
                            new Field( "AAA", "BBB", 2008, 6 ),
                            new Field( "BBB", "CCC", 2008, 6 ),
                            new Field( "CCC", "AAA", 2008, 6 ),
                            new Field( "AAA", "DDD", 2008, 6 ),
                            new Field( "EEE", "AAA", 2008, 6 ),
                            new Field( "EEE", "CCC", 2008, 6 ),
                            new Field( "EEE", "DDD", 2008, 6 ),
                            new Field( "BBB", "HHH", 2008, 6 ), // new
                            new Field( "HHH", "CCC", 2008, 6 ), // new
                            new Field( "III", "III", 2008, 6 ), // new ループ
                            new Field( "JJJ", "KKK", 2008, 6 ), // new 多重辺
                            new Field( "KKK", "JJJ", 2008, 6 ), // new 多重辺
                };
    
                FindCycle( table );
                Console.ReadKey();
            }
    
            public static void FindCycle( Field[] f )
            {
                // 全ての点を調べる
                for ( int i = 0; i < f.Length; i++ )
                {
                    List<Pair> list = new List<Pair>();
    
                    var pair = new Pair( i, f[ i ] );
                    list.Add( pair );
                    InnerFindCycle( f, list );
                }
            }
    
            // 経路探索ループ
            public static void InnerFindCycle( Field[] f, List<Pair> list )
            {
                Pair last = list.Last();
    
                foreach ( var item in Find( f, last.Field.ChildCode, last.Index ) )
                {
                    // 辺の終点が最初の始点と一致する?
                    if ( item.Field.ChildCode == list[ 0 ].Field.ParentCode )
                    {
                        // サイクルを見つけた
                        int n = list.Count;
                        list.Add( item );
                        FoundCycle( list );
                        list.RemoveAt( n );
                        continue;
                    }
    
                    // 辺の終点がリスト中の最初以外の始点と一致した場合、この道はサイクルに接続しているサイクルではない道
                    if ( list.Skip( 1 ).Where( p => p.Field.ParentCode == item.Field.ChildCode ).Any() )
                        continue;
    
                    // どちらでもない場合、続きの道を調べる
                    int m = list.Count;
                    list.Add( item );
                    InnerFindCycle( f, list ); // 再帰
                    list.RemoveAt( m );
                }
            }
    
            // データベースから次の辺を探す処理
            public static IEnumerable<Pair> Find( Field[] f, string code, int startIndex )
            {
                return f.Skip( startIndex ).Select( ( e, index ) => new Pair( index, e ) )
                    .Where( p => p.Field.ParentCode == code );
            }
    
            // サイクルを見つけたときの処理
            public static void FoundCycle( List<Pair> list )
            {
                foreach ( var item in list )
                {
                    Console.WriteLine( item );
                }
                Console.WriteLine();
            }
        }
    
        // 一時保存用のデータ構造
        public struct Pair
        {
            public int Index;
            public Field Field;
    
            public Pair( int i, Field f )
            {
                this.Index = i;
                this.Field = f;
            }
    
            public override string ToString()
            {
                return string.Format( "{0,2} {1}", this.Index, this.Field );
            }
        }
    
        // データベースの1項目相当
        public class Field
        {
            public string ParentCode { set; get; }
            public string ChildCode { set; get; }
            public int Year { set; get; }
            public int Month { set; get; }
    
            public Field( string p, string c, int y, int m )
            {
                this.ParentCode = p;
                this.ChildCode = c;
                this.Year = y;
                this.Month = m;
            }
    
            public override string ToString()
            {
                return string.Format( "{0} {1}", this.ParentCode, this.ChildCode );
            }
        }
    }
    
    
    
    
    
    
記事No.20074 のレス /過去ログ39より / 関連記事表示
削除チェック/



<< 0 >>

パスワード/

- Child Tree -