遞歸和嵌套循環的區別


遞歸和嵌套循環的區別

親,不要誤以為自己調用自己就等於遞歸了!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TreeSearch
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            //查詢前折疊所有
            trv.CollapseAll();

            //關鍵字
            string keyStr = txtKey.Text.Trim();

            //遍歷根節點
            foreach (TreeNode tn in trv.Nodes)
            {
                CheckNode(tn, keyStr);
                FindChildNode(tn, keyStr);
            }
        }

        /// <summary>
        /// 遍歷子節點
        /// </summary>
        /// <param name="tnParent">當前節點</param>
        /// <param name="keyStr">關鍵字</param>
        void FindChildNode(TreeNode tnParent, string keyStr)
        {
            foreach (TreeNode tn in tnParent.Nodes)
            {
                CheckNode(tn, keyStr);

                //遞歸檢查當前節點的子節點
                FindChildNode(tn, keyStr);
            }
        }

        /// <summary>
        /// 展示符合條件的節點
        /// </summary>
        /// <param name="tn"></param>
        void DisplayNode(TreeNode tn)
        {
            if (tn.Parent != null)
            {
                tn.Parent.Expand();
                DisplayNode(tn.Parent);
            }
        }

        /// <summary>
        /// 檢測當前節點
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="keyStr"></param>
        void CheckNode(TreeNode tn, string keyStr)
        {
            //包含關鍵字,變紅並展示節點
            if (tn.Text.Contains(keyStr))
            {
                tn.ForeColor = Color.Red;
                DisplayNode(tn);
            }
            else
            {
                //代替初始化的操作方法
                tn.ForeColor = Color.Black;
            }
        }


        //從低往高加
        public int sum23(int param)
        {
            int sum = 0;
            for (int i = 0; i < param; i++)
            {
                sum += 1;
            }
            return sum;
        }

        //從高往第加
        public int sum1(int param)
        {
            int sum = 0;
            for (int i = param; i >= 0; i--)
            {
                sum += i;
            }
            return sum;
        }


        public int sum(int param)
        {
            if (param == 0) { return 0; }
            else
            {
               return  param += sum(param-1);
            }
        }

        public long Fac(int n)
        {
            if (n == 0)
            {
                return 1;
            }
            else
            {   //用for循環來不斷的疊乘
                long value = 1;
                for (int i = n; i > 0; i--)
                {
                    value *= 1;
                }
                return value;
            }

        }


    }
}

 其實,我也不知道第一種情況算不算嚴格意義上滴遞歸滴呀;

下面的方法是另外的一種方式來實現滴呀;

 void FindChildNode2(TreeNode tnParetn, string KeyStr)
        {
            int len = tnParetn.Nodes.Count;
            for (int i = 0; i < len; i++)
            {
               TreeNode tn= tnParetn.Nodes[i];
               if (tn == null)
               {
                   break;   //種方式也是也已滴一  //這個就算是遍歷了一顆節點數滴呀;
                            //這種到底算不算是遞歸呢?
               }
               else
               {
                   CheckNode(tn, KeyStr);
                   FindChildNode2(tn, KeyStr); //就等於這樣一種形式滴呀;
               }
            }
        }

后面,我對遞歸由進一步的研究了,然后發現,我的結論可能是錯的;!!!

我們來看兩種遞歸的執行情況;

沒有返回值,僅僅是又我們的臨界值;

        /// <summary>
        ///  相當於一個進棧和出棧的過程;當達到臨界值之后,就會回到開始點;
        ///  進行出棧;
        /// </summary>
        /// <param name="n"></param>
        public static  void CallSelf(int n)
        {
            if (n > 0)
            {
                Console.WriteLine(n+" in");
                CallSelf(--n); 
                Console.WriteLine(n+" out");
            }

        }

 

 它的執行過程,可以這么理解:

 

 

或者,你可以這么理解它;

 

我擦,流程有夠丑的,不過相當直觀了,如果還不太清楚,你可以自己調試一哈,跟着走一遍,就會有較為深刻的理解了;

 

這里再貼出一個fibo數的實現過程;

 

 

 

 

 然后,我們來看一個,有返回值的遞歸;

        /// <summary>
        /// call themself with return
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public static int CallSelfWithReturn(int n)
        {
            if (n == 0)
            {
                return 0;
            }
            Console.WriteLine(n);
            return CallSelfWithReturn(--n);//又重新回到這個點;

        }

 

對這里個理解,我們就可以使用,可以用第一個圖來進行解釋,因為,這里有一個關鍵點;就是它會保存,當時執行時的一些變量信息;當回到遞歸的開始點時,又你能用到但是的些變量值;(這個是非常重要的點;)

