再帰メソッドを使用して階層ツリーを接合する

9111 ワード

再帰アルゴリズムこれは非常によく見られるアルゴリズムであり、多くの人が使うものでもあります.それは十分に簡単で、分かりやすいからです.都市を巡って、木などの脳で反応する第一の方法の多くはこれに属する.
再帰は使いやすいですが、使いやすいです.「メモリオーバーフロー」というのは、誰もが再帰で出会うバグだと思います.私はなぜこの方面の知識を書くのか、それは文章の最後に質問があるからです.
まず、前に書いた再帰的な階層ツリーのコードを示します.
private string ParentColumns(List columns, int id)
{
      using (msdb)
      {
           var parents = columns.Where(p => p.parentid == id).ToList();
           StringBuilder sb = new StringBuilder();
           if (parents.Count > 0)
           {
                parents.ForEach(item =>
                {
                        sb.AppendFormat("{0}{1}{2} ", item.cl_id, item.cl_px, item.cl_name);
                        sb.Append(ChildColumns(columns, item.cl_id, 2));
                  });
                  //    
                  parents = null;
           }
           return sb.ToString();
       }
}
private string ChildColumns(List columns, int cl_id, int sub)
        {
            var childs = columns.Where(p => p.parentid == cl_id).ToList();
            StringBuilder sb = new StringBuilder();
            if (childs.Count > 0)
            {
                string substag = "";
                for (int i = 0; i < sub; i++)
                {
                    substag += "";
                }
                childs.ForEach(item =>
                {
                    sb.AppendFormat("{0}{1}{3}{2} ", item.cl_id, item.cl_px, item.cl_name, substag);
                    sb.Append(ChildColumns(columns, item.cl_id, 2 * sub));
                });
                childs = null;
            }
            return sb.ToString();
        }

このコードは私が修復したもので、前にメモリオーバーフローを報告したコードは貼らなかったが、静的変数listやStringBuilderクラスを引用しただけなので、再帰するとメモリが有効に解放されず、最後に黄色いページになった.
では、このバグの本質をお話しします.スレッドは関数を実行しているので、メソッドのパラメータ、変数、戻り値など、一定のメモリ(スタック空間)を割り当てます.この方法はまた絶えず自身を呼び出しているので、深さが大きくなって、そのメモリはまた解放されないで、異常を超えています!だから私はここで静的変数を使って「助纣為虐」に見えます.
では、質問が来ました.趙さんの末尾再帰とContinuationさんの質問の説明によれば、メモリが影響を及ぼさない「末尾再帰」形式に改造することもできます.私はaccStrで前回累積した文字列をパラメータとして記録して渡すだけで、次のような異常なコードがあります.
private string ChildColumnsRecursively(List columns, int cl_id, int sub,string accStr)
        {
            var childs = columns.Where(p => p.parentid == cl_id).Select(p => new { id = p.cl_id, oid = p.cl_px, name = p.cl_name }).ToList();
            StringBuilder sb = new StringBuilder();
            if (childs.Count > 0)
            {
                string substag = "";
                for (int i = 0; i < sub; i++)
                {
                    substag += "";
                }
                childs.ForEach(item =>
                {
                    sb.AppendFormat("{0}{1}{3}{2} ", item.id, item.oid, item.name, substag);
                    accStr += ChildColumnsRecursively(columns, item.id, 2 * sub, sb.ToString());
                });
                childs = null;
                sb.Append(accStr);
            }
            return sb.ToString();
        }

これはどう見ても尾再帰ではなく、「偽再帰」とも言えない.
私の心の中の改造形式は次のような偽コードであるべきです.
public string ChildColumnsRecursively(List columns,int id,string accStr){
      ...Logic Code
      accStr = values;
      return ChildColumnsRecursively(columns,pid,accStr);
}

趙さんのブログを見て、理解したような気がしますが、私はこのケースを使いたいと思っていますが、もう少しで物が足りないような気がします.その方面はまだ理解していません.ここに置いておきなさい,後でゆっくり解決策を考えなさい.