遞歸和嵌套循環的區別
親,不要誤以為自己調用自己就等於遞歸了!
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); } } }