我們再來看他隱身出的一些列的問題:

        //總的來說,它是利用了大頂堆的特性;
        //一個完整的堆定二叉樹,堆定都是一個最大的數據;
        //每次從堆定取出一個數,然后此時堆,新的堆序列又亂了,然后有開始進行重新調整;然后
        //堆棧,它每次回記得調用后的,當時的數據,以便在回到這個點的時候,繼續進行;

       //而同樣的方法,我們可以使用for循環來實現;
       public static void ForEachImple(int n)
        {
            int loopTimes = n;
            for(int i = 0; i <= loopTimes; i++)
            {
                Console.WriteLine(n);

                n = n - 1;
            }

        }

        /// <summary>
        /// 其實,我們的遞歸方式,更像這種方式來執行的;
        /// </summary>
        /// <param name="n"></param>
        public static void ForEachImple2(int n)
        {
            //同樣的方式,我們也可以這樣來實現;
            for(int i = 0; i < 1; i++)
            {
                int loop0 = n; //當前棧的變量值
                Console.WriteLine(loop0);
                n = n - 1;
                for (int j = 0; j < 1; j++)
                {
                    int loop1 = n;//當前棧的變量值
                    Console.WriteLine(loop1);
                    n = n - 1;
                    for (int k = 0; k < 1; k++)
                    {

                        int loop2 = n;//當前棧的變量值
                        Console.WriteLine(loop2);
                        n = n - 1;
                    }

                }

            }
        }

        //所以我們再遍歷,node的時候,常常會寫出這樣的代碼;
        //我想看他是如何如構造一顆樹滴呀;

        public static List<Node> CreateNodes()
        {

            Node node0 = new Node()
            {
                Id = 1,
                PId = 0,
                Name = "Nike",
                Childrends = new List<Node>()
                {
                    new Node() {
                    Id = 2,
                    PId =1,
                    Name = "air jordan1"
                    },
                    new Node() {
                    Id = 3,
                    PId =1,
                    Name = "air jordan2"
                    }

                }
            };

            Node node1 = new Node()
            {
                Id = 4,
                PId = 0,
                Name = "Adidas",
            };


            Node nodeTop = new Node()
            {
                Id = 5,
                PId = -1,  //頂級top 節點;
                Name = "sport brand",
                Childrends = new List<Node>() { node0, node1 }
            };


            List<Node> list = new List<Node>();
            list.Add(nodeTop);


            return list;


        }

        public static void Iteori()
        {





            //然后當我們去遍歷一個可樹的時候,通常我們會這樣寫;

            //先遍歷第一節;
            //然后再遍歷第二節
            //然后遍歷第三節;
            //這樣,我們就可能寫出三個嵌套的for循環;每個循環負責遍歷一個節點;
            List<Node> list = CreateNodes();



            //foreach(var TopNode in list)
            //{

            //    foreach(var SecondeNode in TopNode.Childrends)
            //    {

            //        foreach(var thirdNode in SecondeNode.Childrends)
            //        {

            //            //如果這樣的寫的話,我們是先從葉子節點,開始遍歷的,當葉子節點遍歷完之后,又開始上一級的比那里;
            //            //Console.WriteLine(thirdNode.Name);

            //            //還不能這么寫,如果這么寫的,話 里面的執行次數,將是TopNode.length*SecondeNode.length*thirdNode.length
            //            //所以這樣遍歷是錯誤的;

            //        }

            //    }
            //}


            ///有節點,我們才進入我們的子節點中去進行遍歷,這個相當於我們的左節點遍歷;
            //然后,我們就就有了下面的遍歷方式:遍歷的深度,無法動態的擴展

            foreach (var TopNode in list)
            {
                Console.WriteLine(TopNode.Name);
                if (TopNode.Childrends!=null && TopNode.Childrends.Any())
                {
                    foreach (var SecondeNode in TopNode.Childrends)
                    {
                         Console.WriteLine("  " + SecondeNode.Name);

                        if (SecondeNode.Childrends!=null && SecondeNode.Childrends.Any())
                        {
                            foreach (var thirdNode in SecondeNode.Childrends)
                            {
                                Console.WriteLine("      "+thirdNode.Name);

                            }
                        }

                    }
                }
            }



            //那么還有沒有更好的方式去進行遍歷呢; 
      }

    //就這樣簡單的試煉我們 節點的遍歷;(其實,這個就是我們的前序遍歷的實現)
    public  static void NodeItmeor(List<Node> nodes)
        {
            foreach (var topNode in nodes)
            {
                //第一季,也就是我們的頂級;
                Console.WriteLine(topNode.Name);
                if(topNode.Childrends!=null && topNode.Childrends.Any())
                {
                    NodeItmeor(topNode.Childrends);   //這樣就形成了我們的迭代;遍歷;
                }

            }

        }

 其實,在開發中,我們常常遇到這樣的場景;具有父子節點的集合(類似tree的結構);這個時候,我們需要把它組裝成符合tree nodes(能體現出層級關系的nodes關系圖);

下面我們看具體的實例;

  /// <summary>
        /// 實際的測試方法;
        /// 相當管用滴呀;
        /// </summary>
        public static void Test()
        {
            List<Node> listNodes = new List<Node>()
             {
             new Node() { Id = 1, PId=0,Name="Nike" },
             new Node() { Id = 2, PId = 1, Name = "Air Jordan系列" },
             new Node() { Id = 3, PId = 2, Name = "Air Jordan 1" },
             new Node() { Id = 4, PId = 2, Name = "Air Jordan 2" },
             new Node() { Id = 5, PId = 1, Name = "Air Force系列" },
             new Node() { Id = 6, PId = 5, Name = "Air Force 1" },
             new Node() { Id = 7, PId = 0, Name = "Adidas" }
            };

            var pNodes = listNodes.Where(o=>o.PId==0).ToList();
            var Childrends = listNodes.Where(o=>o.PId!=0).ToList();
            foreach (var topNode in pNodes)
            {
                FindChild(topNode, Childrends);
            }
          
        }

        /// <summary>
        /// /
        /// </summary>
        /// <param name="parentNode"></param>
        /// <param name="nodes"></param>
       public static void  FindChild(Node parentNode, List<Node> nodes)
        {
            //在集合中去尋找自己的子節點;
           var childs=nodes.Where(o=>o.PId==parentNode.Id).ToList();
            if(childs!=null && childs.Any())
            {
                parentNode.Childrends = new List<Node>();
                parentNode.Childrends.AddRange(childs);
                foreach (var p in childs)  //繼續查找,孩子節點的,子節點;
                {
                    FindChild(p, nodes);
                }

            }
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM